Compute Library
 21.05
ImageLoader.h
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2018-2021 Arm Limited.
3  *
4  * SPDX-License-Identifier: MIT
5  *
6  * Permission is hereby granted, free of charge, to any person obtaining a copy
7  * of this software and associated documentation files (the "Software"), to
8  * deal in the Software without restriction, including without limitation the
9  * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
10  * sell copies of the Software, and to permit persons to whom the Software is
11  * furnished to do so, subject to the following conditions:
12  *
13  * The above copyright notice and this permission notice shall be included in all
14  * copies or substantial portions of the Software.
15  *
16  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22  * SOFTWARE.
23  */
24 #ifndef __UTILS_IMAGE_LOADER_H__
25 #define __UTILS_IMAGE_LOADER_H__
26 
27 #include "arm_compute/core/Error.h"
30 #include "arm_compute/core/Types.h"
31 
32 #include "utils/Utils.h"
33 
34 #pragma GCC diagnostic push
35 #pragma GCC diagnostic ignored "-Wswitch-default"
36 #pragma GCC diagnostic ignored "-Wstrict-overflow"
37 #include "stb/stb_image.h"
38 #pragma GCC diagnostic pop
39 
40 #include <cstdlib>
41 #include <memory>
42 #include <string>
43 
44 namespace arm_compute
45 {
46 namespace utils
47 {
48 /** Image feeder interface */
50 {
51 public:
52  /** Virtual base destructor */
53  virtual ~IImageDataFeeder() = default;
54  /** Gets a character from an image feed */
55  virtual uint8_t get() = 0;
56  /** Feed a whole row to a destination pointer
57  *
58  * @param[out] dst Destination pointer
59  * @param[in] row_size Row size in terms of bytes
60  */
61  virtual void get_row(uint8_t *dst, size_t row_size) = 0;
62 };
63 /** File Image feeder concrete implementation */
65 {
66 public:
67  /** Default constructor
68  *
69  * @param[in] fs Image file stream
70  */
71  FileImageFeeder(std::ifstream &fs)
72  : _fs(fs)
73  {
74  }
75  // Inherited overridden methods
76  uint8_t get() override
77  {
78  return _fs.get();
79  }
80  void get_row(uint8_t *dst, size_t row_size) override
81  {
82  ARM_COMPUTE_ERROR_ON(dst == nullptr);
83  _fs.read(reinterpret_cast<std::fstream::char_type *>(dst), row_size);
84  }
85 
86 private:
87  std::ifstream &_fs;
88 };
89 /** Memory Image feeder concrete implementation */
91 {
92 public:
93  /** Default constructor
94  *
95  * @param[in] data Pointer to data
96  */
97  MemoryImageFeeder(const uint8_t *data)
98  : _data(data)
99  {
100  }
101  /** Prevent instances of this class from being copied (As this class contains pointers) */
102  MemoryImageFeeder(const MemoryImageFeeder &) = delete;
103  /** Default move constructor */
104  MemoryImageFeeder(MemoryImageFeeder &&) = default;
105  /** Prevent instances of this class from being copied (As this class contains pointers) */
106  MemoryImageFeeder &operator=(const MemoryImageFeeder &) = delete;
107  /** Default move assignment operator */
109  // Inherited overridden methods
110  uint8_t get() override
111  {
112  return *_data++;
113  }
114  void get_row(uint8_t *dst, size_t row_size) override
115  {
116  ARM_COMPUTE_ERROR_ON(dst == nullptr);
117  memcpy(dst, _data, row_size);
118  _data += row_size;
119  }
120 
121 private:
122  const uint8_t *_data;
123 };
124 
125 /** Image loader interface */
127 {
128 public:
129  /** Default Constructor */
131  : _feeder(nullptr), _width(0), _height(0)
132  {
133  }
134  /** Virtual base destructor */
135  virtual ~IImageLoader() = default;
136  /** Return the width of the currently open image file. */
137  unsigned int width() const
138  {
139  return _width;
140  }
141  /** Return the height of the currently open image file. */
142  unsigned int height() const
143  {
144  return _height;
145  }
146  /** Return true if the image file is currently open */
147  virtual bool is_open() = 0;
148  /** Open an image file and reads its metadata (Width, height)
149  *
150  * @param[in] filename File to open
151  */
152  virtual void open(const std::string &filename) = 0;
153  /** Closes an image file */
154  virtual void close() = 0;
155  /** Initialise an image's metadata with the dimensions of the image file currently open
156  *
157  * @param[out] image Image to initialise
158  * @param[in] format Format to use for the image (Must be RGB888 or U8)
159  */
160  template <typename T>
161  void init_image(T &image, Format format)
162  {
164  ARM_COMPUTE_ERROR_ON(format != Format::RGB888 && format != Format::U8);
165 
166  // Use the size of the input image
167  TensorInfo image_info(_width, _height, format);
168  image.allocator()->init(image_info);
169  }
170  /** Fill an image with the content of the currently open image file.
171  *
172  * @note If the image is a CLImage, the function maps and unmaps the image
173  *
174  * @param[in,out] image Image to fill (Must be allocated, and of matching dimensions with the opened image file).
175  */
176  template <typename T>
177  void fill_image(T &image)
178  {
180  ARM_COMPUTE_ERROR_ON(image.info()->dimension(0) != _width || image.info()->dimension(1) != _height);
182  ARM_COMPUTE_ERROR_ON(_feeder.get() == nullptr);
183  try
184  {
185  // Map buffer if creating a CLTensor
186  map(image, true);
187 
188  // Validate feeding data
189  validate_info(image.info());
190 
191  switch(image.info()->format())
192  {
193  case Format::U8:
194  {
195  // We need to convert the data from RGB to grayscale:
196  // Iterate through every pixel of the image
197  Window window;
198  window.set(Window::DimX, Window::Dimension(0, _width, 1));
199  window.set(Window::DimY, Window::Dimension(0, _height, 1));
200 
201  Iterator out(&image, window);
202 
203  unsigned char red = 0;
204  unsigned char green = 0;
205  unsigned char blue = 0;
206 
207  execute_window_loop(window, [&](const Coordinates &)
208  {
209  red = _feeder->get();
210  green = _feeder->get();
211  blue = _feeder->get();
212 
213  *out.ptr() = 0.2126f * red + 0.7152f * green + 0.0722f * blue;
214  },
215  out);
216 
217  break;
218  }
219  case Format::RGB888:
220  {
221  // There is no format conversion needed: we can simply copy the content of the input file to the image one row at the time.
222  // Create a vertical window to iterate through the image's rows:
223  Window window;
224  window.set(Window::DimY, Window::Dimension(0, _height, 1));
225 
226  Iterator out(&image, window);
227  size_t row_size = _width * image.info()->element_size();
228 
229  execute_window_loop(window, [&](const Coordinates &)
230  {
231  _feeder->get_row(out.ptr(), row_size);
232  },
233  out);
234 
235  break;
236  }
237  default:
238  ARM_COMPUTE_ERROR("Unsupported format");
239  }
240 
241  // Unmap buffer if creating a CLTensor
242  unmap(image);
243  }
244  catch(const std::ifstream::failure &e)
245  {
246  ARM_COMPUTE_ERROR_VAR("Loading image file: %s", e.what());
247  }
248  }
249  /** Fill a tensor with 3 planes (one for each channel) with the content of the currently open image file.
250  *
251  * @note If the image is a CLImage, the function maps and unmaps the image
252  *
253  * @param[in,out] tensor Tensor with 3 planes to fill (Must be allocated, and of matching dimensions with the opened image). Data types supported: U8/F16/F32
254  * @param[in] bgr (Optional) Fill the first plane with blue channel (default = false)
255  */
256  template <typename T>
257  void fill_planar_tensor(T &tensor, bool bgr = false)
258  {
261 
262  const DataLayout data_layout = tensor.info()->data_layout();
263  const TensorShape tensor_shape = tensor.info()->tensor_shape();
264 
265  ARM_COMPUTE_UNUSED(tensor_shape);
269 
270  ARM_COMPUTE_ERROR_ON(_feeder.get() == nullptr);
271 
272  try
273  {
274  // Map buffer if creating a CLTensor
275  map(tensor, true);
276 
277  // Validate feeding data
278  validate_info(tensor.info());
279 
280  // Stride across channels
281  size_t stride_z = 0;
282 
283  // Iterate through every pixel of the image
284  Window window;
286  {
287  window.set(Window::DimX, Window::Dimension(0, _width, 1));
288  window.set(Window::DimY, Window::Dimension(0, _height, 1));
289  window.set(Window::DimZ, Window::Dimension(0, 1, 1));
290  stride_z = tensor.info()->strides_in_bytes()[2];
291  }
292  else
293  {
294  window.set(Window::DimX, Window::Dimension(0, 1, 1));
295  window.set(Window::DimY, Window::Dimension(0, _width, 1));
296  window.set(Window::DimZ, Window::Dimension(0, _height, 1));
297  stride_z = tensor.info()->strides_in_bytes()[0];
298  }
299 
300  Iterator out(&tensor, window);
301 
302  unsigned char red = 0;
303  unsigned char green = 0;
304  unsigned char blue = 0;
305 
306  execute_window_loop(window, [&](const Coordinates &)
307  {
308  red = _feeder->get();
309  green = _feeder->get();
310  blue = _feeder->get();
311 
312  switch(tensor.info()->data_type())
313  {
314  case DataType::U8:
315  case DataType::QASYMM8:
316  {
317  *(out.ptr() + 0 * stride_z) = bgr ? blue : red;
318  *(out.ptr() + 1 * stride_z) = green;
319  *(out.ptr() + 2 * stride_z) = bgr ? red : blue;
320  break;
321  }
322  case DataType::F32:
323  {
324  *reinterpret_cast<float *>(out.ptr() + 0 * stride_z) = static_cast<float>(bgr ? blue : red);
325  *reinterpret_cast<float *>(out.ptr() + 1 * stride_z) = static_cast<float>(green);
326  *reinterpret_cast<float *>(out.ptr() + 2 * stride_z) = static_cast<float>(bgr ? red : blue);
327  break;
328  }
329  case DataType::F16:
330  {
331  *reinterpret_cast<half *>(out.ptr() + 0 * stride_z) = static_cast<half>(bgr ? blue : red);
332  *reinterpret_cast<half *>(out.ptr() + 1 * stride_z) = static_cast<half>(green);
333  *reinterpret_cast<half *>(out.ptr() + 2 * stride_z) = static_cast<half>(bgr ? red : blue);
334  break;
335  }
336  default:
337  {
338  ARM_COMPUTE_ERROR("Unsupported data type");
339  }
340  }
341  },
342  out);
343 
344  // Unmap buffer if creating a CLTensor
345  unmap(tensor);
346  }
347  catch(const std::ifstream::failure &e)
348  {
349  ARM_COMPUTE_ERROR_VAR("Loading image file: %s", e.what());
350  }
351  }
352 
353 protected:
354  /** Validate metadata */
355  virtual void validate_info(const ITensorInfo *tensor_info)
356  {
357  ARM_COMPUTE_UNUSED(tensor_info);
358  }
359 
360 protected:
361  std::unique_ptr<IImageDataFeeder> _feeder;
362  unsigned int _width;
363  unsigned int _height;
364 };
365 
366 /** PPM Image loader concrete implementation */
367 class PPMLoader : public IImageLoader
368 {
369 public:
370  /** Default Constructor */
372  : IImageLoader(), _fs()
373  {
374  }
375 
376  // Inherited methods overridden:
377  bool is_open() override
378  {
379  return _fs.is_open();
380  }
381  void open(const std::string &filename) override
382  {
384  try
385  {
386  _fs.exceptions(std::ifstream::failbit | std::ifstream::badbit);
387  _fs.open(filename, std::ios::in | std::ios::binary);
388 
389  unsigned int max_val = 0;
390  std::tie(_width, _height, max_val) = parse_ppm_header(_fs);
391 
392  ARM_COMPUTE_ERROR_ON_MSG_VAR(max_val >= 256, "2 bytes per colour channel not supported in file %s",
393  filename.c_str());
394 
395  _feeder = std::make_unique<FileImageFeeder>(_fs);
396  }
397  catch(std::runtime_error &e)
398  {
399  ARM_COMPUTE_ERROR_VAR("Accessing %s: %s", filename.c_str(), e.what());
400  }
401  }
402  void close() override
403  {
404  if(is_open())
405  {
406  _fs.close();
407  _feeder = nullptr;
408  }
410  }
411 
412 protected:
413  // Inherited methods overridden:
414  void validate_info(const ITensorInfo *tensor_info) override
415  {
416  // Check if the file is large enough to fill the image
417  const size_t current_position = _fs.tellg();
418  _fs.seekg(0, std::ios_base::end);
419  const size_t end_position = _fs.tellg();
420  _fs.seekg(current_position, std::ios_base::beg);
421 
422  ARM_COMPUTE_ERROR_ON_MSG((end_position - current_position) < tensor_info->tensor_shape().total_size(),
423  "Not enough data in file");
424  ARM_COMPUTE_UNUSED(end_position, tensor_info);
425  }
426 
427 private:
428  std::ifstream _fs;
429 };
430 
431 /** Class to load the content of a JPEG file into an Image */
432 class JPEGLoader : public IImageLoader
433 {
434 private:
435  /** Custom malloc deleter struct */
436  struct malloc_deleter
437  {
438  void operator()(uint8_t *p) const
439  {
440  free(p);
441  }
442  };
443 
444 public:
445  /** Default Constructor */
447  : IImageLoader(), _is_loaded(false), _data(nullptr)
448  {
449  }
450 
451  // Inherited methods overridden:
452  bool is_open() override
453  {
454  return _is_loaded;
455  }
456  void open(const std::string &filename) override
457  {
458  int bpp, width, height;
459  uint8_t *rgb_image = stbi_load(filename.c_str(), &width, &height, &bpp, 3);
460  if(rgb_image == NULL)
461  {
462  ARM_COMPUTE_ERROR_VAR("Accessing %s failed", filename.c_str());
463  }
464  else
465  {
466  _width = width;
467  _height = height;
468  _data = std::unique_ptr<uint8_t, malloc_deleter>(rgb_image);
469  _is_loaded = true;
470  _feeder = std::make_unique<MemoryImageFeeder>(_data.get());
471  }
472  }
473  void close() override
474  {
475  if(is_open())
476  {
477  _width = 0;
478  _height = 0;
479  release();
480  }
482  }
483  /** Explicitly Releases the memory of the loaded data */
484  void release()
485  {
486  if(_is_loaded)
487  {
488  _data.reset();
489  _is_loaded = false;
490  _feeder = nullptr;
491  }
492  }
493 
494 private:
495  bool _is_loaded;
496  std::unique_ptr<uint8_t, malloc_deleter> _data;
497 };
498 
499 /** Factory for generating appropriate image loader**/
501 {
502 public:
503  /** Create an image loader depending on the image type
504  *
505  * @param[in] filename File than needs to be loaded
506  *
507  * @return Image loader
508  */
509  static std::unique_ptr<IImageLoader> create(const std::string &filename)
510  {
512  switch(type)
513  {
514  case ImageType::PPM:
515  return std::make_unique<PPMLoader>();
516  case ImageType::JPEG:
517  return std::make_unique<JPEGLoader>();
518  case ImageType::UNKNOWN:
519  default:
520  return nullptr;
521  }
522  }
523 };
524 } // namespace utils
525 } // namespace arm_compute
526 #endif /* __UTILS_IMAGE_LOADER_H__*/
virtual void get_row(uint8_t *dst, size_t row_size)=0
Feed a whole row to a destination pointer.
void close() override
Closes an image file.
Definition: ImageLoader.h:402
Class to load the content of a JPEG file into an Image.
Definition: ImageLoader.h:432
virtual uint8_t get()=0
Gets a character from an image feed.
Shape of a tensor.
Definition: TensorShape.h:39
MemoryImageFeeder(const uint8_t *data)
Default constructor.
Definition: ImageLoader.h:97
virtual void close()=0
Closes an image file.
#define ARM_COMPUTE_ERROR(msg)
Print the given message then throw an std::runtime_error.
Definition: Error.h:352
void open(const std::string &filename) override
Open an image file and reads its metadata (Width, height)
Definition: ImageLoader.h:381
1 channel, 1 U8 per channel
virtual void open(const std::string &filename)=0
Open an image file and reads its metadata (Width, height)
1 channel, 1 F32 per channel
MemoryImageFeeder & operator=(const MemoryImageFeeder &)=delete
Prevent instances of this class from being copied (As this class contains pointers)
#define ARM_COMPUTE_ERROR_VAR(msg,...)
Print the given message then throw an std::runtime_error.
Definition: Error.h:346
#define ARM_COMPUTE_ERROR_ON(cond)
If the condition is true then an error message is printed and an exception thrown.
Definition: Error.h:466
const DataLayout data_layout
Definition: Im2Col.cpp:151
Store the tensor's metadata.
Definition: ITensorInfo.h:40
Describe one of the image's dimensions with a start, end and step.
Definition: Window.h:77
#define ARM_COMPUTE_ERROR_ON_MSG_VAR(cond, msg,...)
Definition: Error.h:457
static std::unique_ptr< IImageLoader > create(const std::string &filename)
Create an image loader depending on the image type.
Definition: ImageLoader.h:509
virtual ~IImageLoader()=default
Virtual base destructor.
#define ARM_COMPUTE_ERROR_ON_FORMAT_NOT_IN(t,...)
Definition: Validate.h:639
Image loader interface.
Definition: ImageLoader.h:126
void fill_planar_tensor(T &tensor, bool bgr=false)
Fill a tensor with 3 planes (one for each channel) with the content of the currently open image file.
Definition: ImageLoader.h:257
decltype(strategy::transforms) typedef type
IImageLoader()
Default Constructor.
Definition: ImageLoader.h:130
JPEGLoader()
Default Constructor.
Definition: ImageLoader.h:446
Copyright (c) 2017-2021 Arm Limited.
virtual bool is_open()=0
Return true if the image file is currently open.
1 channel, 1 F16 per channel
void unmap(T &tensor)
Unmaps a tensor if needed.
Definition: Utils.h:223
bool is_open() override
Return true if the image file is currently open.
Definition: ImageLoader.h:377
3 channels, 1 U8 per channel
static constexpr size_t DimX
Alias for dimension 0 also known as X dimension.
Definition: Window.h:43
#define ARM_COMPUTE_UNUSED(...)
To avoid unused variables warnings.
Definition: Error.h:152
virtual const TensorShape & tensor_shape() const =0
Size for each dimension of the tensor.
Format
Image colour formats.
Definition: Types.h:54
uint8_t get() override
Gets a character from an image feed.
Definition: ImageLoader.h:76
File Image feeder concrete implementation.
Definition: ImageLoader.h:64
quantized, asymmetric fixed-point 8-bit number unsigned
Factory for generating appropriate image loader.
Definition: ImageLoader.h:500
#define ARM_COMPUTE_ERROR_ON_MSG(cond, msg)
Definition: Error.h:456
unsigned int width() const
Return the width of the currently open image file.
Definition: ImageLoader.h:137
Coordinates of an item.
Definition: Coordinates.h:37
size_t total_size() const
Collapses all dimensions to a single linear total size.
Definition: TensorShape.h:172
void get_row(uint8_t *dst, size_t row_size) override
Feed a whole row to a destination pointer.
Definition: ImageLoader.h:114
constexpr uint8_t * ptr() const
Return a pointer to the current pixel.
Definition: Helpers.inl:139
void end(TokenStream &in, bool &valid)
Definition: MLGOParser.cpp:290
void set(size_t dimension, const Dimension &dim)
Set the values of a given dimension.
Definition: Window.inl:49
Image feeder interface.
Definition: ImageLoader.h:49
Memory Image feeder concrete implementation.
Definition: ImageLoader.h:90
Num samples, channels, height, width.
#define ARM_COMPUTE_ERROR_ON_DATA_TYPE_CHANNEL_NOT_IN(t, c,...)
Definition: Validate.h:786
void fill_image(T &image)
Fill an image with the content of the currently open image file.
Definition: ImageLoader.h:177
static constexpr size_t DimY
Alias for dimension 1 also known as Y dimension.
Definition: Window.h:45
void get_row(uint8_t *dst, size_t row_size) override
Feed a whole row to a destination pointer.
Definition: ImageLoader.h:80
bool is_open() override
Return true if the image file is currently open.
Definition: ImageLoader.h:452
virtual ~IImageDataFeeder()=default
Virtual base destructor.
void map(T &tensor, bool blocking)
Maps a tensor if needed.
Definition: Utils.h:212
static constexpr size_t DimZ
Alias for dimension 2 also known as Z dimension.
Definition: Window.h:47
void close() override
Closes an image file.
Definition: ImageLoader.h:473
FileImageFeeder(std::ifstream &fs)
Default constructor.
Definition: ImageLoader.h:71
ImageType
Supported image types.
Definition: Utils.h:63
std::tuple< unsigned int, unsigned int, int > parse_ppm_header(std::ifstream &fs)
Parse the ppm header from an input file stream.
Definition: Utils.cpp:202
Store the tensor's metadata.
Definition: TensorInfo.h:43
void execute_window_loop(const Window &w, L &&lambda_function, Ts &&... iterators)
Iterate through the passed window, automatically adjusting the iterators and calling the lambda_funct...
Definition: Helpers.inl:77
ImageType get_image_type_from_file(const std::string &filename)
Gets image type given a file.
Definition: Utils.cpp:167
PPMLoader()
Default Constructor.
Definition: ImageLoader.h:371
uint8_t get() override
Gets a character from an image feed.
Definition: ImageLoader.h:110
size_t get_data_layout_dimension_index(const DataLayout data_layout, const DataLayoutDimension data_layout_dimension)
Get the index of the given dimension.
Definition: Helpers.inl:193
Iterator updated by execute_window_loop for each window element.
Definition: Helpers.h:46
unsigned int height() const
Return the height of the currently open image file.
Definition: ImageLoader.h:142
DataLayout
[DataLayout enum definition]
Definition: Types.h:114
Describe a multidimensional execution window.
Definition: Window.h:39
void init_image(T &image, Format format)
Initialise an image's metadata with the dimensions of the image file currently open.
Definition: ImageLoader.h:161
PPM Image loader concrete implementation.
Definition: ImageLoader.h:367
void open(const std::string &filename) override
Open an image file and reads its metadata (Width, height)
Definition: ImageLoader.h:456
void release()
Explicitly Releases the memory of the loaded data.
Definition: ImageLoader.h:484