ArmNN
 20.05
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>
10 
11 #include <boost/filesystem.hpp>
12 #include <boost/filesystem/operations.hpp>
13 #include <boost/filesystem/path.hpp>
14 #include <boost/program_options.hpp>
15 #include <boost/variant.hpp>
16 
17 #include <algorithm>
18 #include <fstream>
19 #include <iostream>
20 #include <string>
21 
22 namespace
23 {
24 
25 // parses the command line to extract
26 // * the input image file -i the input image file path (must exist)
27 // * the layout -l the data layout output generated with (optional - default value is NHWC)
28 // * the output file -o the output raw tensor file path (must not already exist)
29 class CommandLineProcessor
30 {
31 public:
32  bool ValidateInputFile(const std::string& inputFileName)
33  {
34  if (inputFileName.empty())
35  {
36  std::cerr << "No input file name specified" << std::endl;
37  return false;
38  }
39 
40  if (!boost::filesystem::exists(inputFileName))
41  {
42  std::cerr << "Input file [" << inputFileName << "] does not exist" << std::endl;
43  return false;
44  }
45 
46  if (boost::filesystem::is_directory(inputFileName))
47  {
48  std::cerr << "Input file [" << inputFileName << "] is a directory" << std::endl;
49  return false;
50  }
51 
52  return true;
53  }
54 
55  bool ValidateLayout(const std::string& layout)
56  {
57  if (layout.empty())
58  {
59  std::cerr << "No layout specified" << std::endl;
60  return false;
61  }
62 
63  std::vector<std::string> supportedLayouts = { "NHWC", "NCHW" };
64 
65  auto iterator = std::find(supportedLayouts.begin(), supportedLayouts.end(), layout);
66  if (iterator == supportedLayouts.end())
67  {
68  std::cerr << "Layout [" << layout << "] is not supported" << std::endl;
69  return false;
70  }
71 
72  return true;
73  }
74 
75  bool ValidateOutputFile(std::string& outputFileName)
76  {
77  if (outputFileName.empty())
78  {
79  std::cerr << "No output file name specified" << std::endl;
80  return false;
81  }
82 
83  if (boost::filesystem::exists(outputFileName))
84  {
85  std::cerr << "Output file [" << outputFileName << "] already exists" << std::endl;
86  return false;
87  }
88 
89  if (boost::filesystem::is_directory(outputFileName))
90  {
91  std::cerr << "Output file [" << outputFileName << "] is a directory" << std::endl;
92  return false;
93  }
94 
95  boost::filesystem::path outputPath(outputFileName);
96  if (!boost::filesystem::exists(outputPath.parent_path()))
97  {
98  std::cerr << "Output directory [" << outputPath.parent_path().c_str() << "] does not exist" << std::endl;
99  return false;
100  }
101 
102  return true;
103  }
104 
105  bool ProcessCommandLine(int argc, char* argv[])
106  {
107  namespace po = boost::program_options;
108 
109  po::options_description desc("Options");
110  try
111  {
112  desc.add_options()
113  ("help,h", "Display help messages")
114  ("infile,i", po::value<std::string>(&m_InputFileName)->required(),
115  "Input image file to generate tensor from")
116  ("model-format,f", po::value<std::string>(&m_ModelFormat)->required(),
117  "Format of the intended model file that uses the images."
118  "Different formats have different image normalization styles."
119  "Accepted values (caffe, tensorflow, tflite)")
120  ("outfile,o", po::value<std::string>(&m_OutputFileName)->required(),
121  "Output raw tensor file path")
122  ("output-type,z", po::value<std::string>(&m_OutputType)->default_value("float"),
123  "The data type of the output tensors."
124  "If unset, defaults to \"float\" for all defined inputs. "
125  "Accepted values (float, int or qasymm8)")
126  ("new-width", po::value<std::string>(&m_NewWidth)->default_value("0"),
127  "Resize image to new width. Keep original width if unspecified")
128  ("new-height", po::value<std::string>(&m_NewHeight)->default_value("0"),
129  "Resize image to new height. Keep original height if unspecified")
130  ("layout,l", po::value<std::string>(&m_Layout)->default_value("NHWC"),
131  "Output data layout, \"NHWC\" or \"NCHW\", default value NHWC");
132  }
133  catch (const std::exception& e)
134  {
135  std::cerr << "Fatal internal error: [" << e.what() << "]" << std::endl;
136  return false;
137  }
138 
139  po::variables_map vm;
140 
141  try
142  {
143  po::store(po::parse_command_line(argc, argv, desc), vm);
144 
145  if (vm.count("help"))
146  {
147  std::cout << desc << std::endl;
148  return false;
149  }
150 
151  po::notify(vm);
152  }
153  catch (const po::error& e)
154  {
155  std::cerr << e.what() << std::endl << std::endl;
156  std::cerr << desc << std::endl;
157  return false;
158  }
159 
160  if (!ValidateInputFile(m_InputFileName))
161  {
162  return false;
163  }
164 
165  if (!ValidateLayout(m_Layout))
166  {
167  return false;
168  }
169 
170  if (!ValidateOutputFile(m_OutputFileName))
171  {
172  return false;
173  }
174 
175  return true;
176  }
177 
178  std::string GetInputFileName() {return m_InputFileName;}
179  armnn::DataLayout GetLayout()
180  {
181  if (m_Layout == "NHWC")
182  {
184  }
185  else if (m_Layout == "NCHW")
186  {
188  }
189  else
190  {
191  throw armnn::Exception("Unsupported data layout: " + m_Layout);
192  }
193  }
194  std::string GetOutputFileName() {return m_OutputFileName;}
195  unsigned int GetNewWidth() {return static_cast<unsigned int>(std::stoi(m_NewWidth));}
196  unsigned int GetNewHeight() {return static_cast<unsigned int>(std::stoi(m_NewHeight));}
197  SupportedFrontend GetModelFormat()
198  {
199  if (m_ModelFormat == "caffe")
200  {
202  }
203  else if (m_ModelFormat == "tensorflow")
204  {
206  }
207  else if (m_ModelFormat == "tflite")
208  {
210  }
211  else
212  {
213  throw armnn::Exception("Unsupported model format" + m_ModelFormat);
214  }
215  }
216  armnn::DataType GetOutputType()
217  {
218  if (m_OutputType == "float")
219  {
221  }
222  else if (m_OutputType == "int")
223  {
225  }
226  else if (m_OutputType == "qasymm8")
227  {
229  }
230  else
231  {
232  throw armnn::Exception("Unsupported input type" + m_OutputType);
233  }
234  }
235 
236 private:
237  std::string m_InputFileName;
238  std::string m_Layout;
239  std::string m_OutputFileName;
240  std::string m_NewWidth;
241  std::string m_NewHeight;
242  std::string m_ModelFormat;
243  std::string m_OutputType;
244 };
245 
246 } // namespace anonymous
247 
248 int main(int argc, char* argv[])
249 {
250  CommandLineProcessor cmdline;
251  if (!cmdline.ProcessCommandLine(argc, argv))
252  {
253  return -1;
254  }
255  const std::string imagePath(cmdline.GetInputFileName());
256  const std::string outputPath(cmdline.GetOutputFileName());
257  const SupportedFrontend& modelFormat(cmdline.GetModelFormat());
258  const armnn::DataType outputType(cmdline.GetOutputType());
259  const unsigned int newWidth = cmdline.GetNewWidth();
260  const unsigned int newHeight = cmdline.GetNewHeight();
261  const unsigned int batchSize = 1;
262  const armnn::DataLayout outputLayout(cmdline.GetLayout());
263 
264  using TContainer = boost::variant<std::vector<float>, std::vector<int>, std::vector<uint8_t>>;
265  std::vector<TContainer> imageDataContainers;
266  const NormalizationParameters& normParams = GetNormalizationParameters(modelFormat, outputType);
267  try
268  {
269  switch (outputType)
270  {
272  imageDataContainers.push_back(PrepareImageTensor<int>(
273  imagePath, newWidth, newHeight, normParams, batchSize, outputLayout));
274  break;
276  imageDataContainers.push_back(PrepareImageTensor<uint8_t>(
277  imagePath, newWidth, newHeight, normParams, batchSize, outputLayout));
278  break;
280  default:
281  imageDataContainers.push_back(PrepareImageTensor<float>(
282  imagePath, newWidth, newHeight, normParams, batchSize, outputLayout));
283  break;
284  }
285  }
286  catch (const InferenceTestImageException& e)
287  {
288  ARMNN_LOG(fatal) << "Failed to load image file " << imagePath << " with error: " << e.what();
289  return -1;
290  }
291 
292  std::ofstream imageTensorFile;
293  imageTensorFile.open(outputPath, std::ofstream::out);
294  if (imageTensorFile.is_open())
295  {
296  boost::apply_visitor([&imageTensorFile](auto&& imageData) { WriteImageTensorImpl(imageData, imageTensorFile); },
297  imageDataContainers[0]);
298  if (!imageTensorFile)
299  {
300  ARMNN_LOG(fatal) << "Failed to write to output file" << outputPath;
301  imageTensorFile.close();
302  return -1;
303  }
304  imageTensorFile.close();
305  }
306  else
307  {
308  ARMNN_LOG(fatal) << "Failed to open output file" << outputPath;
309  return -1;
310  }
311 
312  return 0;
313 }
DataLayout
Definition: Types.hpp:49
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:163
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)
boost::variant< std::vector< float >, std::vector< int >, std::vector< unsigned char > > TContainer
DataType
Definition: Types.hpp:32
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< float > PrepareImageTensor< float >(const std::string &imagePath, unsigned int newWidth, unsigned int newHeight, const NormalizationParameters &normParams, unsigned int batchSize, const armnn::DataLayout &outputLayout)