• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 //
2 // Copyright © 2017 Arm Ltd. All rights reserved.
3 // SPDX-License-Identifier: MIT
4 //
5 
6 #include "../InferenceTestImage.hpp"
7 
8 #include <armnn/TypesUtils.hpp>
9 
10 #include <armnnUtils/Permute.hpp>
11 
12 #include <algorithm>
13 #include <fstream>
14 #include <iterator>
15 #include <string>
16 
17 // Parameters used in normalizing images
18 struct NormalizationParameters
19 {
20     float scale{ 1.0 };
21     std::array<float, 3> mean{ { 0.0, 0.0, 0.0 } };
22     std::array<float, 3> stddev{ { 1.0, 1.0, 1.0 } };
23 };
24 
25 enum class SupportedFrontend
26 {
27     Caffe      = 0,
28     TensorFlow = 1,
29     TFLite     = 2,
30 };
31 
32 /** Get normalization parameters.
33  * Note that different flavours of models and different model data types have different normalization methods.
34  * This tool currently only supports Caffe, TF and TFLite models
35  *
36  * @param[in] modelFormat   One of the supported frontends
37  * @param[in] outputType    Output type of the image tensor, also the type of the intended model
38  */
GetNormalizationParameters(const SupportedFrontend & modelFormat,const armnn::DataType & outputType)39 NormalizationParameters GetNormalizationParameters(const SupportedFrontend& modelFormat,
40                                                    const armnn::DataType& outputType)
41 {
42     NormalizationParameters normParams;
43     // Explicitly set default parameters
44     normParams.scale  = 1.0;
45     normParams.mean   = { 0.0, 0.0, 0.0 };
46     normParams.stddev = { 1.0, 1.0, 1.0 };
47     switch (modelFormat)
48     {
49         case SupportedFrontend::Caffe:
50             break;
51         case SupportedFrontend::TensorFlow:
52         case SupportedFrontend::TFLite:
53         default:
54             switch (outputType)
55             {
56                 case armnn::DataType::Float32:
57                     normParams.scale = 127.5;
58                     normParams.mean  = { 1.0, 1.0, 1.0 };
59                     break;
60                 case armnn::DataType::Signed32:
61                     normParams.mean = { 128.0, 128.0, 128.0 };
62                     break;
63                 case armnn::DataType::QAsymmU8:
64                 default:
65                     break;
66             }
67             break;
68     }
69     return normParams;
70 }
71 
72 /** Prepare raw image tensor data by loading the image from imagePath and preprocessing it.
73  *
74  * @param[in] imagePath     Path to the image file
75  * @param[in] newWidth      The new width of the output image tensor
76  * @param[in] newHeight     The new height of the output image tensor
77  * @param[in] normParams    Normalization parameters for the normalization of the image
78  * @param[in] batchSize     Batch size
79  * @param[in] outputLayout  Data layout of the output image tensor
80  */
81 template <typename ElemType>
82 std::vector<ElemType> PrepareImageTensor(const std::string& imagePath,
83                                          unsigned int newWidth,
84                                          unsigned int newHeight,
85                                          const NormalizationParameters& normParams,
86                                          unsigned int batchSize                = 1,
87                                          const armnn::DataLayout& outputLayout = armnn::DataLayout::NHWC);
88 
89 // Prepare float32 image tensor
90 template <>
PrepareImageTensor(const std::string & imagePath,unsigned int newWidth,unsigned int newHeight,const NormalizationParameters & normParams,unsigned int batchSize,const armnn::DataLayout & outputLayout)91 std::vector<float> PrepareImageTensor<float>(const std::string& imagePath,
92                                              unsigned int newWidth,
93                                              unsigned int newHeight,
94                                              const NormalizationParameters& normParams,
95                                              unsigned int batchSize,
96                                              const armnn::DataLayout& outputLayout)
97 {
98     // Generate image tensor
99     std::vector<float> imageData;
100     InferenceTestImage testImage(imagePath.c_str());
101     if (newWidth == 0)
102     {
103         newWidth = testImage.GetWidth();
104     }
105     if (newHeight == 0)
106     {
107         newHeight = testImage.GetHeight();
108     }
109     // Resize the image to new width and height or keep at original dimensions if the new width and height are specified
110     // as 0 Centre/Normalise the image.
111     imageData = testImage.Resize(newWidth, newHeight, CHECK_LOCATION(),
112                                  InferenceTestImage::ResizingMethods::BilinearAndNormalized, normParams.mean,
113                                  normParams.stddev, normParams.scale);
114     if (outputLayout == armnn::DataLayout::NCHW)
115     {
116         // Convert to NCHW format
117         const armnn::PermutationVector NHWCToArmNN = { 0, 2, 3, 1 };
118         armnn::TensorShape dstShape({ batchSize, 3, newHeight, newWidth });
119         std::vector<float> tempImage(imageData.size());
120         armnnUtils::Permute(dstShape, NHWCToArmNN, imageData.data(), tempImage.data(), sizeof(float));
121         imageData.swap(tempImage);
122     }
123     return imageData;
124 }
125 
126 // Prepare int32 image tensor
127 template <>
PrepareImageTensor(const std::string & imagePath,unsigned int newWidth,unsigned int newHeight,const NormalizationParameters & normParams,unsigned int batchSize,const armnn::DataLayout & outputLayout)128 std::vector<int> PrepareImageTensor<int>(const std::string& imagePath,
129                                          unsigned int newWidth,
130                                          unsigned int newHeight,
131                                          const NormalizationParameters& normParams,
132                                          unsigned int batchSize,
133                                          const armnn::DataLayout& outputLayout)
134 {
135     // Get float32 image tensor
136     std::vector<float> imageDataFloat =
137         PrepareImageTensor<float>(imagePath, newWidth, newHeight, normParams, batchSize, outputLayout);
138     // Convert to int32 image tensor with static cast
139     std::vector<int> imageDataInt;
140     imageDataInt.reserve(imageDataFloat.size());
141     std::transform(imageDataFloat.begin(), imageDataFloat.end(), std::back_inserter(imageDataInt),
142                    [](float val) { return static_cast<int>(val); });
143     return imageDataInt;
144 }
145 
146 // Prepare qasymm8 image tensor
147 template <>
PrepareImageTensor(const std::string & imagePath,unsigned int newWidth,unsigned int newHeight,const NormalizationParameters & normParams,unsigned int batchSize,const armnn::DataLayout & outputLayout)148 std::vector<uint8_t> PrepareImageTensor<uint8_t>(const std::string& imagePath,
149                                                  unsigned int newWidth,
150                                                  unsigned int newHeight,
151                                                  const NormalizationParameters& normParams,
152                                                  unsigned int batchSize,
153                                                  const armnn::DataLayout& outputLayout)
154 {
155     // Get float32 image tensor
156     std::vector<float> imageDataFloat =
157         PrepareImageTensor<float>(imagePath, newWidth, newHeight, normParams, batchSize, outputLayout);
158     std::vector<uint8_t> imageDataQasymm8;
159     imageDataQasymm8.reserve(imageDataFloat.size());
160     // Convert to uint8 image tensor with static cast
161     std::transform(imageDataFloat.begin(), imageDataFloat.end(), std::back_inserter(imageDataQasymm8),
162                    [](float val) { return static_cast<uint8_t>(val); });
163     return imageDataQasymm8;
164 }
165 
166 /** Write image tensor to ofstream
167  *
168  * @param[in] imageData         Image tensor data
169  * @param[in] imageTensorFile   Output filestream (ofstream) to which the image tensor data is written
170  */
171 template <typename ElemType>
WriteImageTensorImpl(const std::vector<ElemType> & imageData,std::ofstream & imageTensorFile)172 void WriteImageTensorImpl(const std::vector<ElemType>& imageData, std::ofstream& imageTensorFile)
173 {
174     std::copy(imageData.begin(), imageData.end(), std::ostream_iterator<ElemType>(imageTensorFile, " "));
175 }
176 
177 // For uint8_t image tensor, cast it to int before writing it to prevent writing data as characters instead of
178 // numerical values
179 template <>
WriteImageTensorImpl(const std::vector<uint8_t> & imageData,std::ofstream & imageTensorFile)180 void WriteImageTensorImpl<uint8_t>(const std::vector<uint8_t>& imageData, std::ofstream& imageTensorFile)
181 {
182     std::copy(imageData.begin(), imageData.end(), std::ostream_iterator<int>(imageTensorFile, " "));
183 }
184