aboutsummaryrefslogtreecommitdiff
path: root/utils
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
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')
-rw-r--r--utils/GraphUtils.cpp66
-rw-r--r--utils/GraphUtils.h32
-rw-r--r--utils/ImageLoader.h491
-rw-r--r--utils/Utils.h260
4 files changed, 588 insertions, 261 deletions
diff --git a/utils/GraphUtils.cpp b/utils/GraphUtils.cpp
index b51e122d61..4db053cf9f 100644
--- a/utils/GraphUtils.cpp
+++ b/utils/GraphUtils.cpp
@@ -27,6 +27,7 @@
#include "arm_compute/core/Helpers.h"
#include "arm_compute/core/Types.h"
#include "arm_compute/runtime/SubTensor.h"
+#include "utils/ImageLoader.h"
#include "utils/Utils.h"
#include <iomanip>
@@ -202,6 +203,71 @@ bool PPMAccessor::access_tensor(ITensor &tensor)
return true;
}
+ValidationInputAccessor::ValidationInputAccessor(const std::string &image_list,
+ std::string images_path,
+ bool bgr,
+ unsigned int start,
+ unsigned int end)
+ : _path(std::move(images_path)), _images(), _bgr(bgr), _offset(0)
+{
+ ARM_COMPUTE_ERROR_ON_MSG(start > end, "Invalid validation range!");
+
+ std::ifstream ifs;
+ try
+ {
+ ifs.exceptions(std::ifstream::badbit);
+ ifs.open(image_list, std::ios::in | std::ios::binary);
+
+ // Parse image names
+ unsigned int counter = 0;
+ for(std::string line; !std::getline(ifs, line).fail() && counter <= end; ++counter)
+ {
+ // Add image to process if withing range
+ if(counter >= start)
+ {
+ std::stringstream linestream(line);
+ std::string image_name;
+
+ linestream >> image_name;
+ _images.emplace_back(std::move(image_name));
+ }
+ }
+ }
+ catch(const std::ifstream::failure &e)
+ {
+ ARM_COMPUTE_ERROR("Accessing %s: %s", image_list.c_str(), e.what());
+ }
+}
+
+bool ValidationInputAccessor::access_tensor(arm_compute::ITensor &tensor)
+{
+ bool ret = _offset < _images.size();
+ if(ret)
+ {
+ utils::JPEGLoader jpeg;
+
+ // Open JPEG file
+ jpeg.open(_path + _images[_offset++]);
+
+ // Get permutated shape and permutation parameters
+ TensorShape permuted_shape = tensor.info()->tensor_shape();
+ arm_compute::PermutationVector perm;
+ if(tensor.info()->data_layout() != DataLayout::NCHW)
+ {
+ std::tie(permuted_shape, perm) = compute_permutation_paramaters(tensor.info()->tensor_shape(),
+ tensor.info()->data_layout());
+ }
+ ARM_COMPUTE_ERROR_ON_MSG(jpeg.width() != permuted_shape.x() || jpeg.height() != permuted_shape.y(),
+ "Failed to load image file: dimensions [%d,%d] not correct, expected [%d,%d].",
+ jpeg.width(), jpeg.height(), permuted_shape.x(), permuted_shape.y());
+
+ // Fill the tensor with the PPM content (BGR)
+ jpeg.fill_planar_tensor(tensor, _bgr);
+ }
+
+ return ret;
+}
+
TopNPredictionsAccessor::TopNPredictionsAccessor(const std::string &labels_path, size_t top_n, std::ostream &output_stream)
: _labels(), _output_stream(output_stream), _top_n(top_n)
{
diff --git a/utils/GraphUtils.h b/utils/GraphUtils.h
index 597708369d..349d8558fc 100644
--- a/utils/GraphUtils.h
+++ b/utils/GraphUtils.h
@@ -157,7 +157,7 @@ public:
/** Constructor
*
* @param[in] ppm_path Path to PPM file
- * @param[in] bgr (Optional) Fill the first plane with blue channel (default = false)
+ * @param[in] bgr (Optional) Fill the first plane with blue channel (default = false - RGB format)
* @param[in] preprocessor (Optional) PPM pre-processing object
*/
PPMAccessor(std::string ppm_path, bool bgr = true, std::unique_ptr<IPreprocessor> preprocessor = nullptr);
@@ -173,6 +173,36 @@ private:
std::unique_ptr<IPreprocessor> _preprocessor;
};
+/** Input Accessor used for network validation */
+class ValidationInputAccessor final : public graph::ITensorAccessor
+{
+public:
+ /** Constructor
+ *
+ * @param[in] image_list File containing all the images to validate
+ * @param[in] images_path Path to images.
+ * @param[in] bgr (Optional) Fill the first plane with blue channel (default = false - RGB format)
+ * @param[in] start (Optional) Start range
+ * @param[in] end (Optional) End range
+ *
+ * @note
+ */
+ ValidationInputAccessor(const std::string &image_list,
+ std::string images_path,
+ bool bgr = true,
+ unsigned int start = 0,
+ unsigned int end = 0);
+
+ // Inherited methods overriden:
+ bool access_tensor(ITensor &tensor) override;
+
+private:
+ std::string _path;
+ std::vector<std::string> _images;
+ bool _bgr;
+ size_t _offset;
+};
+
/** Result accessor class */
class TopNPredictionsAccessor final : public graph::ITensorAccessor
{
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__*/
diff --git a/utils/Utils.h b/utils/Utils.h
index 6cb71fd3ba..c18ad217a4 100644
--- a/utils/Utils.h
+++ b/utils/Utils.h
@@ -254,266 +254,6 @@ inline void unmap(GCTensor &tensor)
}
#endif /* ARM_COMPUTE_GC */
-/** Class to load the content of a PPM file into an Image
- */
-class PPMLoader
-{
-public:
- PPMLoader()
- : _fs(), _width(0), _height(0)
- {
- }
- /** Open a PPM file and reads its metadata (Width, height)
- *
- * @param[in] ppm_filename File to open
- */
- void open(const std::string &ppm_filename)
- {
- ARM_COMPUTE_ERROR_ON(is_open());
- try
- {
- _fs.exceptions(std::ifstream::failbit | std::ifstream::badbit);
- _fs.open(ppm_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", ppm_filename.c_str());
- }
- catch(std::runtime_error &e)
- {
- ARM_COMPUTE_ERROR("Accessing %s: %s", ppm_filename.c_str(), e.what());
- }
- }
- /** Return true if a PPM file is currently open
- */
- bool is_open()
- {
- return _fs.is_open();
- }
-
- /** Initialise an image's metadata with the dimensions of the PPM 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, arm_compute::Format format)
- {
- ARM_COMPUTE_ERROR_ON(!is_open());
- ARM_COMPUTE_ERROR_ON(format != arm_compute::Format::RGB888 && format != arm_compute::Format::U8);
-
- // Use the size of the input PPM image
- arm_compute::TensorInfo image_info(_width, _height, format);
- image.allocator()->init(image_info);
- }
-
- /** Fill an image with the content of the currently open PPM 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 PPM).
- */
- 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, arm_compute::Format::U8, arm_compute::Format::RGB888);
- try
- {
- // Map buffer if creating a CLTensor/GCTensor
- map(image, true);
-
- // 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) < image.info()->tensor_shape().total_size() * image.info()->element_size(),
- "Not enough data in file");
- ARM_COMPUTE_UNUSED(end_position);
-
- switch(image.info()->format())
- {
- case arm_compute::Format::U8:
- {
- // We need to convert the data from RGB to grayscale:
- // Iterate through every pixel of the image
- arm_compute::Window window;
- window.set(arm_compute::Window::DimX, arm_compute::Window::Dimension(0, _width, 1));
- window.set(arm_compute::Window::DimY, arm_compute::Window::Dimension(0, _height, 1));
-
- arm_compute::Iterator out(&image, window);
-
- unsigned char red = 0;
- unsigned char green = 0;
- unsigned char blue = 0;
-
- arm_compute::execute_window_loop(window, [&](const arm_compute::Coordinates & id)
- {
- red = _fs.get();
- green = _fs.get();
- blue = _fs.get();
-
- *out.ptr() = 0.2126f * red + 0.7152f * green + 0.0722f * blue;
- },
- out);
-
- break;
- }
- case arm_compute::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:
- arm_compute::Window window;
- window.set(arm_compute::Window::DimY, arm_compute::Window::Dimension(0, _height, 1));
-
- arm_compute::Iterator out(&image, window);
-
- arm_compute::execute_window_loop(window, [&](const arm_compute::Coordinates & id)
- {
- // Copy one row from the input file to the current row of the image:
- _fs.read(reinterpret_cast<std::fstream::char_type *>(out.ptr()), _width * image.info()->element_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 PPM file: %s", e.what());
- }
- }
-
- /** Fill a tensor with 3 planes (one for each channel) with the content of the currently open PPM 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 PPM). 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);
-
- try
- {
- // Map buffer if creating a CLTensor
- map(tensor, true);
-
- // 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);
-
- // Stride across channels
- size_t stride_z = 0;
-
- // Iterate through every pixel of the image
- arm_compute::Window window;
- if(data_layout == DataLayout::NCHW)
- {
- window.set(arm_compute::Window::DimX, arm_compute::Window::Dimension(0, _width, 1));
- window.set(arm_compute::Window::DimY, arm_compute::Window::Dimension(0, _height, 1));
- window.set(arm_compute::Window::DimZ, arm_compute::Window::Dimension(0, 1, 1));
- stride_z = tensor.info()->strides_in_bytes()[2];
- }
- else
- {
- window.set(arm_compute::Window::DimX, arm_compute::Window::Dimension(0, 1, 1));
- window.set(arm_compute::Window::DimY, arm_compute::Window::Dimension(0, _width, 1));
- window.set(arm_compute::Window::DimZ, arm_compute::Window::Dimension(0, _height, 1));
- stride_z = tensor.info()->strides_in_bytes()[0];
- }
-
- arm_compute::Iterator out(&tensor, window);
-
- unsigned char red = 0;
- unsigned char green = 0;
- unsigned char blue = 0;
-
- arm_compute::execute_window_loop(window, [&](const arm_compute::Coordinates & id)
- {
- red = _fs.get();
- green = _fs.get();
- blue = _fs.get();
-
- switch(tensor.info()->data_type())
- {
- case arm_compute::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 arm_compute::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 PPM file: %s", e.what());
- }
- }
-
- /** Return the width of the currently open PPM file.
- */
- unsigned int width() const
- {
- return _width;
- }
-
- /** Return the height of the currently open PPM file.
- */
- unsigned int height() const
- {
- return _height;
- }
-
-private:
- std::ifstream _fs;
- unsigned int _width, _height;
-};
-
/** Numpy data loader */
class NPYLoader
{