xref: /aosp_15_r20/external/armnn/tests/InferenceTestImage.cpp (revision 89c4ff92f2867872bb9e2354d150bf0c8c502810)
1*89c4ff92SAndroid Build Coastguard Worker //
2*89c4ff92SAndroid Build Coastguard Worker // Copyright © 2017 Arm Ltd. All rights reserved.
3*89c4ff92SAndroid Build Coastguard Worker // SPDX-License-Identifier: MIT
4*89c4ff92SAndroid Build Coastguard Worker //
5*89c4ff92SAndroid Build Coastguard Worker #include "InferenceTestImage.hpp"
6*89c4ff92SAndroid Build Coastguard Worker 
7*89c4ff92SAndroid Build Coastguard Worker #include <armnn/utility/Assert.hpp>
8*89c4ff92SAndroid Build Coastguard Worker #include <armnn/utility/IgnoreUnused.hpp>
9*89c4ff92SAndroid Build Coastguard Worker #include <armnn/utility/NumericCast.hpp>
10*89c4ff92SAndroid Build Coastguard Worker 
11*89c4ff92SAndroid Build Coastguard Worker #include <fmt/format.h>
12*89c4ff92SAndroid Build Coastguard Worker 
13*89c4ff92SAndroid Build Coastguard Worker #include <array>
14*89c4ff92SAndroid Build Coastguard Worker 
15*89c4ff92SAndroid Build Coastguard Worker #define STB_IMAGE_IMPLEMENTATION
16*89c4ff92SAndroid Build Coastguard Worker #include <stb/stb_image.h>
17*89c4ff92SAndroid Build Coastguard Worker 
18*89c4ff92SAndroid Build Coastguard Worker #define STB_IMAGE_RESIZE_IMPLEMENTATION
19*89c4ff92SAndroid Build Coastguard Worker #include <stb/stb_image_resize.h>
20*89c4ff92SAndroid Build Coastguard Worker 
21*89c4ff92SAndroid Build Coastguard Worker #define STB_IMAGE_WRITE_IMPLEMENTATION
22*89c4ff92SAndroid Build Coastguard Worker #include <stb/stb_image_write.h>
23*89c4ff92SAndroid Build Coastguard Worker 
24*89c4ff92SAndroid Build Coastguard Worker namespace
25*89c4ff92SAndroid Build Coastguard Worker {
26*89c4ff92SAndroid Build Coastguard Worker 
GetImageChannelIndex(ImageChannelLayout channelLayout,ImageChannel channel)27*89c4ff92SAndroid Build Coastguard Worker unsigned int GetImageChannelIndex(ImageChannelLayout channelLayout, ImageChannel channel)
28*89c4ff92SAndroid Build Coastguard Worker {
29*89c4ff92SAndroid Build Coastguard Worker     switch (channelLayout)
30*89c4ff92SAndroid Build Coastguard Worker     {
31*89c4ff92SAndroid Build Coastguard Worker     case ImageChannelLayout::Rgb:
32*89c4ff92SAndroid Build Coastguard Worker         return static_cast<unsigned int>(channel);
33*89c4ff92SAndroid Build Coastguard Worker     case ImageChannelLayout::Bgr:
34*89c4ff92SAndroid Build Coastguard Worker         return 2u - static_cast<unsigned int>(channel);
35*89c4ff92SAndroid Build Coastguard Worker     default:
36*89c4ff92SAndroid Build Coastguard Worker         throw UnknownImageChannelLayout(fmt::format("Unknown layout {}", static_cast<int>(channelLayout)));
37*89c4ff92SAndroid Build Coastguard Worker     }
38*89c4ff92SAndroid Build Coastguard Worker }
39*89c4ff92SAndroid Build Coastguard Worker 
Lerp(float a,float b,float w)40*89c4ff92SAndroid Build Coastguard Worker inline float Lerp(float a, float b, float w)
41*89c4ff92SAndroid Build Coastguard Worker {
42*89c4ff92SAndroid Build Coastguard Worker     return w * b + (1.f - w) * a;
43*89c4ff92SAndroid Build Coastguard Worker }
44*89c4ff92SAndroid Build Coastguard Worker 
PutData(std::vector<float> & data,const unsigned int width,const unsigned int x,const unsigned int y,const unsigned int c,float value)45*89c4ff92SAndroid Build Coastguard Worker inline void PutData(std::vector<float> & data,
46*89c4ff92SAndroid Build Coastguard Worker                     const unsigned int width,
47*89c4ff92SAndroid Build Coastguard Worker                     const unsigned int x,
48*89c4ff92SAndroid Build Coastguard Worker                     const unsigned int y,
49*89c4ff92SAndroid Build Coastguard Worker                     const unsigned int c,
50*89c4ff92SAndroid Build Coastguard Worker                     float value)
51*89c4ff92SAndroid Build Coastguard Worker {
52*89c4ff92SAndroid Build Coastguard Worker     data[(3*((y*width)+x)) + c] = value;
53*89c4ff92SAndroid Build Coastguard Worker }
54*89c4ff92SAndroid Build Coastguard Worker 
ResizeBilinearAndNormalize(const InferenceTestImage & image,const unsigned int outputWidth,const unsigned int outputHeight,const float scale,const std::array<float,3> & mean,const std::array<float,3> & stddev)55*89c4ff92SAndroid Build Coastguard Worker std::vector<float> ResizeBilinearAndNormalize(const InferenceTestImage & image,
56*89c4ff92SAndroid Build Coastguard Worker                                               const unsigned int outputWidth,
57*89c4ff92SAndroid Build Coastguard Worker                                               const unsigned int outputHeight,
58*89c4ff92SAndroid Build Coastguard Worker                                               const float scale,
59*89c4ff92SAndroid Build Coastguard Worker                                               const std::array<float, 3>& mean,
60*89c4ff92SAndroid Build Coastguard Worker                                               const std::array<float, 3>& stddev)
61*89c4ff92SAndroid Build Coastguard Worker {
62*89c4ff92SAndroid Build Coastguard Worker     std::vector<float> out;
63*89c4ff92SAndroid Build Coastguard Worker     out.resize(outputWidth * outputHeight * 3);
64*89c4ff92SAndroid Build Coastguard Worker 
65*89c4ff92SAndroid Build Coastguard Worker     // We follow the definition of TensorFlow and AndroidNN: the top-left corner of a texel in the output
66*89c4ff92SAndroid Build Coastguard Worker     // image is projected into the input image to figure out the interpolants and weights. Note that this
67*89c4ff92SAndroid Build Coastguard Worker     // will yield different results than if projecting the centre of output texels.
68*89c4ff92SAndroid Build Coastguard Worker 
69*89c4ff92SAndroid Build Coastguard Worker     const unsigned int inputWidth = image.GetWidth();
70*89c4ff92SAndroid Build Coastguard Worker     const unsigned int inputHeight = image.GetHeight();
71*89c4ff92SAndroid Build Coastguard Worker 
72*89c4ff92SAndroid Build Coastguard Worker     // How much to scale pixel coordinates in the output image to get the corresponding pixel coordinates
73*89c4ff92SAndroid Build Coastguard Worker     // in the input image.
74*89c4ff92SAndroid Build Coastguard Worker     const float scaleY = armnn::numeric_cast<float>(inputHeight) / armnn::numeric_cast<float>(outputHeight);
75*89c4ff92SAndroid Build Coastguard Worker     const float scaleX = armnn::numeric_cast<float>(inputWidth) / armnn::numeric_cast<float>(outputWidth);
76*89c4ff92SAndroid Build Coastguard Worker 
77*89c4ff92SAndroid Build Coastguard Worker     uint8_t rgb_x0y0[3];
78*89c4ff92SAndroid Build Coastguard Worker     uint8_t rgb_x1y0[3];
79*89c4ff92SAndroid Build Coastguard Worker     uint8_t rgb_x0y1[3];
80*89c4ff92SAndroid Build Coastguard Worker     uint8_t rgb_x1y1[3];
81*89c4ff92SAndroid Build Coastguard Worker 
82*89c4ff92SAndroid Build Coastguard Worker     for (unsigned int y = 0; y < outputHeight; ++y)
83*89c4ff92SAndroid Build Coastguard Worker     {
84*89c4ff92SAndroid Build Coastguard Worker         // Corresponding real-valued height coordinate in input image.
85*89c4ff92SAndroid Build Coastguard Worker         const float iy = armnn::numeric_cast<float>(y) * scaleY;
86*89c4ff92SAndroid Build Coastguard Worker 
87*89c4ff92SAndroid Build Coastguard Worker         // Discrete height coordinate of top-left texel (in the 2x2 texel area used for interpolation).
88*89c4ff92SAndroid Build Coastguard Worker         const float fiy = floorf(iy);
89*89c4ff92SAndroid Build Coastguard Worker         const unsigned int y0 = armnn::numeric_cast<unsigned int>(fiy);
90*89c4ff92SAndroid Build Coastguard Worker 
91*89c4ff92SAndroid Build Coastguard Worker         // Interpolation weight (range [0,1])
92*89c4ff92SAndroid Build Coastguard Worker         const float yw = iy - fiy;
93*89c4ff92SAndroid Build Coastguard Worker 
94*89c4ff92SAndroid Build Coastguard Worker         for (unsigned int x = 0; x < outputWidth; ++x)
95*89c4ff92SAndroid Build Coastguard Worker         {
96*89c4ff92SAndroid Build Coastguard Worker             // Real-valued and discrete width coordinates in input image.
97*89c4ff92SAndroid Build Coastguard Worker             const float ix = armnn::numeric_cast<float>(x) * scaleX;
98*89c4ff92SAndroid Build Coastguard Worker             const float fix = floorf(ix);
99*89c4ff92SAndroid Build Coastguard Worker             const unsigned int x0 = armnn::numeric_cast<unsigned int>(fix);
100*89c4ff92SAndroid Build Coastguard Worker 
101*89c4ff92SAndroid Build Coastguard Worker             // Interpolation weight (range [0,1]).
102*89c4ff92SAndroid Build Coastguard Worker             const float xw = ix - fix;
103*89c4ff92SAndroid Build Coastguard Worker 
104*89c4ff92SAndroid Build Coastguard Worker             // Discrete width/height coordinates of texels below and to the right of (x0, y0).
105*89c4ff92SAndroid Build Coastguard Worker             const unsigned int x1 = std::min(x0 + 1, inputWidth - 1u);
106*89c4ff92SAndroid Build Coastguard Worker             const unsigned int y1 = std::min(y0 + 1, inputHeight - 1u);
107*89c4ff92SAndroid Build Coastguard Worker 
108*89c4ff92SAndroid Build Coastguard Worker             std::tie(rgb_x0y0[0], rgb_x0y0[1], rgb_x0y0[2]) = image.GetPixelAs3Channels(x0, y0);
109*89c4ff92SAndroid Build Coastguard Worker             std::tie(rgb_x1y0[0], rgb_x1y0[1], rgb_x1y0[2]) = image.GetPixelAs3Channels(x1, y0);
110*89c4ff92SAndroid Build Coastguard Worker             std::tie(rgb_x0y1[0], rgb_x0y1[1], rgb_x0y1[2]) = image.GetPixelAs3Channels(x0, y1);
111*89c4ff92SAndroid Build Coastguard Worker             std::tie(rgb_x1y1[0], rgb_x1y1[1], rgb_x1y1[2]) = image.GetPixelAs3Channels(x1, y1);
112*89c4ff92SAndroid Build Coastguard Worker 
113*89c4ff92SAndroid Build Coastguard Worker             for (unsigned c=0; c<3; ++c)
114*89c4ff92SAndroid Build Coastguard Worker             {
115*89c4ff92SAndroid Build Coastguard Worker                 const float ly0 = Lerp(float(rgb_x0y0[c]), float(rgb_x1y0[c]), xw);
116*89c4ff92SAndroid Build Coastguard Worker                 const float ly1 = Lerp(float(rgb_x0y1[c]), float(rgb_x1y1[c]), xw);
117*89c4ff92SAndroid Build Coastguard Worker                 const float l = Lerp(ly0, ly1, yw);
118*89c4ff92SAndroid Build Coastguard Worker                 PutData(out, outputWidth, x, y, c, ((l / scale) - mean[c]) / stddev[c]);
119*89c4ff92SAndroid Build Coastguard Worker             }
120*89c4ff92SAndroid Build Coastguard Worker         }
121*89c4ff92SAndroid Build Coastguard Worker     }
122*89c4ff92SAndroid Build Coastguard Worker     return out;
123*89c4ff92SAndroid Build Coastguard Worker }
124*89c4ff92SAndroid Build Coastguard Worker 
125*89c4ff92SAndroid Build Coastguard Worker } // namespace
126*89c4ff92SAndroid Build Coastguard Worker 
InferenceTestImage(char const * filePath)127*89c4ff92SAndroid Build Coastguard Worker InferenceTestImage::InferenceTestImage(char const* filePath)
128*89c4ff92SAndroid Build Coastguard Worker  : m_Width(0u)
129*89c4ff92SAndroid Build Coastguard Worker  , m_Height(0u)
130*89c4ff92SAndroid Build Coastguard Worker  , m_NumChannels(0u)
131*89c4ff92SAndroid Build Coastguard Worker {
132*89c4ff92SAndroid Build Coastguard Worker     int width;
133*89c4ff92SAndroid Build Coastguard Worker     int height;
134*89c4ff92SAndroid Build Coastguard Worker     int channels;
135*89c4ff92SAndroid Build Coastguard Worker 
136*89c4ff92SAndroid Build Coastguard Worker     using StbImageDataPtr = std::unique_ptr<unsigned char, decltype(&stbi_image_free)>;
137*89c4ff92SAndroid Build Coastguard Worker     StbImageDataPtr stbData(stbi_load(filePath, &width, &height, &channels, 0), &stbi_image_free);
138*89c4ff92SAndroid Build Coastguard Worker 
139*89c4ff92SAndroid Build Coastguard Worker     if (stbData == nullptr)
140*89c4ff92SAndroid Build Coastguard Worker     {
141*89c4ff92SAndroid Build Coastguard Worker         throw InferenceTestImageLoadFailed(fmt::format("Could not load the image at {}", filePath));
142*89c4ff92SAndroid Build Coastguard Worker     }
143*89c4ff92SAndroid Build Coastguard Worker 
144*89c4ff92SAndroid Build Coastguard Worker     if (width == 0 || height == 0)
145*89c4ff92SAndroid Build Coastguard Worker     {
146*89c4ff92SAndroid Build Coastguard Worker         throw InferenceTestImageLoadFailed(fmt::format("Could not load empty image at {}", filePath));
147*89c4ff92SAndroid Build Coastguard Worker     }
148*89c4ff92SAndroid Build Coastguard Worker 
149*89c4ff92SAndroid Build Coastguard Worker     m_Width = armnn::numeric_cast<unsigned int>(width);
150*89c4ff92SAndroid Build Coastguard Worker     m_Height = armnn::numeric_cast<unsigned int>(height);
151*89c4ff92SAndroid Build Coastguard Worker     m_NumChannels = armnn::numeric_cast<unsigned int>(channels);
152*89c4ff92SAndroid Build Coastguard Worker 
153*89c4ff92SAndroid Build Coastguard Worker     const unsigned int sizeInBytes = GetSizeInBytes();
154*89c4ff92SAndroid Build Coastguard Worker     m_Data.resize(sizeInBytes);
155*89c4ff92SAndroid Build Coastguard Worker     memcpy(m_Data.data(), stbData.get(), sizeInBytes);
156*89c4ff92SAndroid Build Coastguard Worker }
157*89c4ff92SAndroid Build Coastguard Worker 
GetPixelAs3Channels(unsigned int x,unsigned int y) const158*89c4ff92SAndroid Build Coastguard Worker std::tuple<uint8_t, uint8_t, uint8_t> InferenceTestImage::GetPixelAs3Channels(unsigned int x, unsigned int y) const
159*89c4ff92SAndroid Build Coastguard Worker {
160*89c4ff92SAndroid Build Coastguard Worker     if (x >= m_Width || y >= m_Height)
161*89c4ff92SAndroid Build Coastguard Worker     {
162*89c4ff92SAndroid Build Coastguard Worker         throw InferenceTestImageOutOfBoundsAccess(fmt::format("Attempted out of bounds image access. "
163*89c4ff92SAndroid Build Coastguard Worker             "Requested ({0}, {1}). Maximum valid coordinates ({2}, {3}).", x, y, (m_Width - 1), (m_Height - 1)));
164*89c4ff92SAndroid Build Coastguard Worker     }
165*89c4ff92SAndroid Build Coastguard Worker 
166*89c4ff92SAndroid Build Coastguard Worker     const unsigned int pixelOffset = x * GetNumChannels() + y * GetWidth() * GetNumChannels();
167*89c4ff92SAndroid Build Coastguard Worker     const uint8_t* const pixelData = m_Data.data() + pixelOffset;
168*89c4ff92SAndroid Build Coastguard Worker     ARMNN_ASSERT(pixelData <= (m_Data.data() + GetSizeInBytes()));
169*89c4ff92SAndroid Build Coastguard Worker 
170*89c4ff92SAndroid Build Coastguard Worker     std::array<uint8_t, 3> outPixelData;
171*89c4ff92SAndroid Build Coastguard Worker     outPixelData.fill(0);
172*89c4ff92SAndroid Build Coastguard Worker 
173*89c4ff92SAndroid Build Coastguard Worker     const unsigned int maxChannelsInPixel = std::min(GetNumChannels(), static_cast<unsigned int>(outPixelData.size()));
174*89c4ff92SAndroid Build Coastguard Worker     for (unsigned int c = 0; c < maxChannelsInPixel; ++c)
175*89c4ff92SAndroid Build Coastguard Worker     {
176*89c4ff92SAndroid Build Coastguard Worker         outPixelData[c] = pixelData[c];
177*89c4ff92SAndroid Build Coastguard Worker     }
178*89c4ff92SAndroid Build Coastguard Worker 
179*89c4ff92SAndroid Build Coastguard Worker     return std::make_tuple(outPixelData[0], outPixelData[1], outPixelData[2]);
180*89c4ff92SAndroid Build Coastguard Worker }
181*89c4ff92SAndroid Build Coastguard Worker 
182*89c4ff92SAndroid Build Coastguard Worker 
StbResize(InferenceTestImage & im,const unsigned int newWidth,const unsigned int newHeight)183*89c4ff92SAndroid Build Coastguard Worker void InferenceTestImage::StbResize(InferenceTestImage& im, const unsigned int newWidth, const unsigned int newHeight)
184*89c4ff92SAndroid Build Coastguard Worker {
185*89c4ff92SAndroid Build Coastguard Worker     std::vector<uint8_t> newData;
186*89c4ff92SAndroid Build Coastguard Worker     newData.resize(newWidth * newHeight * im.GetNumChannels() * im.GetSingleElementSizeInBytes());
187*89c4ff92SAndroid Build Coastguard Worker 
188*89c4ff92SAndroid Build Coastguard Worker     // armnn::numeric_cast<>() is used for user-provided data (protecting about overflows).
189*89c4ff92SAndroid Build Coastguard Worker     // static_cast<> is ok for internal data (assumes that, when internal data was originally provided by a user,
190*89c4ff92SAndroid Build Coastguard Worker     // a armnn::numeric_cast<>() handled the conversion).
191*89c4ff92SAndroid Build Coastguard Worker     const int nW = armnn::numeric_cast<int>(newWidth);
192*89c4ff92SAndroid Build Coastguard Worker     const int nH = armnn::numeric_cast<int>(newHeight);
193*89c4ff92SAndroid Build Coastguard Worker 
194*89c4ff92SAndroid Build Coastguard Worker     const int w = static_cast<int>(im.GetWidth());
195*89c4ff92SAndroid Build Coastguard Worker     const int h = static_cast<int>(im.GetHeight());
196*89c4ff92SAndroid Build Coastguard Worker     const int numChannels = static_cast<int>(im.GetNumChannels());
197*89c4ff92SAndroid Build Coastguard Worker 
198*89c4ff92SAndroid Build Coastguard Worker     const int res = stbir_resize_uint8(im.m_Data.data(), w, h, 0, newData.data(), nW, nH, 0, numChannels);
199*89c4ff92SAndroid Build Coastguard Worker     if (res == 0)
200*89c4ff92SAndroid Build Coastguard Worker     {
201*89c4ff92SAndroid Build Coastguard Worker         throw InferenceTestImageResizeFailed("The resizing operation failed");
202*89c4ff92SAndroid Build Coastguard Worker     }
203*89c4ff92SAndroid Build Coastguard Worker 
204*89c4ff92SAndroid Build Coastguard Worker     im.m_Data.swap(newData);
205*89c4ff92SAndroid Build Coastguard Worker     im.m_Width = newWidth;
206*89c4ff92SAndroid Build Coastguard Worker     im.m_Height = newHeight;
207*89c4ff92SAndroid Build Coastguard Worker }
208*89c4ff92SAndroid Build Coastguard Worker 
Resize(unsigned int newWidth,unsigned int newHeight,const armnn::CheckLocation & location,const ResizingMethods meth,const std::array<float,3> & mean,const std::array<float,3> & stddev,const float scale)209*89c4ff92SAndroid Build Coastguard Worker std::vector<float> InferenceTestImage::Resize(unsigned int newWidth,
210*89c4ff92SAndroid Build Coastguard Worker                                               unsigned int newHeight,
211*89c4ff92SAndroid Build Coastguard Worker                                               const armnn::CheckLocation& location,
212*89c4ff92SAndroid Build Coastguard Worker                                               const ResizingMethods meth,
213*89c4ff92SAndroid Build Coastguard Worker                                               const std::array<float, 3>& mean,
214*89c4ff92SAndroid Build Coastguard Worker                                               const std::array<float, 3>& stddev,
215*89c4ff92SAndroid Build Coastguard Worker                                               const float scale)
216*89c4ff92SAndroid Build Coastguard Worker {
217*89c4ff92SAndroid Build Coastguard Worker     std::vector<float> out;
218*89c4ff92SAndroid Build Coastguard Worker     if (newWidth == 0 || newHeight == 0)
219*89c4ff92SAndroid Build Coastguard Worker     {
220*89c4ff92SAndroid Build Coastguard Worker         throw InferenceTestImageResizeFailed(fmt::format("None of the dimensions passed to a resize "
221*89c4ff92SAndroid Build Coastguard Worker             "operation can be zero. Requested width: {0}. Requested height: {1}.", newWidth, newHeight));
222*89c4ff92SAndroid Build Coastguard Worker     }
223*89c4ff92SAndroid Build Coastguard Worker 
224*89c4ff92SAndroid Build Coastguard Worker     switch (meth) {
225*89c4ff92SAndroid Build Coastguard Worker         case ResizingMethods::STB:
226*89c4ff92SAndroid Build Coastguard Worker         {
227*89c4ff92SAndroid Build Coastguard Worker             StbResize(*this, newWidth, newHeight);
228*89c4ff92SAndroid Build Coastguard Worker             break;
229*89c4ff92SAndroid Build Coastguard Worker         }
230*89c4ff92SAndroid Build Coastguard Worker         case ResizingMethods::BilinearAndNormalized:
231*89c4ff92SAndroid Build Coastguard Worker         {
232*89c4ff92SAndroid Build Coastguard Worker             out = ResizeBilinearAndNormalize(*this, newWidth, newHeight, scale, mean, stddev);
233*89c4ff92SAndroid Build Coastguard Worker             break;
234*89c4ff92SAndroid Build Coastguard Worker         }
235*89c4ff92SAndroid Build Coastguard Worker         default:
236*89c4ff92SAndroid Build Coastguard Worker             throw InferenceTestImageResizeFailed(fmt::format("Unknown resizing method asked ArmNN only"
237*89c4ff92SAndroid Build Coastguard Worker                                                              " supports {STB, BilinearAndNormalized} {}",
238*89c4ff92SAndroid Build Coastguard Worker                                                              location.AsString()));
239*89c4ff92SAndroid Build Coastguard Worker     }
240*89c4ff92SAndroid Build Coastguard Worker     return out;
241*89c4ff92SAndroid Build Coastguard Worker }
242*89c4ff92SAndroid Build Coastguard Worker 
Write(WriteFormat format,const char * filePath) const243*89c4ff92SAndroid Build Coastguard Worker void InferenceTestImage::Write(WriteFormat format, const char* filePath) const
244*89c4ff92SAndroid Build Coastguard Worker {
245*89c4ff92SAndroid Build Coastguard Worker     const int w = static_cast<int>(GetWidth());
246*89c4ff92SAndroid Build Coastguard Worker     const int h = static_cast<int>(GetHeight());
247*89c4ff92SAndroid Build Coastguard Worker     const int numChannels = static_cast<int>(GetNumChannels());
248*89c4ff92SAndroid Build Coastguard Worker     int res = 0;
249*89c4ff92SAndroid Build Coastguard Worker 
250*89c4ff92SAndroid Build Coastguard Worker     switch (format)
251*89c4ff92SAndroid Build Coastguard Worker     {
252*89c4ff92SAndroid Build Coastguard Worker     case WriteFormat::Png:
253*89c4ff92SAndroid Build Coastguard Worker         {
254*89c4ff92SAndroid Build Coastguard Worker             res = stbi_write_png(filePath, w, h, numChannels, m_Data.data(), 0);
255*89c4ff92SAndroid Build Coastguard Worker             break;
256*89c4ff92SAndroid Build Coastguard Worker         }
257*89c4ff92SAndroid Build Coastguard Worker     case WriteFormat::Bmp:
258*89c4ff92SAndroid Build Coastguard Worker         {
259*89c4ff92SAndroid Build Coastguard Worker             res = stbi_write_bmp(filePath, w, h, numChannels, m_Data.data());
260*89c4ff92SAndroid Build Coastguard Worker             break;
261*89c4ff92SAndroid Build Coastguard Worker         }
262*89c4ff92SAndroid Build Coastguard Worker     case WriteFormat::Tga:
263*89c4ff92SAndroid Build Coastguard Worker         {
264*89c4ff92SAndroid Build Coastguard Worker             res = stbi_write_tga(filePath, w, h, numChannels, m_Data.data());
265*89c4ff92SAndroid Build Coastguard Worker             break;
266*89c4ff92SAndroid Build Coastguard Worker         }
267*89c4ff92SAndroid Build Coastguard Worker     default:
268*89c4ff92SAndroid Build Coastguard Worker         throw InferenceTestImageWriteFailed(fmt::format("Unknown format {}", static_cast<int>(format)));
269*89c4ff92SAndroid Build Coastguard Worker     }
270*89c4ff92SAndroid Build Coastguard Worker 
271*89c4ff92SAndroid Build Coastguard Worker     if (res == 0)
272*89c4ff92SAndroid Build Coastguard Worker     {
273*89c4ff92SAndroid Build Coastguard Worker         throw InferenceTestImageWriteFailed(fmt::format("An error occurred when writing to file {}",
274*89c4ff92SAndroid Build Coastguard Worker                                                         filePath));
275*89c4ff92SAndroid Build Coastguard Worker     }
276*89c4ff92SAndroid Build Coastguard Worker }
277*89c4ff92SAndroid Build Coastguard Worker 
278*89c4ff92SAndroid Build Coastguard Worker template <typename TProcessValueCallable>
GetImageDataInArmNnLayoutAsFloats(ImageChannelLayout channelLayout,const InferenceTestImage & image,TProcessValueCallable processValue)279*89c4ff92SAndroid Build Coastguard Worker std::vector<float> GetImageDataInArmNnLayoutAsFloats(ImageChannelLayout channelLayout,
280*89c4ff92SAndroid Build Coastguard Worker     const InferenceTestImage& image,
281*89c4ff92SAndroid Build Coastguard Worker     TProcessValueCallable processValue)
282*89c4ff92SAndroid Build Coastguard Worker {
283*89c4ff92SAndroid Build Coastguard Worker     const unsigned int h = image.GetHeight();
284*89c4ff92SAndroid Build Coastguard Worker     const unsigned int w = image.GetWidth();
285*89c4ff92SAndroid Build Coastguard Worker 
286*89c4ff92SAndroid Build Coastguard Worker     std::vector<float> imageData;
287*89c4ff92SAndroid Build Coastguard Worker     imageData.resize(h * w * 3);
288*89c4ff92SAndroid Build Coastguard Worker 
289*89c4ff92SAndroid Build Coastguard Worker     for (unsigned int j = 0; j < h; ++j)
290*89c4ff92SAndroid Build Coastguard Worker     {
291*89c4ff92SAndroid Build Coastguard Worker         for (unsigned int i = 0; i < w; ++i)
292*89c4ff92SAndroid Build Coastguard Worker         {
293*89c4ff92SAndroid Build Coastguard Worker             uint8_t r, g, b;
294*89c4ff92SAndroid Build Coastguard Worker             std::tie(r, g, b) = image.GetPixelAs3Channels(i, j);
295*89c4ff92SAndroid Build Coastguard Worker 
296*89c4ff92SAndroid Build Coastguard Worker             // ArmNN order: C, H, W
297*89c4ff92SAndroid Build Coastguard Worker             const unsigned int rDstIndex = GetImageChannelIndex(channelLayout, ImageChannel::R) * h * w + j * w + i;
298*89c4ff92SAndroid Build Coastguard Worker             const unsigned int gDstIndex = GetImageChannelIndex(channelLayout, ImageChannel::G) * h * w + j * w + i;
299*89c4ff92SAndroid Build Coastguard Worker             const unsigned int bDstIndex = GetImageChannelIndex(channelLayout, ImageChannel::B) * h * w + j * w + i;
300*89c4ff92SAndroid Build Coastguard Worker 
301*89c4ff92SAndroid Build Coastguard Worker             imageData[rDstIndex] = processValue(ImageChannel::R, float(r));
302*89c4ff92SAndroid Build Coastguard Worker             imageData[gDstIndex] = processValue(ImageChannel::G, float(g));
303*89c4ff92SAndroid Build Coastguard Worker             imageData[bDstIndex] = processValue(ImageChannel::B, float(b));
304*89c4ff92SAndroid Build Coastguard Worker         }
305*89c4ff92SAndroid Build Coastguard Worker     }
306*89c4ff92SAndroid Build Coastguard Worker 
307*89c4ff92SAndroid Build Coastguard Worker     return imageData;
308*89c4ff92SAndroid Build Coastguard Worker }
309*89c4ff92SAndroid Build Coastguard Worker 
GetImageDataInArmNnLayoutAsNormalizedFloats(ImageChannelLayout layout,const InferenceTestImage & image)310*89c4ff92SAndroid Build Coastguard Worker std::vector<float> GetImageDataInArmNnLayoutAsNormalizedFloats(ImageChannelLayout layout,
311*89c4ff92SAndroid Build Coastguard Worker     const InferenceTestImage& image)
312*89c4ff92SAndroid Build Coastguard Worker {
313*89c4ff92SAndroid Build Coastguard Worker     return GetImageDataInArmNnLayoutAsFloats(layout, image,
314*89c4ff92SAndroid Build Coastguard Worker         [](ImageChannel channel, float value)
315*89c4ff92SAndroid Build Coastguard Worker         {
316*89c4ff92SAndroid Build Coastguard Worker             armnn::IgnoreUnused(channel);
317*89c4ff92SAndroid Build Coastguard Worker             return value / 255.f;
318*89c4ff92SAndroid Build Coastguard Worker         });
319*89c4ff92SAndroid Build Coastguard Worker }
320*89c4ff92SAndroid Build Coastguard Worker 
GetImageDataInArmNnLayoutAsFloatsSubtractingMean(ImageChannelLayout layout,const InferenceTestImage & image,const std::array<float,3> & mean)321*89c4ff92SAndroid Build Coastguard Worker std::vector<float> GetImageDataInArmNnLayoutAsFloatsSubtractingMean(ImageChannelLayout layout,
322*89c4ff92SAndroid Build Coastguard Worker     const InferenceTestImage& image,
323*89c4ff92SAndroid Build Coastguard Worker     const std::array<float, 3>& mean)
324*89c4ff92SAndroid Build Coastguard Worker {
325*89c4ff92SAndroid Build Coastguard Worker     return GetImageDataInArmNnLayoutAsFloats(layout, image,
326*89c4ff92SAndroid Build Coastguard Worker         [layout, &mean](ImageChannel channel, float value)
327*89c4ff92SAndroid Build Coastguard Worker         {
328*89c4ff92SAndroid Build Coastguard Worker             const unsigned int channelIndex = GetImageChannelIndex(layout, channel);
329*89c4ff92SAndroid Build Coastguard Worker             return value - mean[channelIndex];
330*89c4ff92SAndroid Build Coastguard Worker         });
331*89c4ff92SAndroid Build Coastguard Worker }
332*89c4ff92SAndroid Build Coastguard Worker 
GetImageDataAsNormalizedFloats(ImageChannelLayout layout,const InferenceTestImage & image)333*89c4ff92SAndroid Build Coastguard Worker std::vector<float> GetImageDataAsNormalizedFloats(ImageChannelLayout layout,
334*89c4ff92SAndroid Build Coastguard Worker                                                   const InferenceTestImage& image)
335*89c4ff92SAndroid Build Coastguard Worker {
336*89c4ff92SAndroid Build Coastguard Worker     std::vector<float> imageData;
337*89c4ff92SAndroid Build Coastguard Worker     const unsigned int h = image.GetHeight();
338*89c4ff92SAndroid Build Coastguard Worker     const unsigned int w = image.GetWidth();
339*89c4ff92SAndroid Build Coastguard Worker 
340*89c4ff92SAndroid Build Coastguard Worker     const unsigned int rDstIndex = GetImageChannelIndex(layout, ImageChannel::R);
341*89c4ff92SAndroid Build Coastguard Worker     const unsigned int gDstIndex = GetImageChannelIndex(layout, ImageChannel::G);
342*89c4ff92SAndroid Build Coastguard Worker     const unsigned int bDstIndex = GetImageChannelIndex(layout, ImageChannel::B);
343*89c4ff92SAndroid Build Coastguard Worker 
344*89c4ff92SAndroid Build Coastguard Worker     imageData.resize(h * w * 3);
345*89c4ff92SAndroid Build Coastguard Worker     unsigned int offset = 0;
346*89c4ff92SAndroid Build Coastguard Worker 
347*89c4ff92SAndroid Build Coastguard Worker     for (unsigned int j = 0; j < h; ++j)
348*89c4ff92SAndroid Build Coastguard Worker     {
349*89c4ff92SAndroid Build Coastguard Worker         for (unsigned int i = 0; i < w; ++i)
350*89c4ff92SAndroid Build Coastguard Worker         {
351*89c4ff92SAndroid Build Coastguard Worker             uint8_t r, g, b;
352*89c4ff92SAndroid Build Coastguard Worker             std::tie(r, g, b) = image.GetPixelAs3Channels(i, j);
353*89c4ff92SAndroid Build Coastguard Worker 
354*89c4ff92SAndroid Build Coastguard Worker             imageData[offset+rDstIndex] = float(r) / 255.0f;
355*89c4ff92SAndroid Build Coastguard Worker             imageData[offset+gDstIndex] = float(g) / 255.0f;
356*89c4ff92SAndroid Build Coastguard Worker             imageData[offset+bDstIndex] = float(b) / 255.0f;
357*89c4ff92SAndroid Build Coastguard Worker             offset += 3;
358*89c4ff92SAndroid Build Coastguard Worker         }
359*89c4ff92SAndroid Build Coastguard Worker     }
360*89c4ff92SAndroid Build Coastguard Worker 
361*89c4ff92SAndroid Build Coastguard Worker     return imageData;
362*89c4ff92SAndroid Build Coastguard Worker }
363