aboutsummaryrefslogtreecommitdiff
path: root/tests/InferenceTestImage.cpp
diff options
context:
space:
mode:
authortelsoa01 <telmo.soares@arm.com>2018-03-09 14:13:49 +0000
committertelsoa01 <telmo.soares@arm.com>2018-03-09 14:13:49 +0000
commit4fcda0101ec3d110c1d6d7bee5c83416b645528a (patch)
treec9a70aeb2887006160c1b3d265c27efadb7bdbae /tests/InferenceTestImage.cpp
downloadarmnn-4fcda0101ec3d110c1d6d7bee5c83416b645528a.tar.gz
Release 18.02
Change-Id: Id3c11dc5ee94ef664374a988fcc6901e9a232fa6
Diffstat (limited to 'tests/InferenceTestImage.cpp')
-rw-r--r--tests/InferenceTestImage.cpp224
1 files changed, 224 insertions, 0 deletions
diff --git a/tests/InferenceTestImage.cpp b/tests/InferenceTestImage.cpp
new file mode 100644
index 0000000000..8fc6f12867
--- /dev/null
+++ b/tests/InferenceTestImage.cpp
@@ -0,0 +1,224 @@
+//
+// Copyright © 2017 Arm Ltd. All rights reserved.
+// See LICENSE file in the project root for full license information.
+//
+#include "InferenceTestImage.hpp"
+
+#include <boost/core/ignore_unused.hpp>
+#include <boost/format.hpp>
+#include <boost/core/ignore_unused.hpp>
+#include <boost/numeric/conversion/cast.hpp>
+
+#include <array>
+
+#define STB_IMAGE_IMPLEMENTATION
+#include <stb_image.h>
+
+#define STB_IMAGE_RESIZE_IMPLEMENTATION
+#include <stb_image_resize.h>
+
+#define STB_IMAGE_WRITE_IMPLEMENTATION
+#include <stb_image_write.h>
+
+namespace
+{
+
+unsigned int GetImageChannelIndex(ImageChannelLayout channelLayout, ImageChannel channel)
+{
+ switch (channelLayout)
+ {
+ case ImageChannelLayout::Rgb:
+ return static_cast<unsigned int>(channel);
+ case ImageChannelLayout::Bgr:
+ return 2u - static_cast<unsigned int>(channel);
+ default:
+ throw UnknownImageChannelLayout(boost::str(boost::format("Unknown layout %1%")
+ % static_cast<int>(channelLayout)));
+ }
+}
+
+} // namespace
+
+InferenceTestImage::InferenceTestImage(char const* filePath)
+ : m_Width(0u)
+ , m_Height(0u)
+ , m_NumChannels(0u)
+{
+ int width;
+ int height;
+ int channels;
+
+ using StbImageDataPtr = std::unique_ptr<unsigned char, decltype(&stbi_image_free)>;
+ StbImageDataPtr stbData(stbi_load(filePath, &width, &height, &channels, 0), &stbi_image_free);
+
+ if (stbData == nullptr)
+ {
+ throw InferenceTestImageLoadFailed(boost::str(boost::format("Could not load the image at %1%") % filePath));
+ }
+
+ if (width == 0 || height == 0)
+ {
+ throw InferenceTestImageLoadFailed(boost::str(boost::format("Could not load empty image at %1%") % filePath));
+ }
+
+ m_Width = boost::numeric_cast<unsigned int>(width);
+ m_Height = boost::numeric_cast<unsigned int>(height);
+ m_NumChannels = boost::numeric_cast<unsigned int>(channels);
+
+ const unsigned int sizeInBytes = GetSizeInBytes();
+ m_Data.resize(sizeInBytes);
+ memcpy(m_Data.data(), stbData.get(), sizeInBytes);
+}
+
+std::tuple<uint8_t, uint8_t, uint8_t> InferenceTestImage::GetPixelAs3Channels(unsigned int x, unsigned int y) const
+{
+ if (x >= m_Width || y >= m_Height)
+ {
+ throw InferenceTestImageOutOfBoundsAccess(boost::str(boost::format("Attempted out of bounds image access. "
+ "Requested (%1%, %2%). Maximum valid coordinates (%3%, %4%).") % x % y % (m_Width - 1) % (m_Height - 1)));
+ }
+
+ const unsigned int pixelOffset = x * GetNumChannels() + y * GetWidth() * GetNumChannels();
+ const uint8_t* const pixelData = m_Data.data() + pixelOffset;
+ BOOST_ASSERT(pixelData <= (m_Data.data() + GetSizeInBytes()));
+
+ std::array<uint8_t, 3> outPixelData;
+ outPixelData.fill(0);
+
+ const unsigned int maxChannelsInPixel = std::min(GetNumChannels(), static_cast<unsigned int>(outPixelData.size()));
+ for (unsigned int c = 0; c < maxChannelsInPixel; ++c)
+ {
+ outPixelData[c] = pixelData[c];
+ }
+
+ return std::make_tuple(outPixelData[0], outPixelData[1], outPixelData[2]);
+}
+
+void InferenceTestImage::Resize(unsigned int newWidth, unsigned int newHeight)
+{
+ if (newWidth == 0 || newHeight == 0)
+ {
+ throw InferenceTestImageResizeFailed(boost::str(boost::format("None of the dimensions passed to a resize "
+ "operation can be zero. Requested width: %1%. Requested height: %2%.") % newWidth % newHeight));
+ }
+
+ if (newWidth == m_Width && newHeight == m_Height)
+ {
+ // nothing to do
+ return;
+ }
+
+ std::vector<uint8_t> newData;
+ newData.resize(newWidth * newHeight * GetNumChannels() * GetSingleElementSizeInBytes());
+
+ // boost::numeric_cast<>() is used for user-provided data (protecting about overflows).
+ // static_cast<> ok for internal data (assumes that, when internal data was originally provided by a user,
+ // a boost::numeric_cast<>() handled the conversion).
+ const int nW = boost::numeric_cast<int>(newWidth);
+ const int nH = boost::numeric_cast<int>(newHeight);
+
+ const int w = static_cast<int>(GetWidth());
+ const int h = static_cast<int>(GetHeight());
+ const int numChannels = static_cast<int>(GetNumChannels());
+
+ const int res = stbir_resize_uint8(m_Data.data(), w, h, 0, newData.data(), nW, nH, 0, numChannels);
+ if (res == 0)
+ {
+ throw InferenceTestImageResizeFailed("The resizing operation failed");
+ }
+
+ m_Data.swap(newData);
+ m_Width = newWidth;
+ m_Height = newHeight;
+}
+
+void InferenceTestImage::Write(WriteFormat format, const char* filePath) const
+{
+ const int w = static_cast<int>(GetWidth());
+ const int h = static_cast<int>(GetHeight());
+ const int numChannels = static_cast<int>(GetNumChannels());
+ int res = 0;
+
+ switch (format)
+ {
+ case WriteFormat::Png:
+ {
+ res = stbi_write_png(filePath, w, h, numChannels, m_Data.data(), 0);
+ break;
+ }
+ case WriteFormat::Bmp:
+ {
+ res = stbi_write_bmp(filePath, w, h, numChannels, m_Data.data());
+ break;
+ }
+ case WriteFormat::Tga:
+ {
+ res = stbi_write_tga(filePath, w, h, numChannels, m_Data.data());
+ break;
+ }
+ default:
+ throw InferenceTestImageWriteFailed(boost::str(boost::format("Unknown format %1%")
+ % static_cast<int>(format)));
+ }
+
+ if (res == 0)
+ {
+ throw InferenceTestImageWriteFailed(boost::str(boost::format("An error occurred when writing to file %1%")
+ % filePath));
+ }
+}
+
+template <typename TProcessValueCallable>
+std::vector<float> GetImageDataInArmNnLayoutAsFloats(ImageChannelLayout channelLayout,
+ const InferenceTestImage& image,
+ TProcessValueCallable processValue)
+{
+ const unsigned int h = image.GetHeight();
+ const unsigned int w = image.GetWidth();
+
+ std::vector<float> imageData;
+ imageData.resize(h * w * 3);
+
+ for (unsigned int j = 0; j < h; ++j)
+ {
+ for (unsigned int i = 0; i < w; ++i)
+ {
+ uint8_t r, g, b;
+ std::tie(r, g, b) = image.GetPixelAs3Channels(i, j);
+
+ // ArmNN order: C, H, W
+ const unsigned int rDstIndex = GetImageChannelIndex(channelLayout, ImageChannel::R) * h * w + j * w + i;
+ const unsigned int gDstIndex = GetImageChannelIndex(channelLayout, ImageChannel::G) * h * w + j * w + i;
+ const unsigned int bDstIndex = GetImageChannelIndex(channelLayout, ImageChannel::B) * h * w + j * w + i;
+
+ imageData[rDstIndex] = processValue(ImageChannel::R, float(r));
+ imageData[gDstIndex] = processValue(ImageChannel::G, float(g));
+ imageData[bDstIndex] = processValue(ImageChannel::B, float(b));
+ }
+ }
+
+ return imageData;
+}
+
+std::vector<float> GetImageDataInArmNnLayoutAsNormalizedFloats(ImageChannelLayout layout,
+ const InferenceTestImage& image)
+{
+ return GetImageDataInArmNnLayoutAsFloats(layout, image,
+ [](ImageChannel channel, float value)
+ {
+ boost::ignore_unused(channel);
+ return value / 255.f;
+ });
+}
+
+std::vector<float> GetImageDataInArmNnLayoutAsFloatsSubtractingMean(ImageChannelLayout layout,
+ const InferenceTestImage& image,
+ const std::array<float, 3>& mean)
+{
+ return GetImageDataInArmNnLayoutAsFloats(layout, image,
+ [layout, &mean](ImageChannel channel, float value)
+ {
+ const unsigned int channelIndex = GetImageChannelIndex(layout, channel);
+ return value - mean[channelIndex];
+ });
+}