PetaVision  Alpha
Image.cpp
1 #include "Image.hpp"
2 #include "Buffer.hpp"
3 #include "utils/PVLog.hpp"
4 
5 // These defines are required by the stb headers
6 #ifndef STB_IMAGE_IMPLEMENTATION
7 #define STB_IMAGE_IMPLEMENTATION
8 #include "io/stb_image.h"
9 #endif
10 #ifndef STB_IMAGE_WRITE_IMPLEMENTATION
11 #define STB_IMAGE_WRITE_IMPLEMENTATION
12 #include "io/stb_image_write.h"
13 #endif
14 
15 #include <cstring>
16 
17 namespace PV {
18 
19 Image::Image(std::string filename) { read(filename); }
20 
21 Image::Image(const std::vector<float> &data, int width, int height, int channels) {
22  set(data, width, height, channels);
23 }
24 
25 void Image::setPixel(int x, int y, float r, float g, float b) {
26  if (getFeatures() > mRPos) {
27  set(x, y, mRPos, r);
28  }
29  if (getFeatures() > mGPos) {
30  set(x, y, mGPos, g);
31  }
32  if (getFeatures() > mBPos) {
33  set(x, y, mBPos, b);
34  }
35 }
36 
37 void Image::setPixel(int x, int y, float r, float g, float b, float a) {
38  setPixel(x, y, r, g, b);
39  set(x, y, mAPos, a);
40 }
41 
42 float Image::getPixelR(int x, int y) {
43  if (getFeatures() <= mRPos) {
44  return 0.0f;
45  }
46  return at(x, y, mRPos);
47 }
48 
49 float Image::getPixelG(int x, int y) {
50  if (getFeatures() <= mGPos) {
51  return 0.0f;
52  }
53  return at(x, y, mGPos);
54 }
55 
56 float Image::getPixelB(int x, int y) {
57  if (getFeatures() <= mBPos) {
58  return 0.0f;
59  }
60  return at(x, y, mBPos);
61 }
62 
63 float Image::getPixelA(int x, int y) {
64  if (getFeatures() <= mAPos) {
65  return 1.0f;
66  }
67  return at(x, y, mAPos);
68 }
69 
70 void Image::convertToGray(bool alphaChannel) {
71  if (getFeatures() < 3) {
72  if ((getFeatures() == 1 && !alphaChannel) || (getFeatures() == 2 && alphaChannel)) {
73  // Do nothing if we are already in the correct format
74  return;
75  }
76  else {
77  // We are already grayscale, but we're adding or removing an alpha channel
78  Buffer<float> grayScale(getWidth(), getHeight(), alphaChannel ? 2 : 1);
79  for (int y = 0; y < getHeight(); ++y) {
80  for (int x = 0; x < getWidth(); ++x) {
81  grayScale.set(x, y, 0, at(x, y, 0));
82  if (alphaChannel) {
83  grayScale.set(x, y, 1, 1.0f);
84  }
85  }
86  }
87  set(grayScale.asVector(), getWidth(), getHeight(), alphaChannel ? 2 : 1);
88  return;
89  }
90  }
91  else {
92  // We're currently RGB or RGBA and need to be Grayscale or Grayscale + Alpha
93  // RGB weights from <https://en.wikipedia.org/wiki/Grayscale>, citing Pratt, Digital Image
94  // Processing
95  const float rgbWeights[3] = {mRToGray, mGToGray, mBToGray}; //{0.30f, 0.59f, 0.11f};
96  Buffer<float> grayScale(getWidth(), getHeight(), alphaChannel ? 2 : 1);
97 
98  for (int y = 0; y < getHeight(); ++y) {
99  for (int x = 0; x < getWidth(); ++x) {
100  float sum = 0.0f;
101  for (int f = 0; f < 3; ++f) {
102  sum += at(x, y, f) * rgbWeights[f];
103  }
104  grayScale.set(x, y, 0, sum);
105  if (alphaChannel) {
106  if (getFeatures() > 3) {
107  grayScale.set(x, y, 1, at(x, y, 3));
108  }
109  else {
110  grayScale.set(x, y, 1, 1.0f);
111  }
112  }
113  }
114  }
115  set(grayScale.asVector(), getWidth(), getHeight(), alphaChannel ? 2 : 1);
116  }
117 }
118 
119 void Image::convertToColor(bool alphaChannel) {
120  // Are we already color? If so, do we need to add or remove an alpha channel?
121  if (getFeatures() > 2) {
122  if ((getFeatures() == 3 && !alphaChannel) || (getFeatures() == 4 && alphaChannel)) {
123  // This is the correct format already, nothing to be done
124  return;
125  }
126  else {
127  // We're already color, but we're adding or removing an alpha channel
128  Buffer<float> color(getWidth(), getHeight(), alphaChannel ? 4 : 3);
129  for (int y = 0; y < getHeight(); ++y) {
130  for (int x = 0; x < getWidth(); ++x) {
131  color.set(x, y, mRPos, at(x, y, mRPos));
132  color.set(x, y, mGPos, at(x, y, mGPos));
133  color.set(x, y, mBPos, at(x, y, mBPos));
134  if (alphaChannel) {
135  color.set(x, y, mAPos, 1.0f);
136  }
137  }
138  }
139  set(color.asVector(), getWidth(), getHeight(), alphaChannel ? 4 : 3);
140  }
141  }
142  else {
143  // We're converting a grayscale image to color
144  Buffer<float> color(getWidth(), getHeight(), alphaChannel ? 4 : 3);
145  for (int y = 0; y < getHeight(); ++y) {
146  for (int x = 0; x < getWidth(); ++x) {
147  float val = at(x, y, 0);
148  color.set(x, y, mRPos, val);
149  color.set(x, y, mGPos, val);
150  color.set(x, y, mBPos, val);
151  if (alphaChannel) {
152  if (getFeatures() == 2) {
153  color.set(x, y, mAPos, at(x, y, 1));
154  }
155  else {
156  color.set(x, y, mAPos, 1.0f);
157  }
158  }
159  }
160  }
161  set(color.asVector(), getWidth(), getHeight(), alphaChannel ? 4 : 3);
162  }
163 }
164 
165 void Image::read(std::string filename) {
166  int width = 0, height = 0, channels = 0;
167  uint8_t *data = stbi_load(filename.c_str(), &width, &height, &channels, 0);
168  if (data == nullptr) {
169  if (!std::strcmp(stbi_failure_reason(), "unknown image type")) {
170  Fatal().printf(
171  " File \"%s\" is an unknown image type.\n"
172  " (A list of image files must have a .txt extension;"
173  " an individual image file must be readable by the stb_image library.)\n",
174  filename.c_str());
175  }
176  else {
177  Fatal().printf("Unable to load \"%s\": %s.\n", filename.c_str(), stbi_failure_reason());
178  }
179  }
180  FatalIf(data == nullptr, " File not found: %s\n", filename.c_str());
181  resize(width, height, channels);
182 
183  for (int y = 0; y < height; ++y) {
184  for (int x = 0; x < width; ++x) {
185  for (int f = 0; f < channels; ++f) {
186  float value = static_cast<float>(data[(y * width + x) * channels + f]) / 255.0f;
187  set(x, y, f, value);
188  }
189  }
190  }
191 
192  stbi_image_free(data);
193 }
194 
195 void Image::write(std::string filename) {
196  std::vector<uint8_t> byteData(getWidth() * getHeight() * getFeatures());
197  int byteIndex = 0;
198  float imageMin = 0.0f;
199  float imageMax = 1.0f;
200 
201  for (int y = 0; y < getHeight(); ++y) {
202  for (int x = 0; x < getWidth(); ++x) {
203  for (int f = 0; f < getFeatures(); ++f) {
204  imageMin = at(x, y, f) < imageMin ? at(x, y, f) : imageMin;
205  imageMax = at(x, y, f) > imageMax ? at(x, y, f) : imageMax;
206  }
207  }
208  }
209 
210  for (int y = 0; y < getHeight(); ++y) {
211  for (int x = 0; x < getWidth(); ++x) {
212  for (int f = 0; f < getFeatures(); ++f) {
213  float normVal = (at(x, y, f) - imageMin) / (imageMax - imageMin);
214  byteData.at(byteIndex++) = static_cast<uint8_t>(normVal * 255.0f);
215  }
216  }
217  }
218 
219  stbi_write_png(
220  filename.c_str(),
221  getWidth(),
222  getHeight(),
223  getFeatures(),
224  byteData.data(),
225  getWidth() * getFeatures());
226 }
227 }