tiny_dnn 1.0.0
A header only, dependency-free deep learning framework in C++11
Loading...
Searching...
No Matches
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
53namespace tiny_dnn {
54
55namespace detail {
56
57template <typename T>
58typename 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
62template <typename T>
63typename 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
67inline 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
72inline 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
77inline 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
84enum class image_type {
85 grayscale,
86 rgb,
87 bgr
88};
89
93template<typename T = unsigned char>
94class image {
95public:
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
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
266private:
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
283template <typename T>
284image<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
306template <typename T>
307inline 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]
331template <typename T>
332image<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
350template <typename T>
351image<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
388template<typename T>
389inline 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
442template<typename T>
443inline 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