tiny_dnn  1.0.0
A header only, dependency-free deep learning framework in C++11
image.h
1 /*
2  Copyright (c) 2013, Taiga Nomi
3  All rights reserved.
4 
5  Redistribution and use in source and binary forms, with or without
6  modification, are permitted provided that the following conditions are met:
7  * Redistributions of source code must retain the above copyright
8  notice, this list of conditions and the following disclaimer.
9  * Redistributions in binary form must reproduce the above copyright
10  notice, this list of conditions and the following disclaimer in the
11  documentation and/or other materials provided with the distribution.
12  * Neither the name of the <organization> nor the
13  names of its contributors may be used to endorse or promote products
14  derived from this software without specific prior written permission.
15 
16  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
17  EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18  WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19  DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY
20  DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21  (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22  LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23  ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27 #pragma once
28 #include <vector>
29 #include <fstream>
30 #include <cstdint>
31 #include <algorithm>
32 #include <array>
33 #include "tiny_dnn/util/util.h"
34 
35 #ifdef _MSC_VER
36 #pragma warning(push)
37 #pragma warning(disable:4996) // suppress warnings about using fopen
38 #endif
39 
40 #define STB_IMAGE_IMPLEMENTATION
41 #define STB_IMAGE_INLINE // We need this define to avoid multiple definition
42 #include "third_party/stb/stb_image.h"
43 
44 #define STB_IMAGE_RESIZE_IMPLEMENTATION
45 #define STB_IMAGE_RESIZE_INLINE
46 #include "third_party/stb/stb_image_resize.h"
47 
48 #define STB_IMAGE_WRITE_IMPLEMENTATION
49 #define STB_IMAGE_WRITE_INLINE
50 #include "third_party/stb/stb_image_write.h"
51 
52 
53 namespace tiny_dnn {
54 
55 namespace detail {
56 
57 template <typename T>
58 typename std::enable_if<std::is_unsigned<T>::value, T>::type saturated_sub(T s1, T s2) {
59  return s1 > s2 ? static_cast<T>(s1 - s2) : 0;
60 }
61 
62 template <typename T>
63 typename std::enable_if<!std::is_unsigned<T>::value, T>::type saturated_sub(T s1, T s2) {
64  return static_cast<T>(s1 - s2);
65 }
66 
67 inline bool ends_with(std::string const & value, std::string const & ending) {
68  if (ending.size() > value.size()) return false;
69  return std::equal(ending.rbegin(), ending.rend(), value.rbegin());
70 }
71 
72 inline void resize_image_core(const uint8_t* src, int srcw, int srch, uint8_t* dst, int dstw, int dsth, int channels)
73 {
74  stbir_resize_uint8(src, srcw, srch, 0, dst, dstw, dsth, 0, channels);
75 }
76 
77 inline void resize_image_core(const float* src, int srcw, int srch, float* dst, int dstw, int dsth, int channels)
78 {
79  stbir_resize_float(src, srcw, srch, 0, dst, dstw, dsth, 0, channels);
80 }
81 
82 } // namespace detail
83 
84 enum class image_type {
85  grayscale,
86  rgb,
87  bgr
88 };
89 
93 template<typename T = unsigned char>
94 class image {
95 public:
96  typedef T intensity_t;
97  typedef typename std::vector<intensity_t>::iterator iterator;
98  typedef typename std::vector<intensity_t>::const_iterator const_iterator;
99 
100  image() : width_(0), height_(0), depth_(1) {}
101 
105  image(const T* data, size_t width, size_t height, image_type type)
106  : width_(width), height_(height), depth_(type == image_type::grayscale ? 1: 3), type_(type), data_(depth_ * width_ * height_, 0)
107  {
108  std::copy(data, data + width * height * depth_, &data_[0]);
109  }
110 
114  image(const shape3d& size, image_type type)
115  : width_(size.width_), height_(size.height_), depth_(size.depth_),
116  type_(type),
117  data_(depth_ * width_ * height_, 0){
118  if (type == image_type::grayscale && size.depth_ != 1) {
119  throw nn_error("depth must be 1 in grayscale");
120  }
121  else if (type != image_type::grayscale && size.depth_ != 3) {
122  throw nn_error("depth must be 3 in rgb/bgr");
123  }
124  }
125 
126  template <typename U>
127  image(const image<U>& rhs) : width_(rhs.width()), height_(rhs.height()), depth_(rhs.depth()), type_(rhs.type()), data_(rhs.shape().size()) {
128  std::transform(rhs.begin(), rhs.end(), data_.begin(), [](T src) { return static_cast<intensity_t>(src); });
129  }
130 
136  image(const std::string& filename, image_type type)
137  {
138  int w, h, d;
139  stbi_uc* input_pixels = stbi_load(filename.c_str(), &w, &h, &d, type == image_type::grayscale ? 1 : 3);
140  if (input_pixels == nullptr) {
141  throw nn_error("failed to open image:" + std::string(stbi_failure_reason()));
142  }
143 
144  width_ = static_cast<size_t>(w);
145  height_ = static_cast<size_t>(h);
146  depth_ = type == image_type::grayscale ? 1 : 3;
147  type_ = type;
148 
149  data_.resize(width_*height_*depth_);
150 
151  // reorder to HxWxD -> DxHxW
152  from_rgb(input_pixels, input_pixels + data_.size());
153 
154  stbi_image_free(input_pixels);
155  }
156 
157  void save(const std::string& path) const {
158  int ret;
159  std::vector<uint8_t> buf = to_rgb<uint8_t>();
160 
161  if (detail::ends_with(path, "png")) {
162  ret = stbi_write_png(path.c_str(),
163  static_cast<int>(width_),
164  static_cast<int>(height_),
165  static_cast<int>(depth_),
166  (const void*)&buf[0], 0);
167  }
168  else {
169  ret = stbi_write_bmp(path.c_str(),
170  static_cast<int>(width_),
171  static_cast<int>(height_),
172  static_cast<int>(depth_),
173  (const void*)&buf[0]);
174  }
175  if (ret == 0) {
176  throw nn_error("failed to save image:" + path);
177  }
178  }
179 
180  void write(const std::string& path) const {
181  save(path);
182  }
183 
184  void resize(size_t width, size_t height)
185  {
186  data_.resize(width * height * depth_);
187  width_ = width;
188  height_ = height;
189  //depth_ = depth;
190  }
191 
192  void fill(intensity_t value) {
193  std::fill(data_.begin(), data_.end(), value);
194  }
195 
196  intensity_t& at(size_t x, size_t y, size_t z = 0) {
197  assert(x < width_);
198  assert(y < height_);
199  assert(z < depth_);
200  return data_[z * width_ * height_ + y * width_ + x];
201  }
202 
203  const intensity_t& at(size_t x, size_t y, size_t z = 0) const {
204  assert(x < width_);
205  assert(y < height_);
206  assert(z < depth_);
207  return data_[z * width_ * height_ + y * width_ + x];
208  }
209 
210  bool empty() const { return data_.empty(); }
211  iterator begin() { return data_.begin(); }
212  iterator end() { return data_.end(); }
213  const_iterator begin() const { return data_.begin(); }
214  const_iterator end() const { return data_.end(); }
215 
216  intensity_t& operator[](std::size_t idx) { return data_[idx]; };
217  const intensity_t& operator[](std::size_t idx) const { return data_[idx]; };
218 
219  size_t width() const { return width_; }
220  size_t height() const { return height_; }
221  size_t depth() const {return depth_;}
222  image_type type() const { return type_; }
223  shape3d shape() const {
224  return shape3d(static_cast<serial_size_t>(width_),
225  static_cast<serial_size_t>(height_),
226  static_cast<serial_size_t>(depth_));
227  }
228  const std::vector<intensity_t>& data() const { return data_; }
229  vec_t to_vec() const { return vec_t(begin(), end()); }
230 
231  template <typename U>
232  std::vector<U> to_rgb() const {
233  if (depth_ == 1) {
234  return std::vector<U>(data_.begin(), data_.end());
235  }
236  else {
237  std::vector<U> buf(shape().size());
238  auto order = depth_order(type_);
239  auto dst = buf.begin();
240 
241  for (size_t y = 0; y < height_; y++)
242  for (size_t x = 0; x < width_; x++)
243  for (size_t i = 0; i < depth_; i++)
244  *dst++ = static_cast<U>(at(x, y, order[i]));
245  return buf;
246  }
247  }
248 
249  template <typename Iter>
250  void from_rgb(Iter begin, Iter end) {
251  if (depth_ == 1) {
252  std::copy(begin, end, data_.begin());
253  }
254  else {
255  auto order = depth_order(type_);
256  assert(static_cast<serial_size_t>(
257  std::distance(begin, end)) == data_.size());
258 
259  for (size_t y = 0; y < height_; y++)
260  for (size_t x = 0; x < width_; x++)
261  for (size_t i = 0; i < depth_; i++)
262  at(x, y, order[i]) = static_cast<intensity_t>(*begin++);
263  }
264  }
265 
266 private:
267  std::array<size_t, 3> depth_order(image_type img) const {
268  if (img == image_type::rgb) {
269  return{ {0,1,2} };
270  }
271  else {
272  assert(img == image_type::bgr);
273  return{ {2,1,0 } };
274  }
275  }
276  size_t width_;
277  size_t height_;
278  size_t depth_;
279  image_type type_;
280  std::vector<intensity_t> data_;
281 };
282 
283 template <typename T>
284 image<float_t> mean_image(const image<T>& src)
285 {
286  image<float_t> mean(shape3d(1, 1, (serial_size_t)src.depth()), src.type());
287 
288  for (size_t i = 0; i < src.depth(); i++) {
289  float_t sum = 0.0f;
290  for (size_t y = 0; y < src.height(); y++) {
291  for (size_t x = 0; x < src.width(); x++) {
292  sum += src.at(x, y, i);
293  }
294  }
295  mean.at(0, 0, i) = sum / (src.width() * src.height());
296  }
297 
298  return mean;
299 }
300 
306 template <typename T>
307 inline image<T> resize_image(const image<T>& src, int width, int height)
308 {
309  image<T> resized(shape3d(static_cast<serial_size_t>(width),
310  static_cast<serial_size_t>(height),
311  static_cast<serial_size_t>(src.depth())),
312  src.type());
313  std::vector<T> src_rgb = src.template to_rgb<T>();
314  std::vector<T> dst_rgb(resized.shape().size());
315 
316  detail::resize_image_core(&src_rgb[0],
317  static_cast<int>(src.width()),
318  static_cast<int>(src.height()),
319  &dst_rgb[0],
320  width,
321  height,
322  static_cast<int>(src.depth()));
323 
324  resized.from_rgb(dst_rgb.begin(), dst_rgb.end());
325 
326  return resized;
327 }
328 
329 
330 // dst[x,y,d] = lhs[x,y,d] - rhs[x,y,d]
331 template <typename T>
332 image<T> subtract_image(const image<T>& lhs, const image<T>& rhs)
333 {
334  if (lhs.shape() != rhs.shape()) {
335  throw nn_error("Shapes of lhs/rhs must be same. lhs:" + to_string(lhs.shape()) + ",rhs:" + to_string(rhs.shape()));
336  }
337 
338  image<T> dst(lhs.shape(), lhs.type());
339 
340  auto dstit = dst.begin();
341  auto lhsit = lhs.begin();
342  auto rhsit = rhs.begin();
343 
344  for (; dstit != dst.end(); ++dstit, ++lhsit, ++rhsit) {
345  *dstit = detail::saturated_sub(*lhsit, *rhsit);
346  }
347  return dst;
348 }
349 
350 template <typename T>
351 image<T> subtract_scalar(const image<T>& lhs, const image<T>& rhs)
352 {
353  if (lhs.depth() != rhs.depth()) {
354  throw nn_error("Depth of lhs/rhs must be same. lhs:" + to_string(lhs.depth()) + ",rhs:" + to_string(rhs.depth()));
355  }
356  if (rhs.width() != 1 || rhs.height() != 1) {
357  throw nn_error("rhs must be 1x1xN");
358  }
359 
360  image<T> dst(lhs.shape(), lhs.type());
361 
362  auto dstit = dst.begin();
363  auto lhsit = lhs.begin();
364  auto rhsit = rhs.begin();
365 
366  for (size_t i = 0; i < lhs.depth(); i++, ++rhsit) {
367  for (size_t j = 0; j < lhs.width() * lhs.height(); j++, ++dstit, ++lhsit) {
368  *dstit = detail::saturated_sub(*lhsit, *rhsit);
369  }
370  }
371 
372  return dst;
373 }
374 
388 template<typename T>
389 inline image<T> vec2image(const vec_t& vec, serial_size_t block_size = 2, serial_size_t max_cols = 20)
390 {
391  if (vec.empty())
392  throw nn_error("failed to visialize image: vector is empty");
393 
394  image<T> img;
395  const serial_size_t border_width = 1;
396  const auto cols = vec.size() >= (serial_size_t)max_cols ? (serial_size_t)max_cols : vec.size();
397  const auto rows = (vec.size() - 1) / cols + 1;
398  const auto pitch = block_size + border_width;
399  const auto width = pitch * cols + border_width;
400  const auto height = pitch * rows + border_width;
401  const typename image<T>::intensity_t bg_color = 255;
402  serial_size_t current_idx = 0;
403 
404  img.resize(width, height);
405  img.fill(bg_color);
406 
407  auto minmax = std::minmax_element(vec.begin(), vec.end());
408 
409  for (unsigned int r = 0; r < rows; r++) {
410  serial_size_t topy = pitch * r + border_width;
411 
412  for (unsigned int c = 0; c < cols; c++, current_idx++) {
413  serial_size_t leftx = pitch * c + border_width;
414  const float_t src = vec[current_idx];
415  image<>::intensity_t dst
416  = static_cast<typename image<T>::intensity_t>(rescale(src, *minmax.first, *minmax.second, 0, 255));
417 
418  for (serial_size_t y = 0; y < block_size; y++)
419  for (serial_size_t x = 0; x < block_size; x++)
420  img.at(x + leftx, y + topy) = dst;
421 
422  if (current_idx == vec.size()) return img;
423  }
424  }
425  return img;
426 }
427 
442 template<typename T>
443 inline image<T> vec2image(const vec_t& vec, const index3d<serial_size_t>& maps) {
444  if (vec.empty())
445  throw nn_error("failed to visualize image: vector is empty");
446  if (vec.size() != maps.size())
447  throw nn_error("failed to visualize image: vector size invalid");
448 
449  const serial_size_t border_width = 1;
450  const auto pitch = maps.width_ + border_width;
451  const auto width = maps.depth_ * pitch + border_width;
452  const auto height = maps.height_ + 2 * border_width;
453  const typename image<T>::intensity_t bg_color = 255;
454  image<T> img;
455 
456  img.resize(width, height);
457  img.fill(bg_color);
458 
459  auto minmax = std::minmax_element(vec.begin(), vec.end());
460 
461  for (serial_size_t c = 0; c < maps.depth_; ++c) {
462  const auto top = border_width;
463  const auto left = c * pitch + border_width;
464 
465  for (serial_size_t y = 0; y < maps.height_; ++y) {
466  for (serial_size_t x = 0; x < maps.width_; ++x) {
467  const float_t val = vec[maps.get_index(x, y, c)];
468 
469  img.at(left + x, top + y)
470  = static_cast<typename image<T>::intensity_t>(rescale(val, *minmax.first, *minmax.second, 0, 255));
471  }
472  }
473  }
474  return img;
475 }
476 
477 } // namespace tiny_dnn
478 
479 #ifdef _MSC_VER
480 #pragma warning(pop)
481 #endif
Simple image utility class.
Definition: image.h:94
image(const T *data, size_t width, size_t height, image_type type)
create image from raw pointer
Definition: image.h:105
image(const std::string &filename, image_type type)
create image from file supported file format: JPEG/PNG/TGA/BMP/PSD/GIF/HDR/PIC/PNM (see detail at the...
Definition: image.h:136
image(const shape3d &size, image_type type)
create WxHxD image filled with 0
Definition: image.h:114
error exception class for tiny-dnn
Definition: nn_error.h:37