aboutsummaryrefslogtreecommitdiff
path: root/utils/ImageLoader.h
diff options
context:
space:
mode:
authorGeorgios Pinitas <georgios.pinitas@arm.com>2018-06-21 19:01:25 +0100
committerAnthony Barbier <anthony.barbier@arm.com>2018-11-02 16:53:57 +0000
commit7c3b92403958e8970e901fd15b2fc904e7996eee (patch)
tree09250c8269ad2f78f2a8587f454d83728e97e9dc /utils/ImageLoader.h
parentf3139dde4b93b4ea54e4f3bcd27e639c22a7b6ca (diff)
downloadComputeLibrary-7c3b92403958e8970e901fd15b2fc904e7996eee.tar.gz
COMPMID-1308: Add and validate JPEG accessors
Change-Id: I93d7345a795b26153600287346d672209dbb2622 Reviewed-on: https://eu-gerrit-1.euhpc.arm.com/137479 Tested-by: Jenkins <bsgcomp@arm.com> Reviewed-by: Anthony Barbier <anthony.barbier@arm.com>
Diffstat (limited to 'utils/ImageLoader.h')
-rw-r--r--utils/ImageLoader.h491
1 files changed, 491 insertions, 0 deletions
diff --git a/utils/ImageLoader.h b/utils/ImageLoader.h
new file mode 100644
index 0000000000..edc89286a2
--- /dev/null
+++ b/utils/ImageLoader.h
@@ -0,0 +1,491 @@
+/*
+ * Copyright (c) 2018 ARM Limited.
+ *
+ * SPDX-License-Identifier: MIT
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+#ifndef __UTILS_IMAGE_LOADER_H__
+#define __UTILS_IMAGE_LOADER_H__
+
+#include "arm_compute/core/Error.h"
+#include "arm_compute/core/ITensor.h"
+#include "arm_compute/core/TensorInfo.h"
+#include "arm_compute/core/Types.h"
+
+#include "utils/Utils.h"
+
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wswitch-default"
+#define STB_IMAGE_IMPLEMENTATION
+#include "stb/stb_image.h"
+#pragma GCC diagnostic pop
+
+#include <cstdlib>
+#include <memory>
+#include <string>
+
+namespace arm_compute
+{
+namespace utils
+{
+/** Image feeder interface */
+class IImageDataFeeder
+{
+public:
+ /** Virtual base destructor */
+ virtual ~IImageDataFeeder() = default;
+ /** Gets a character from an image feed */
+ virtual uint8_t get() = 0;
+ /** Feed a whole row to a destination pointer
+ *
+ * @param[out] dst Destination pointer
+ * @param[in] row_size Row size in terms of bytes
+ */
+ virtual void get_row(uint8_t *dst, size_t row_size) = 0;
+};
+/** File Image feeder concrete implementation */
+class FileImageFeeder : public IImageDataFeeder
+{
+public:
+ /** Default constructor
+ *
+ * @param[in] fs Image file stream
+ */
+ FileImageFeeder(std::ifstream &fs)
+ : _fs(fs)
+ {
+ }
+ // Inherited overridden methods
+ uint8_t get() override
+ {
+ return _fs.get();
+ }
+ void get_row(uint8_t *dst, size_t row_size) override
+ {
+ ARM_COMPUTE_ERROR_ON(dst == nullptr);
+ _fs.read(reinterpret_cast<std::fstream::char_type *>(dst), row_size);
+ }
+
+private:
+ std::ifstream &_fs;
+};
+/** Memory Image feeder concrete implementation */
+class MemoryImageFeeder : public IImageDataFeeder
+{
+public:
+ /** Default constructor
+ *
+ * @param[in] data Pointer to data
+ */
+ MemoryImageFeeder(const uint8_t *data)
+ : _data(data)
+ {
+ }
+ /** Prevent instances of this class from being copied (As this class contains pointers) */
+ MemoryImageFeeder(const MemoryImageFeeder &) = delete;
+ /** Default move constructor */
+ MemoryImageFeeder(MemoryImageFeeder &&) = default;
+ /** Prevent instances of this class from being copied (As this class contains pointers) */
+ MemoryImageFeeder &operator=(const MemoryImageFeeder &) = delete;
+ /** Default move assignment operator */
+ MemoryImageFeeder &operator=(MemoryImageFeeder &&) = default;
+ // Inherited overridden methods
+ uint8_t get() override
+ {
+ return *_data++;
+ }
+ void get_row(uint8_t *dst, size_t row_size) override
+ {
+ ARM_COMPUTE_ERROR_ON(dst == nullptr);
+ memcpy(dst, _data, row_size);
+ _data += row_size;
+ }
+
+private:
+ const uint8_t *_data;
+};
+
+/** Image loader interface */
+class IImageLoader
+{
+public:
+ /** Default Constructor */
+ IImageLoader()
+ : _feeder(nullptr), _width(0), _height(0)
+ {
+ }
+ /** Virtual base destructor */
+ virtual ~IImageLoader() = default;
+ /** Return the width of the currently open image file. */
+ unsigned int width() const
+ {
+ return _width;
+ }
+ /** Return the height of the currently open image file. */
+ unsigned int height() const
+ {
+ return _height;
+ }
+ /** Return true if the image file is currently open */
+ virtual bool is_open() = 0;
+ /** Open an image file and reads its metadata (Width, height)
+ *
+ * @param[in] filename File to open
+ */
+ virtual void open(const std::string &filename) = 0;
+ /** Closes an image file */
+ virtual void close() = 0;
+ /** Initialise an image's metadata with the dimensions of the image file currently open
+ *
+ * @param[out] image Image to initialise
+ * @param[in] format Format to use for the image (Must be RGB888 or U8)
+ */
+ template <typename T>
+ void init_image(T &image, Format format)
+ {
+ ARM_COMPUTE_ERROR_ON(!is_open());
+ ARM_COMPUTE_ERROR_ON(format != Format::RGB888 && format != Format::U8);
+
+ // Use the size of the input image
+ TensorInfo image_info(_width, _height, format);
+ image.allocator()->init(image_info);
+ }
+ /** Fill an image with the content of the currently open image file.
+ *
+ * @note If the image is a CLImage, the function maps and unmaps the image
+ *
+ * @param[in,out] image Image to fill (Must be allocated, and of matching dimensions with the opened image file).
+ */
+ template <typename T>
+ void fill_image(T &image)
+ {
+ ARM_COMPUTE_ERROR_ON(!is_open());
+ ARM_COMPUTE_ERROR_ON(image.info()->dimension(0) != _width || image.info()->dimension(1) != _height);
+ ARM_COMPUTE_ERROR_ON_FORMAT_NOT_IN(&image, Format::U8, Format::RGB888);
+ ARM_COMPUTE_ERROR_ON(_feeder.get() == nullptr);
+ try
+ {
+ // Map buffer if creating a CLTensor/GCTensor
+ map(image, true);
+
+ // Validate feeding data
+ validate_info(image.info());
+
+ switch(image.info()->format())
+ {
+ case Format::U8:
+ {
+ // We need to convert the data from RGB to grayscale:
+ // Iterate through every pixel of the image
+ Window window;
+ window.set(Window::DimX, Window::Dimension(0, _width, 1));
+ window.set(Window::DimY, Window::Dimension(0, _height, 1));
+
+ Iterator out(&image, window);
+
+ unsigned char red = 0;
+ unsigned char green = 0;
+ unsigned char blue = 0;
+
+ execute_window_loop(window, [&](const Coordinates & id)
+ {
+ red = _feeder->get();
+ green = _feeder->get();
+ blue = _feeder->get();
+
+ *out.ptr() = 0.2126f * red + 0.7152f * green + 0.0722f * blue;
+ },
+ out);
+
+ break;
+ }
+ case Format::RGB888:
+ {
+ // There is no format conversion needed: we can simply copy the content of the input file to the image one row at the time.
+ // Create a vertical window to iterate through the image's rows:
+ Window window;
+ window.set(Window::DimY, Window::Dimension(0, _height, 1));
+
+ Iterator out(&image, window);
+ size_t row_size = _width * image.info()->element_size();
+
+ execute_window_loop(window, [&](const Coordinates & id)
+ {
+ _feeder->get_row(out.ptr(), row_size);
+ },
+ out);
+
+ break;
+ }
+ default:
+ ARM_COMPUTE_ERROR("Unsupported format");
+ }
+
+ // Unmap buffer if creating a CLTensor/GCTensor
+ unmap(image);
+ }
+ catch(const std::ifstream::failure &e)
+ {
+ ARM_COMPUTE_ERROR("Loading image file: %s", e.what());
+ }
+ }
+ /** Fill a tensor with 3 planes (one for each channel) with the content of the currently open image file.
+ *
+ * @note If the image is a CLImage, the function maps and unmaps the image
+ *
+ * @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/F32
+ * @param[in] bgr (Optional) Fill the first plane with blue channel (default = false)
+ */
+ template <typename T>
+ void fill_planar_tensor(T &tensor, bool bgr = false)
+ {
+ ARM_COMPUTE_ERROR_ON(!is_open());
+ ARM_COMPUTE_ERROR_ON_DATA_TYPE_CHANNEL_NOT_IN(&tensor, 1, DataType::U8, DataType::F32);
+
+ const DataLayout data_layout = tensor.info()->data_layout();
+ const TensorShape tensor_shape = tensor.info()->tensor_shape();
+
+ ARM_COMPUTE_UNUSED(tensor_shape);
+ ARM_COMPUTE_ERROR_ON(tensor_shape[get_data_layout_dimension_index(data_layout, DataLayoutDimension::WIDTH)] != _width);
+ ARM_COMPUTE_ERROR_ON(tensor_shape[get_data_layout_dimension_index(data_layout, DataLayoutDimension::HEIGHT)] != _height);
+ ARM_COMPUTE_ERROR_ON(tensor_shape[get_data_layout_dimension_index(data_layout, DataLayoutDimension::CHANNEL)] != 3);
+
+ ARM_COMPUTE_ERROR_ON(_feeder.get() == nullptr);
+
+ try
+ {
+ // Map buffer if creating a CLTensor
+ map(tensor, true);
+
+ // Validate feeding data
+ validate_info(tensor.info());
+
+ // Stride across channels
+ size_t stride_z = 0;
+
+ // Iterate through every pixel of the image
+ Window window;
+ if(data_layout == DataLayout::NCHW)
+ {
+ window.set(Window::DimX, Window::Dimension(0, _width, 1));
+ window.set(Window::DimY, Window::Dimension(0, _height, 1));
+ window.set(Window::DimZ, Window::Dimension(0, 1, 1));
+ stride_z = tensor.info()->strides_in_bytes()[2];
+ }
+ else
+ {
+ window.set(Window::DimX, Window::Dimension(0, 1, 1));
+ window.set(Window::DimY, Window::Dimension(0, _width, 1));
+ window.set(Window::DimZ, Window::Dimension(0, _height, 1));
+ stride_z = tensor.info()->strides_in_bytes()[0];
+ }
+
+ Iterator out(&tensor, window);
+
+ unsigned char red = 0;
+ unsigned char green = 0;
+ unsigned char blue = 0;
+
+ execute_window_loop(window, [&](const Coordinates & id)
+ {
+ red = _feeder->get();
+ green = _feeder->get();
+ blue = _feeder->get();
+
+ switch(tensor.info()->data_type())
+ {
+ case DataType::U8:
+ {
+ *(out.ptr() + 0 * stride_z) = bgr ? blue : red;
+ *(out.ptr() + 1 * stride_z) = green;
+ *(out.ptr() + 2 * stride_z) = bgr ? red : blue;
+ break;
+ }
+ case DataType::F32:
+ {
+ *reinterpret_cast<float *>(out.ptr() + 0 * stride_z) = static_cast<float>(bgr ? blue : red);
+ *reinterpret_cast<float *>(out.ptr() + 1 * stride_z) = static_cast<float>(green);
+ *reinterpret_cast<float *>(out.ptr() + 2 * stride_z) = static_cast<float>(bgr ? red : blue);
+ break;
+ }
+ default:
+ {
+ ARM_COMPUTE_ERROR("Unsupported data type");
+ }
+ }
+ },
+ out);
+
+ // Unmap buffer if creating a CLTensor
+ unmap(tensor);
+ }
+ catch(const std::ifstream::failure &e)
+ {
+ ARM_COMPUTE_ERROR("Loading image file: %s", e.what());
+ }
+ }
+
+protected:
+ /** Validate metadata */
+ virtual void validate_info(const ITensorInfo *tensor_info)
+ {
+ }
+
+protected:
+ std::unique_ptr<IImageDataFeeder> _feeder;
+ unsigned int _width;
+ unsigned int _height;
+};
+
+/** PPM Image loader concrete implementation */
+class PPMLoader : public IImageLoader
+{
+public:
+ /** Default Constructor */
+ PPMLoader()
+ : IImageLoader(), _fs()
+ {
+ }
+
+ // Inherited methods overridden:
+ bool is_open() override
+ {
+ return _fs.is_open();
+ }
+ void open(const std::string &filename) override
+ {
+ ARM_COMPUTE_ERROR_ON(is_open());
+ try
+ {
+ _fs.exceptions(std::ifstream::failbit | std::ifstream::badbit);
+ _fs.open(filename, std::ios::in | std::ios::binary);
+
+ unsigned int max_val = 0;
+ std::tie(_width, _height, max_val) = parse_ppm_header(_fs);
+
+ ARM_COMPUTE_ERROR_ON_MSG(max_val >= 256, "2 bytes per colour channel not supported in file %s",
+ filename.c_str());
+
+ _feeder = support::cpp14::make_unique<FileImageFeeder>(_fs);
+ }
+ catch(std::runtime_error &e)
+ {
+ ARM_COMPUTE_ERROR("Accessing %s: %s", filename.c_str(), e.what());
+ }
+ }
+ void close() override
+ {
+ if(is_open())
+ {
+ _fs.close();
+ _feeder = nullptr;
+ }
+ ARM_COMPUTE_ERROR_ON(is_open());
+ }
+
+protected:
+ // Inherited methods overridden:
+ void validate_info(const ITensorInfo *tensor_info) override
+ {
+ // Check if the file is large enough to fill the image
+ const size_t current_position = _fs.tellg();
+ _fs.seekg(0, std::ios_base::end);
+ const size_t end_position = _fs.tellg();
+ _fs.seekg(current_position, std::ios_base::beg);
+
+ ARM_COMPUTE_ERROR_ON_MSG((end_position - current_position) < tensor_info->tensor_shape().total_size(),
+ "Not enough data in file");
+ ARM_COMPUTE_UNUSED(end_position);
+ }
+
+private:
+ std::ifstream _fs;
+};
+
+/** Class to load the content of a JPEG file into an Image */
+class JPEGLoader : public IImageLoader
+{
+private:
+ /** Custom malloc deleter struct */
+ struct malloc_deleter
+ {
+ void operator()(uint8_t *p) const
+ {
+ free(p);
+ }
+ };
+
+public:
+ /** Default Constructor */
+ JPEGLoader()
+ : IImageLoader(), _is_loaded(false), _data(nullptr)
+ {
+ }
+
+ // Inherited methods overridden:
+ bool is_open() override
+ {
+ return _is_loaded;
+ }
+ void open(const std::string &filename) override
+ {
+ int bpp, width, height;
+ uint8_t *rgb_image = stbi_load(filename.c_str(), &width, &height, &bpp, 3);
+ if(rgb_image == NULL)
+ {
+ ARM_COMPUTE_ERROR("Accessing %s failed", filename.c_str());
+ }
+ else
+ {
+ _width = width;
+ _height = height;
+ _data = std::unique_ptr<uint8_t, malloc_deleter>(rgb_image);
+ _is_loaded = true;
+ _feeder = support::cpp14::make_unique<MemoryImageFeeder>(_data.get());
+ }
+ }
+ void close() override
+ {
+ if(is_open())
+ {
+ _width = 0;
+ _height = 0;
+ release();
+ }
+ ARM_COMPUTE_ERROR_ON(is_open());
+ }
+ /** Explicitly Releases the memory of the loaded data */
+ void release()
+ {
+ if(_is_loaded)
+ {
+ _data.reset();
+ _is_loaded = false;
+ _feeder = nullptr;
+ }
+ }
+
+private:
+ bool _is_loaded;
+ std::unique_ptr<uint8_t, malloc_deleter> _data;
+};
+} // namespace utils
+} // namespace arm_compute
+#endif /* __UTILS_IMAGE_LOADER_H__*/