blob: 4e8fe78ad89ce8ab2fe220d884caafa90d02084c [file] [log] [blame]
Sadik Armagan8271f812019-04-19 09:55:06 +01001//
2// Copyright © 2017 Arm Ltd. All rights reserved.
3// SPDX-License-Identifier: MIT
4//
5
SiCong Li39f46392019-06-21 12:00:04 +01006#include "ImageTensorGenerator.hpp"
Sadik Armagan8271f812019-04-19 09:55:06 +01007#include "../InferenceTestImage.hpp"
Derek Lamberti08446972019-11-26 16:38:31 +00008#include <armnn/Logging.hpp>
SiCong Li39f46392019-06-21 12:00:04 +01009#include <armnn/TypesUtils.hpp>
Sadik Armagan8271f812019-04-19 09:55:06 +010010
11#include <boost/filesystem.hpp>
12#include <boost/filesystem/operations.hpp>
13#include <boost/filesystem/path.hpp>
Sadik Armagan8271f812019-04-19 09:55:06 +010014#include <boost/program_options.hpp>
SiCong Li39f46392019-06-21 12:00:04 +010015#include <boost/variant.hpp>
Sadik Armagan8271f812019-04-19 09:55:06 +010016
17#include <algorithm>
18#include <fstream>
19#include <iostream>
20#include <string>
21
22namespace
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)
29class CommandLineProcessor
30{
31public:
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
SiCong Li39f46392019-06-21 12:00:04 +010063 std::vector<std::string> supportedLayouts = { "NHWC", "NCHW" };
Sadik Armagan8271f812019-04-19 09:55:06 +010064
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")
SiCong Li39f46392019-06-21 12:00:04 +0100116 ("model-format,f", po::value<std::string>(&m_ModelFormat)->required(),
SiCong Li90bb7ab2019-07-11 12:02:45 +0100117 "Format of the intended model file that uses the images."
118 "Different formats have different image normalization styles."
119 "Accepted values (caffe, tensorflow, tflite)")
Sadik Armagan8271f812019-04-19 09:55:06 +0100120 ("outfile,o", po::value<std::string>(&m_OutputFileName)->required(),
SiCong Li39f46392019-06-21 12:00:04 +0100121 "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)")
SiCong Li90bb7ab2019-07-11 12:02:45 +0100126 ("new-width", po::value<std::string>(&m_NewWidth)->default_value("0"),
SiCong Li39f46392019-06-21 12:00:04 +0100127 "Resize image to new width. Keep original width if unspecified")
SiCong Li90bb7ab2019-07-11 12:02:45 +0100128 ("new-height", po::value<std::string>(&m_NewHeight)->default_value("0"),
SiCong Li39f46392019-06-21 12:00:04 +0100129 "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");
Sadik Armagan8271f812019-04-19 09:55:06 +0100132 }
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;}
SiCong Li39f46392019-06-21 12:00:04 +0100179 armnn::DataLayout GetLayout()
180 {
181 if (m_Layout == "NHWC")
182 {
183 return armnn::DataLayout::NHWC;
184 }
185 else if (m_Layout == "NCHW")
186 {
187 return armnn::DataLayout::NCHW;
188 }
189 else
190 {
191 throw armnn::Exception("Unsupported data layout: " + m_Layout);
192 }
193 }
Sadik Armagan8271f812019-04-19 09:55:06 +0100194 std::string GetOutputFileName() {return m_OutputFileName;}
SiCong Li39f46392019-06-21 12:00:04 +0100195 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 {
201 return SupportedFrontend::Caffe;
202 }
203 else if (m_ModelFormat == "tensorflow")
204 {
205 return SupportedFrontend::TensorFlow;
206 }
207 else if (m_ModelFormat == "tflite")
208 {
209 return SupportedFrontend::TFLite;
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 {
220 return armnn::DataType::Float32;
221 }
222 else if (m_OutputType == "int")
223 {
224 return armnn::DataType::Signed32;
225 }
226 else if (m_OutputType == "qasymm8")
227 {
228 return armnn::DataType::QuantisedAsymm8;
229 }
230 else
231 {
232 throw armnn::Exception("Unsupported input type" + m_OutputType);
233 }
234 }
Sadik Armagan8271f812019-04-19 09:55:06 +0100235
236private:
237 std::string m_InputFileName;
238 std::string m_Layout;
239 std::string m_OutputFileName;
SiCong Li39f46392019-06-21 12:00:04 +0100240 std::string m_NewWidth;
241 std::string m_NewHeight;
242 std::string m_ModelFormat;
243 std::string m_OutputType;
Sadik Armagan8271f812019-04-19 09:55:06 +0100244};
245
246} // namespace anonymous
247
248int main(int argc, char* argv[])
249{
250 CommandLineProcessor cmdline;
251 if (!cmdline.ProcessCommandLine(argc, argv))
252 {
253 return -1;
254 }
Sadik Armagan8271f812019-04-19 09:55:06 +0100255 const std::string imagePath(cmdline.GetInputFileName());
256 const std::string outputPath(cmdline.GetOutputFileName());
SiCong Li39f46392019-06-21 12:00:04 +0100257 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());
Sadik Armagan8271f812019-04-19 09:55:06 +0100263
SiCong Li39f46392019-06-21 12:00:04 +0100264 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);
Sadik Armagan8271f812019-04-19 09:55:06 +0100267 try
268 {
SiCong Li39f46392019-06-21 12:00:04 +0100269 switch (outputType)
270 {
271 case armnn::DataType::Signed32:
272 imageDataContainers.push_back(PrepareImageTensor<int>(
273 imagePath, newWidth, newHeight, normParams, batchSize, outputLayout));
274 break;
275 case armnn::DataType::QuantisedAsymm8:
276 imageDataContainers.push_back(PrepareImageTensor<uint8_t>(
277 imagePath, newWidth, newHeight, normParams, batchSize, outputLayout));
278 break;
279 case armnn::DataType::Float32:
280 default:
281 imageDataContainers.push_back(PrepareImageTensor<float>(
282 imagePath, newWidth, newHeight, normParams, batchSize, outputLayout));
283 break;
284 }
Sadik Armagan8271f812019-04-19 09:55:06 +0100285 }
286 catch (const InferenceTestImageException& e)
287 {
Derek Lamberti08446972019-11-26 16:38:31 +0000288 ARMNN_LOG(fatal) << "Failed to load image file " << imagePath << " with error: " << e.what();
Sadik Armagan8271f812019-04-19 09:55:06 +0100289 return -1;
290 }
291
292 std::ofstream imageTensorFile;
293 imageTensorFile.open(outputPath, std::ofstream::out);
294 if (imageTensorFile.is_open())
295 {
SiCong Li39f46392019-06-21 12:00:04 +0100296 boost::apply_visitor([&imageTensorFile](auto&& imageData) { WriteImageTensorImpl(imageData, imageTensorFile); },
297 imageDataContainers[0]);
Sadik Armagan8271f812019-04-19 09:55:06 +0100298 if (!imageTensorFile)
299 {
Derek Lamberti08446972019-11-26 16:38:31 +0000300 ARMNN_LOG(fatal) << "Failed to write to output file" << outputPath;
Sadik Armagan8271f812019-04-19 09:55:06 +0100301 imageTensorFile.close();
302 return -1;
303 }
304 imageTensorFile.close();
305 }
306 else
307 {
Derek Lamberti08446972019-11-26 16:38:31 +0000308 ARMNN_LOG(fatal) << "Failed to open output file" << outputPath;
Sadik Armagan8271f812019-04-19 09:55:06 +0100309 return -1;
310 }
311
312 return 0;
SiCong Li39f46392019-06-21 12:00:04 +0100313}