blob: b4432558c4d31696102035b89ba7475e61953c87 [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>
Rob Hughes9542f902021-07-14 09:48:54 +010010#include <armnnUtils/Filesystem.hpp>
Sadik Armagan8271f812019-04-19 09:55:06 +010011
James Ward6d9f5c52020-09-28 11:56:35 +010012#include <mapbox/variant.hpp>
Matthew Sloyanbf88b6f2020-09-25 17:22:00 +010013#include <cxxopts/cxxopts.hpp>
Sadik Armagan8271f812019-04-19 09:55:06 +010014
15#include <algorithm>
16#include <fstream>
17#include <iostream>
18#include <string>
19
20namespace
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)
27class CommandLineProcessor
28{
29public:
Matthew Sloyanbf88b6f2020-09-25 17:22:00 +010030 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
Sadik Armagan8271f812019-04-19 09:55:06 +010078 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
Francis Murtagh532a29d2020-06-29 11:50:01 +010086 if (!fs::exists(inputFileName))
Sadik Armagan8271f812019-04-19 09:55:06 +010087 {
88 std::cerr << "Input file [" << inputFileName << "] does not exist" << std::endl;
89 return false;
90 }
91
Francis Murtagh532a29d2020-06-29 11:50:01 +010092 if (fs::is_directory(inputFileName))
Sadik Armagan8271f812019-04-19 09:55:06 +010093 {
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
SiCong Li39f46392019-06-21 12:00:04 +0100109 std::vector<std::string> supportedLayouts = { "NHWC", "NCHW" };
Sadik Armagan8271f812019-04-19 09:55:06 +0100110
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
Matthew Sloyanbf88b6f2020-09-25 17:22:00 +0100121 bool ValidateOutputFile(const std::string& outputFileName)
Sadik Armagan8271f812019-04-19 09:55:06 +0100122 {
123 if (outputFileName.empty())
124 {
125 std::cerr << "No output file name specified" << std::endl;
126 return false;
127 }
128
Francis Murtagh532a29d2020-06-29 11:50:01 +0100129 if (fs::exists(outputFileName))
Sadik Armagan8271f812019-04-19 09:55:06 +0100130 {
131 std::cerr << "Output file [" << outputFileName << "] already exists" << std::endl;
132 return false;
133 }
134
Francis Murtagh532a29d2020-06-29 11:50:01 +0100135 if (fs::is_directory(outputFileName))
Sadik Armagan8271f812019-04-19 09:55:06 +0100136 {
137 std::cerr << "Output file [" << outputFileName << "] is a directory" << std::endl;
138 return false;
139 }
140
Francis Murtagh532a29d2020-06-29 11:50:01 +0100141 fs::path outputPath(outputFileName);
142 if (!fs::exists(outputPath.parent_path()))
Sadik Armagan8271f812019-04-19 09:55:06 +0100143 {
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 {
Matthew Sloyanbf88b6f2020-09-25 17:22:00 +0100153 cxxopts::Options options("ImageTensorGenerator",
154 "Program for pre-processing a .jpg image "
155 "before generating a .raw tensor file from it.");
Sadik Armagan8271f812019-04-19 09:55:06 +0100156
Sadik Armagan8271f812019-04-19 09:55:06 +0100157 try
158 {
Matthew Sloyanbf88b6f2020-09-25 17:22:00 +0100159 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."
Mike Kellyd7ed6d42021-07-21 09:42:43 +0100167 "If unset, defaults to tflite."
Kevin Mayeb03e0f2021-04-28 16:16:22 +0100168 "Accepted value (tflite)",
Mike Kellyd7ed6d42021-07-21 09:42:43 +0100169 cxxopts::value<std::string>(m_ModelFormat)->default_value("tflite"))
Matthew Sloyanbf88b6f2020-09-25 17:22:00 +0100170 ("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. "
Mike Kellyd7ed6d42021-07-21 09:42:43 +0100176 "Accepted values (float, int, qasymms8 or qasymmu8)",
Matthew Sloyanbf88b6f2020-09-25 17:22:00 +0100177 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"));
Sadik Armagan8271f812019-04-19 09:55:06 +0100187 }
188 catch (const std::exception& e)
189 {
Matthew Sloyanbf88b6f2020-09-25 17:22:00 +0100190 std::cerr << options.help() << std::endl;
Sadik Armagan8271f812019-04-19 09:55:06 +0100191 return false;
192 }
193
Sadik Armagan8271f812019-04-19 09:55:06 +0100194 try
195 {
Matthew Sloyanbf88b6f2020-09-25 17:22:00 +0100196 auto result = options.parse(argc, argv);
Sadik Armagan8271f812019-04-19 09:55:06 +0100197
Matthew Sloyanbf88b6f2020-09-25 17:22:00 +0100198 if (result.count("help"))
Sadik Armagan8271f812019-04-19 09:55:06 +0100199 {
Matthew Sloyanbf88b6f2020-09-25 17:22:00 +0100200 std::cout << options.help() << std::endl;
Sadik Armagan8271f812019-04-19 09:55:06 +0100201 return false;
202 }
203
Matthew Sloyanbf88b6f2020-09-25 17:22:00 +0100204 // Check for mandatory parameters and validate inputs
205 if(!ParseOptions(result)){
206 return false;
207 }
Sadik Armagan8271f812019-04-19 09:55:06 +0100208 }
Matthew Sloyanbf88b6f2020-09-25 17:22:00 +0100209 catch (const cxxopts::OptionException& e)
Sadik Armagan8271f812019-04-19 09:55:06 +0100210 {
211 std::cerr << e.what() << std::endl << std::endl;
Sadik Armagan8271f812019-04-19 09:55:06 +0100212 return false;
213 }
214
215 return true;
216 }
217
218 std::string GetInputFileName() {return m_InputFileName;}
SiCong Li39f46392019-06-21 12:00:04 +0100219 armnn::DataLayout GetLayout()
220 {
221 if (m_Layout == "NHWC")
222 {
223 return armnn::DataLayout::NHWC;
224 }
225 else if (m_Layout == "NCHW")
226 {
227 return armnn::DataLayout::NCHW;
228 }
229 else
230 {
231 throw armnn::Exception("Unsupported data layout: " + m_Layout);
232 }
233 }
Sadik Armagan8271f812019-04-19 09:55:06 +0100234 std::string GetOutputFileName() {return m_OutputFileName;}
SiCong Li39f46392019-06-21 12:00:04 +0100235 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 {
Kevin Mayeb03e0f2021-04-28 16:16:22 +0100239 if (m_ModelFormat == "tflite")
SiCong Li39f46392019-06-21 12:00:04 +0100240 {
241 return SupportedFrontend::TFLite;
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 {
252 return armnn::DataType::Float32;
253 }
254 else if (m_OutputType == "int")
255 {
256 return armnn::DataType::Signed32;
257 }
Mike Kellyd7ed6d42021-07-21 09:42:43 +0100258 else if (m_OutputType == "qasymm8" || m_OutputType == "qasymmu8")
SiCong Li39f46392019-06-21 12:00:04 +0100259 {
Derek Lambertif90c56d2020-01-10 17:14:08 +0000260 return armnn::DataType::QAsymmU8;
SiCong Li39f46392019-06-21 12:00:04 +0100261 }
Mike Kellyd7ed6d42021-07-21 09:42:43 +0100262 else if (m_OutputType == "qasymms8")
263 {
264 return armnn::DataType::QAsymmS8;
265 }
SiCong Li39f46392019-06-21 12:00:04 +0100266 else
267 {
268 throw armnn::Exception("Unsupported input type" + m_OutputType);
269 }
270 }
Sadik Armagan8271f812019-04-19 09:55:06 +0100271
272private:
273 std::string m_InputFileName;
274 std::string m_Layout;
275 std::string m_OutputFileName;
SiCong Li39f46392019-06-21 12:00:04 +0100276 std::string m_NewWidth;
277 std::string m_NewHeight;
278 std::string m_ModelFormat;
279 std::string m_OutputType;
Sadik Armagan8271f812019-04-19 09:55:06 +0100280};
281
282} // namespace anonymous
283
284int main(int argc, char* argv[])
285{
286 CommandLineProcessor cmdline;
287 if (!cmdline.ProcessCommandLine(argc, argv))
288 {
289 return -1;
290 }
Sadik Armagan8271f812019-04-19 09:55:06 +0100291 const std::string imagePath(cmdline.GetInputFileName());
292 const std::string outputPath(cmdline.GetOutputFileName());
SiCong Li39f46392019-06-21 12:00:04 +0100293 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());
Sadik Armagan8271f812019-04-19 09:55:06 +0100299
Mike Kellyd7ed6d42021-07-21 09:42:43 +0100300 using TContainer = mapbox::util::variant<std::vector<float>, std::vector<int>, std::vector<uint8_t>,
301 std::vector<int8_t>>;
SiCong Li39f46392019-06-21 12:00:04 +0100302 std::vector<TContainer> imageDataContainers;
303 const NormalizationParameters& normParams = GetNormalizationParameters(modelFormat, outputType);
Sadik Armagan8271f812019-04-19 09:55:06 +0100304 try
305 {
SiCong Li39f46392019-06-21 12:00:04 +0100306 switch (outputType)
307 {
308 case armnn::DataType::Signed32:
309 imageDataContainers.push_back(PrepareImageTensor<int>(
310 imagePath, newWidth, newHeight, normParams, batchSize, outputLayout));
311 break;
Derek Lambertif90c56d2020-01-10 17:14:08 +0000312 case armnn::DataType::QAsymmU8:
SiCong Li39f46392019-06-21 12:00:04 +0100313 imageDataContainers.push_back(PrepareImageTensor<uint8_t>(
314 imagePath, newWidth, newHeight, normParams, batchSize, outputLayout));
315 break;
Mike Kellyd7ed6d42021-07-21 09:42:43 +0100316 case armnn::DataType::QAsymmS8:
317 imageDataContainers.push_back(PrepareImageTensor<int8_t>(
318 imagePath, newWidth, newHeight, normParams, batchSize, outputLayout));
319 break;
SiCong Li39f46392019-06-21 12:00:04 +0100320 case armnn::DataType::Float32:
321 default:
322 imageDataContainers.push_back(PrepareImageTensor<float>(
323 imagePath, newWidth, newHeight, normParams, batchSize, outputLayout));
324 break;
325 }
Sadik Armagan8271f812019-04-19 09:55:06 +0100326 }
327 catch (const InferenceTestImageException& e)
328 {
Derek Lamberti08446972019-11-26 16:38:31 +0000329 ARMNN_LOG(fatal) << "Failed to load image file " << imagePath << " with error: " << e.what();
Sadik Armagan8271f812019-04-19 09:55:06 +0100330 return -1;
331 }
332
333 std::ofstream imageTensorFile;
334 imageTensorFile.open(outputPath, std::ofstream::out);
335 if (imageTensorFile.is_open())
336 {
James Ward6d9f5c52020-09-28 11:56:35 +0100337 mapbox::util::apply_visitor(
338 [&imageTensorFile](auto&& imageData){ WriteImageTensorImpl(imageData,imageTensorFile); },
339 imageDataContainers[0]
340 );
341
Sadik Armagan8271f812019-04-19 09:55:06 +0100342 if (!imageTensorFile)
343 {
Derek Lamberti08446972019-11-26 16:38:31 +0000344 ARMNN_LOG(fatal) << "Failed to write to output file" << outputPath;
Sadik Armagan8271f812019-04-19 09:55:06 +0100345 imageTensorFile.close();
346 return -1;
347 }
348 imageTensorFile.close();
349 }
350 else
351 {
Derek Lamberti08446972019-11-26 16:38:31 +0000352 ARMNN_LOG(fatal) << "Failed to open output file" << outputPath;
Sadik Armagan8271f812019-04-19 09:55:06 +0100353 return -1;
354 }
355
356 return 0;
SiCong Li39f46392019-06-21 12:00:04 +0100357}