ArmNN
 21.08
ImageTensorGenerator.cpp
Go to the documentation of this file.
1 //
2 // Copyright © 2017 Arm Ltd. All rights reserved.
3 // SPDX-License-Identifier: MIT
4 //
5 
7 #include "../InferenceTestImage.hpp"
8 #include <armnn/Logging.hpp>
9 #include <armnn/TypesUtils.hpp>
11 
12 #include <mapbox/variant.hpp>
13 #include <cxxopts/cxxopts.hpp>
14 
15 #include <algorithm>
16 #include <fstream>
17 #include <iostream>
18 #include <string>
19 
20 namespace
21 {
22 
23 // parses the command line to extract
24 // * the input image file -i the input image file path (must exist)
25 // * the layout -l the data layout output generated with (optional - default value is NHWC)
26 // * the output file -o the output raw tensor file path (must not already exist)
27 class CommandLineProcessor
28 {
29 public:
30  bool ParseOptions(cxxopts::ParseResult& result)
31  {
32  // infile is mandatory
33  if (result.count("infile"))
34  {
35  if (!ValidateInputFile(result["infile"].as<std::string>()))
36  {
37  return false;
38  }
39  }
40  else
41  {
42  std::cerr << "-i/--infile parameter is mandatory." << std::endl;
43  return false;
44  }
45 
46  // model-format is mandatory
47  if (!result.count("model-format"))
48  {
49  std::cerr << "-f/--model-format parameter is mandatory." << std::endl;
50  return false;
51  }
52 
53  // outfile is mandatory
54  if (result.count("outfile"))
55  {
56  if (!ValidateOutputFile(result["outfile"].as<std::string>()))
57  {
58  return false;
59  }
60  }
61  else
62  {
63  std::cerr << "-o/--outfile parameter is mandatory." << std::endl;
64  return false;
65  }
66 
67  if (result.count("layout"))
68  {
69  if(!ValidateLayout(result["layout"].as<std::string>()))
70  {
71  return false;
72  }
73  }
74 
75  return true;
76  }
77 
78  bool ValidateInputFile(const std::string& inputFileName)
79  {
80  if (inputFileName.empty())
81  {
82  std::cerr << "No input file name specified" << std::endl;
83  return false;
84  }
85 
86  if (!fs::exists(inputFileName))
87  {
88  std::cerr << "Input file [" << inputFileName << "] does not exist" << std::endl;
89  return false;
90  }
91 
92  if (fs::is_directory(inputFileName))
93  {
94  std::cerr << "Input file [" << inputFileName << "] is a directory" << std::endl;
95  return false;
96  }
97 
98  return true;
99  }
100 
101  bool ValidateLayout(const std::string& layout)
102  {
103  if (layout.empty())
104  {
105  std::cerr << "No layout specified" << std::endl;
106  return false;
107  }
108 
109  std::vector<std::string> supportedLayouts = { "NHWC", "NCHW" };
110 
111  auto iterator = std::find(supportedLayouts.begin(), supportedLayouts.end(), layout);
112  if (iterator == supportedLayouts.end())
113  {
114  std::cerr << "Layout [" << layout << "] is not supported" << std::endl;
115  return false;
116  }
117 
118  return true;
119  }
120 
121  bool ValidateOutputFile(const std::string& outputFileName)
122  {
123  if (outputFileName.empty())
124  {
125  std::cerr << "No output file name specified" << std::endl;
126  return false;
127  }
128 
129  if (fs::exists(outputFileName))
130  {
131  std::cerr << "Output file [" << outputFileName << "] already exists" << std::endl;
132  return false;
133  }
134 
135  if (fs::is_directory(outputFileName))
136  {
137  std::cerr << "Output file [" << outputFileName << "] is a directory" << std::endl;
138  return false;
139  }
140 
141  fs::path outputPath(outputFileName);
142  if (!fs::exists(outputPath.parent_path()))
143  {
144  std::cerr << "Output directory [" << outputPath.parent_path().c_str() << "] does not exist" << std::endl;
145  return false;
146  }
147 
148  return true;
149  }
150 
151  bool ProcessCommandLine(int argc, char* argv[])
152  {
153  cxxopts::Options options("ImageTensorGenerator",
154  "Program for pre-processing a .jpg image "
155  "before generating a .raw tensor file from it.");
156 
157  try
158  {
159  options.add_options()
160  ("h,help", "Display help messages")
161  ("i,infile",
162  "Input image file to generate tensor from",
163  cxxopts::value<std::string>(m_InputFileName))
164  ("f,model-format",
165  "Format of the intended model file that uses the images."
166  "Different formats have different image normalization styles."
167  "If unset, defaults to tflite."
168  "Accepted value (tflite)",
169  cxxopts::value<std::string>(m_ModelFormat)->default_value("tflite"))
170  ("o,outfile",
171  "Output raw tensor file path",
172  cxxopts::value<std::string>(m_OutputFileName))
173  ("z,output-type",
174  "The data type of the output tensors."
175  "If unset, defaults to \"float\" for all defined inputs. "
176  "Accepted values (float, int, qasymms8 or qasymmu8)",
177  cxxopts::value<std::string>(m_OutputType)->default_value("float"))
178  ("new-width",
179  "Resize image to new width. Keep original width if unspecified",
180  cxxopts::value<std::string>(m_NewWidth)->default_value("0"))
181  ("new-height",
182  "Resize image to new height. Keep original height if unspecified",
183  cxxopts::value<std::string>(m_NewHeight)->default_value("0"))
184  ("l,layout",
185  "Output data layout, \"NHWC\" or \"NCHW\", default value NHWC",
186  cxxopts::value<std::string>(m_Layout)->default_value("NHWC"));
187  }
188  catch (const std::exception& e)
189  {
190  std::cerr << options.help() << std::endl;
191  return false;
192  }
193 
194  try
195  {
196  auto result = options.parse(argc, argv);
197 
198  if (result.count("help"))
199  {
200  std::cout << options.help() << std::endl;
201  return false;
202  }
203 
204  // Check for mandatory parameters and validate inputs
205  if(!ParseOptions(result)){
206  return false;
207  }
208  }
209  catch (const cxxopts::OptionException& e)
210  {
211  std::cerr << e.what() << std::endl << std::endl;
212  return false;
213  }
214 
215  return true;
216  }
217 
218  std::string GetInputFileName() {return m_InputFileName;}
219  armnn::DataLayout GetLayout()
220  {
221  if (m_Layout == "NHWC")
222  {
224  }
225  else if (m_Layout == "NCHW")
226  {
228  }
229  else
230  {
231  throw armnn::Exception("Unsupported data layout: " + m_Layout);
232  }
233  }
234  std::string GetOutputFileName() {return m_OutputFileName;}
235  unsigned int GetNewWidth() {return static_cast<unsigned int>(std::stoi(m_NewWidth));}
236  unsigned int GetNewHeight() {return static_cast<unsigned int>(std::stoi(m_NewHeight));}
237  SupportedFrontend GetModelFormat()
238  {
239  if (m_ModelFormat == "tflite")
240  {
242  }
243  else
244  {
245  throw armnn::Exception("Unsupported model format" + m_ModelFormat);
246  }
247  }
248  armnn::DataType GetOutputType()
249  {
250  if (m_OutputType == "float")
251  {
253  }
254  else if (m_OutputType == "int")
255  {
257  }
258  else if (m_OutputType == "qasymm8" || m_OutputType == "qasymmu8")
259  {
261  }
262  else if (m_OutputType == "qasymms8")
263  {
265  }
266  else
267  {
268  throw armnn::Exception("Unsupported input type" + m_OutputType);
269  }
270  }
271 
272 private:
273  std::string m_InputFileName;
274  std::string m_Layout;
275  std::string m_OutputFileName;
276  std::string m_NewWidth;
277  std::string m_NewHeight;
278  std::string m_ModelFormat;
279  std::string m_OutputType;
280 };
281 
282 } // namespace anonymous
283 
284 int main(int argc, char* argv[])
285 {
286  CommandLineProcessor cmdline;
287  if (!cmdline.ProcessCommandLine(argc, argv))
288  {
289  return -1;
290  }
291  const std::string imagePath(cmdline.GetInputFileName());
292  const std::string outputPath(cmdline.GetOutputFileName());
293  const SupportedFrontend& modelFormat(cmdline.GetModelFormat());
294  const armnn::DataType outputType(cmdline.GetOutputType());
295  const unsigned int newWidth = cmdline.GetNewWidth();
296  const unsigned int newHeight = cmdline.GetNewHeight();
297  const unsigned int batchSize = 1;
298  const armnn::DataLayout outputLayout(cmdline.GetLayout());
299 
300  using TContainer = mapbox::util::variant<std::vector<float>, std::vector<int>, std::vector<uint8_t>,
301  std::vector<int8_t>>;
302  std::vector<TContainer> imageDataContainers;
303  const NormalizationParameters& normParams = GetNormalizationParameters(modelFormat, outputType);
304  try
305  {
306  switch (outputType)
307  {
309  imageDataContainers.push_back(PrepareImageTensor<int>(
310  imagePath, newWidth, newHeight, normParams, batchSize, outputLayout));
311  break;
313  imageDataContainers.push_back(PrepareImageTensor<uint8_t>(
314  imagePath, newWidth, newHeight, normParams, batchSize, outputLayout));
315  break;
317  imageDataContainers.push_back(PrepareImageTensor<int8_t>(
318  imagePath, newWidth, newHeight, normParams, batchSize, outputLayout));
319  break;
321  default:
322  imageDataContainers.push_back(PrepareImageTensor<float>(
323  imagePath, newWidth, newHeight, normParams, batchSize, outputLayout));
324  break;
325  }
326  }
327  catch (const InferenceTestImageException& e)
328  {
329  ARMNN_LOG(fatal) << "Failed to load image file " << imagePath << " with error: " << e.what();
330  return -1;
331  }
332 
333  std::ofstream imageTensorFile;
334  imageTensorFile.open(outputPath, std::ofstream::out);
335  if (imageTensorFile.is_open())
336  {
337  mapbox::util::apply_visitor(
338  [&imageTensorFile](auto&& imageData){ WriteImageTensorImpl(imageData,imageTensorFile); },
339  imageDataContainers[0]
340  );
341 
342  if (!imageTensorFile)
343  {
344  ARMNN_LOG(fatal) << "Failed to write to output file" << outputPath;
345  imageTensorFile.close();
346  return -1;
347  }
348  imageTensorFile.close();
349  }
350  else
351  {
352  ARMNN_LOG(fatal) << "Failed to open output file" << outputPath;
353  return -1;
354  }
355 
356  return 0;
357 }
DataLayout
Definition: Types.hpp:53
void ParseOptions(const std::vector< BackendOptions > &options, BackendId backend, F f)
NormalizationParameters GetNormalizationParameters(const SupportedFrontend &modelFormat, const armnn::DataType &outputType)
Get normalization parameters.
virtual const char * what() const noexcept override
Definition: Exceptions.cpp:32
#define ARMNN_LOG(severity)
Definition: Logging.hpp:202
void WriteImageTensorImpl(const std::vector< ElemType > &imageData, std::ofstream &imageTensorFile)
Write image tensor to ofstream.
std::vector< uint8_t > PrepareImageTensor< uint8_t >(const std::string &imagePath, unsigned int newWidth, unsigned int newHeight, const NormalizationParameters &normParams, unsigned int batchSize, const armnn::DataLayout &outputLayout)
DataType
Definition: Types.hpp:35
std::vector< int > PrepareImageTensor< int >(const std::string &imagePath, unsigned int newWidth, unsigned int newHeight, const NormalizationParameters &normParams, unsigned int batchSize, const armnn::DataLayout &outputLayout)
Base class for all ArmNN exceptions so that users can filter to just those.
Definition: Exceptions.hpp:46
int main(int argc, char *argv[])
std::vector< int8_t > PrepareImageTensor< int8_t >(const std::string &imagePath, unsigned int newWidth, unsigned int newHeight, const NormalizationParameters &normParams, unsigned int batchSize, const armnn::DataLayout &outputLayout)
mapbox::util::variant< std::vector< float >, std::vector< int >, std::vector< unsigned char >, std::vector< int8_t > > TContainer
std::vector< float > PrepareImageTensor< float >(const std::string &imagePath, unsigned int newWidth, unsigned int newHeight, const NormalizationParameters &normParams, unsigned int batchSize, const armnn::DataLayout &outputLayout)