blob: 6e5b8ca0d9eab01b452e3ddb2924cccbddadb57c [file] [log] [blame]
Sadik Armagan8271f812019-04-19 09:55:06 +01001//
Jim Flynn357add22023-04-10 23:26:40 +01002// Copyright © 2017, 2023 Arm Ltd and Contributors. All rights reserved.
Sadik Armagan8271f812019-04-19 09:55:06 +01003// 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
Matthew Sloyanbf88b6f2020-09-25 17:22:00 +010012#include <cxxopts/cxxopts.hpp>
Sadik Armagan8271f812019-04-19 09:55:06 +010013
14#include <algorithm>
15#include <fstream>
16#include <iostream>
17#include <string>
18
19namespace
20{
21
22// parses the command line to extract
23// * the input image file -i the input image file path (must exist)
24// * the layout -l the data layout output generated with (optional - default value is NHWC)
25// * the output file -o the output raw tensor file path (must not already exist)
26class CommandLineProcessor
27{
28public:
Matthew Sloyanbf88b6f2020-09-25 17:22:00 +010029 bool ParseOptions(cxxopts::ParseResult& result)
30 {
31 // infile is mandatory
32 if (result.count("infile"))
33 {
34 if (!ValidateInputFile(result["infile"].as<std::string>()))
35 {
36 return false;
37 }
38 }
39 else
40 {
41 std::cerr << "-i/--infile parameter is mandatory." << std::endl;
42 return false;
43 }
44
45 // model-format is mandatory
46 if (!result.count("model-format"))
47 {
48 std::cerr << "-f/--model-format parameter is mandatory." << std::endl;
49 return false;
50 }
51
52 // outfile is mandatory
53 if (result.count("outfile"))
54 {
55 if (!ValidateOutputFile(result["outfile"].as<std::string>()))
56 {
57 return false;
58 }
59 }
60 else
61 {
62 std::cerr << "-o/--outfile parameter is mandatory." << std::endl;
63 return false;
64 }
65
66 if (result.count("layout"))
67 {
68 if(!ValidateLayout(result["layout"].as<std::string>()))
69 {
70 return false;
71 }
72 }
73
74 return true;
75 }
76
Sadik Armagan8271f812019-04-19 09:55:06 +010077 bool ValidateInputFile(const std::string& inputFileName)
78 {
79 if (inputFileName.empty())
80 {
81 std::cerr << "No input file name specified" << std::endl;
82 return false;
83 }
84
Francis Murtagh532a29d2020-06-29 11:50:01 +010085 if (!fs::exists(inputFileName))
Sadik Armagan8271f812019-04-19 09:55:06 +010086 {
87 std::cerr << "Input file [" << inputFileName << "] does not exist" << std::endl;
88 return false;
89 }
90
Francis Murtagh532a29d2020-06-29 11:50:01 +010091 if (fs::is_directory(inputFileName))
Sadik Armagan8271f812019-04-19 09:55:06 +010092 {
93 std::cerr << "Input file [" << inputFileName << "] is a directory" << std::endl;
94 return false;
95 }
96
97 return true;
98 }
99
100 bool ValidateLayout(const std::string& layout)
101 {
102 if (layout.empty())
103 {
104 std::cerr << "No layout specified" << std::endl;
105 return false;
106 }
107
SiCong Li39f46392019-06-21 12:00:04 +0100108 std::vector<std::string> supportedLayouts = { "NHWC", "NCHW" };
Sadik Armagan8271f812019-04-19 09:55:06 +0100109
110 auto iterator = std::find(supportedLayouts.begin(), supportedLayouts.end(), layout);
111 if (iterator == supportedLayouts.end())
112 {
113 std::cerr << "Layout [" << layout << "] is not supported" << std::endl;
114 return false;
115 }
116
117 return true;
118 }
119
Matthew Sloyanbf88b6f2020-09-25 17:22:00 +0100120 bool ValidateOutputFile(const std::string& outputFileName)
Sadik Armagan8271f812019-04-19 09:55:06 +0100121 {
122 if (outputFileName.empty())
123 {
124 std::cerr << "No output file name specified" << std::endl;
125 return false;
126 }
127
Francis Murtagh532a29d2020-06-29 11:50:01 +0100128 if (fs::exists(outputFileName))
Sadik Armagan8271f812019-04-19 09:55:06 +0100129 {
130 std::cerr << "Output file [" << outputFileName << "] already exists" << std::endl;
131 return false;
132 }
133
Francis Murtagh532a29d2020-06-29 11:50:01 +0100134 if (fs::is_directory(outputFileName))
Sadik Armagan8271f812019-04-19 09:55:06 +0100135 {
136 std::cerr << "Output file [" << outputFileName << "] is a directory" << std::endl;
137 return false;
138 }
139
Francis Murtagh532a29d2020-06-29 11:50:01 +0100140 fs::path outputPath(outputFileName);
141 if (!fs::exists(outputPath.parent_path()))
Sadik Armagan8271f812019-04-19 09:55:06 +0100142 {
143 std::cerr << "Output directory [" << outputPath.parent_path().c_str() << "] does not exist" << std::endl;
144 return false;
145 }
146
147 return true;
148 }
149
150 bool ProcessCommandLine(int argc, char* argv[])
151 {
Matthew Sloyanbf88b6f2020-09-25 17:22:00 +0100152 cxxopts::Options options("ImageTensorGenerator",
153 "Program for pre-processing a .jpg image "
154 "before generating a .raw tensor file from it.");
Sadik Armagan8271f812019-04-19 09:55:06 +0100155
Sadik Armagan8271f812019-04-19 09:55:06 +0100156 try
157 {
Matthew Sloyanbf88b6f2020-09-25 17:22:00 +0100158 options.add_options()
159 ("h,help", "Display help messages")
160 ("i,infile",
161 "Input image file to generate tensor from",
162 cxxopts::value<std::string>(m_InputFileName))
163 ("f,model-format",
164 "Format of the intended model file that uses the images."
165 "Different formats have different image normalization styles."
Mike Kellyd7ed6d42021-07-21 09:42:43 +0100166 "If unset, defaults to tflite."
Kevin Mayeb03e0f2021-04-28 16:16:22 +0100167 "Accepted value (tflite)",
Mike Kellyd7ed6d42021-07-21 09:42:43 +0100168 cxxopts::value<std::string>(m_ModelFormat)->default_value("tflite"))
Matthew Sloyanbf88b6f2020-09-25 17:22:00 +0100169 ("o,outfile",
170 "Output raw tensor file path",
171 cxxopts::value<std::string>(m_OutputFileName))
172 ("z,output-type",
173 "The data type of the output tensors."
174 "If unset, defaults to \"float\" for all defined inputs. "
Mike Kellyd7ed6d42021-07-21 09:42:43 +0100175 "Accepted values (float, int, qasymms8 or qasymmu8)",
Matthew Sloyanbf88b6f2020-09-25 17:22:00 +0100176 cxxopts::value<std::string>(m_OutputType)->default_value("float"))
177 ("new-width",
178 "Resize image to new width. Keep original width if unspecified",
179 cxxopts::value<std::string>(m_NewWidth)->default_value("0"))
180 ("new-height",
181 "Resize image to new height. Keep original height if unspecified",
182 cxxopts::value<std::string>(m_NewHeight)->default_value("0"))
183 ("l,layout",
184 "Output data layout, \"NHWC\" or \"NCHW\", default value NHWC",
185 cxxopts::value<std::string>(m_Layout)->default_value("NHWC"));
Sadik Armagan8271f812019-04-19 09:55:06 +0100186 }
187 catch (const std::exception& e)
188 {
Matthew Sloyanbf88b6f2020-09-25 17:22:00 +0100189 std::cerr << options.help() << std::endl;
Sadik Armagan8271f812019-04-19 09:55:06 +0100190 return false;
191 }
192
Sadik Armagan8271f812019-04-19 09:55:06 +0100193 try
194 {
Matthew Sloyanbf88b6f2020-09-25 17:22:00 +0100195 auto result = options.parse(argc, argv);
Sadik Armagan8271f812019-04-19 09:55:06 +0100196
Matthew Sloyanbf88b6f2020-09-25 17:22:00 +0100197 if (result.count("help"))
Sadik Armagan8271f812019-04-19 09:55:06 +0100198 {
Matthew Sloyanbf88b6f2020-09-25 17:22:00 +0100199 std::cout << options.help() << std::endl;
Sadik Armagan8271f812019-04-19 09:55:06 +0100200 return false;
201 }
202
Matthew Sloyanbf88b6f2020-09-25 17:22:00 +0100203 // Check for mandatory parameters and validate inputs
204 if(!ParseOptions(result)){
205 return false;
206 }
Sadik Armagan8271f812019-04-19 09:55:06 +0100207 }
Jim Flynn357add22023-04-10 23:26:40 +0100208 catch (const cxxopts::exceptions::exception& e)
Sadik Armagan8271f812019-04-19 09:55:06 +0100209 {
210 std::cerr << e.what() << std::endl << std::endl;
Sadik Armagan8271f812019-04-19 09:55:06 +0100211 return false;
212 }
213
214 return true;
215 }
216
217 std::string GetInputFileName() {return m_InputFileName;}
SiCong Li39f46392019-06-21 12:00:04 +0100218 armnn::DataLayout GetLayout()
219 {
220 if (m_Layout == "NHWC")
221 {
222 return armnn::DataLayout::NHWC;
223 }
224 else if (m_Layout == "NCHW")
225 {
226 return armnn::DataLayout::NCHW;
227 }
228 else
229 {
230 throw armnn::Exception("Unsupported data layout: " + m_Layout);
231 }
232 }
Sadik Armagan8271f812019-04-19 09:55:06 +0100233 std::string GetOutputFileName() {return m_OutputFileName;}
SiCong Li39f46392019-06-21 12:00:04 +0100234 unsigned int GetNewWidth() {return static_cast<unsigned int>(std::stoi(m_NewWidth));}
235 unsigned int GetNewHeight() {return static_cast<unsigned int>(std::stoi(m_NewHeight));}
236 SupportedFrontend GetModelFormat()
237 {
Kevin Mayeb03e0f2021-04-28 16:16:22 +0100238 if (m_ModelFormat == "tflite")
SiCong Li39f46392019-06-21 12:00:04 +0100239 {
240 return SupportedFrontend::TFLite;
241 }
242 else
243 {
244 throw armnn::Exception("Unsupported model format" + m_ModelFormat);
245 }
246 }
247 armnn::DataType GetOutputType()
248 {
249 if (m_OutputType == "float")
250 {
251 return armnn::DataType::Float32;
252 }
253 else if (m_OutputType == "int")
254 {
255 return armnn::DataType::Signed32;
256 }
Mike Kellyd7ed6d42021-07-21 09:42:43 +0100257 else if (m_OutputType == "qasymm8" || m_OutputType == "qasymmu8")
SiCong Li39f46392019-06-21 12:00:04 +0100258 {
Derek Lambertif90c56d2020-01-10 17:14:08 +0000259 return armnn::DataType::QAsymmU8;
SiCong Li39f46392019-06-21 12:00:04 +0100260 }
Mike Kellyd7ed6d42021-07-21 09:42:43 +0100261 else if (m_OutputType == "qasymms8")
262 {
263 return armnn::DataType::QAsymmS8;
264 }
SiCong Li39f46392019-06-21 12:00:04 +0100265 else
266 {
Colm Donelan194086f2022-11-14 17:23:07 +0000267 throw armnn::Exception("Unsupported input type " + m_OutputType);
SiCong Li39f46392019-06-21 12:00:04 +0100268 }
269 }
Sadik Armagan8271f812019-04-19 09:55:06 +0100270
271private:
272 std::string m_InputFileName;
273 std::string m_Layout;
274 std::string m_OutputFileName;
SiCong Li39f46392019-06-21 12:00:04 +0100275 std::string m_NewWidth;
276 std::string m_NewHeight;
277 std::string m_ModelFormat;
278 std::string m_OutputType;
Sadik Armagan8271f812019-04-19 09:55:06 +0100279};
280
281} // namespace anonymous
282
283int main(int argc, char* argv[])
284{
285 CommandLineProcessor cmdline;
286 if (!cmdline.ProcessCommandLine(argc, argv))
287 {
288 return -1;
289 }
Sadik Armagan8271f812019-04-19 09:55:06 +0100290 const std::string imagePath(cmdline.GetInputFileName());
291 const std::string outputPath(cmdline.GetOutputFileName());
SiCong Li39f46392019-06-21 12:00:04 +0100292 const SupportedFrontend& modelFormat(cmdline.GetModelFormat());
293 const armnn::DataType outputType(cmdline.GetOutputType());
294 const unsigned int newWidth = cmdline.GetNewWidth();
295 const unsigned int newHeight = cmdline.GetNewHeight();
296 const unsigned int batchSize = 1;
297 const armnn::DataLayout outputLayout(cmdline.GetLayout());
Sadik Armagan8271f812019-04-19 09:55:06 +0100298
Francis Murtagh40d27412021-10-28 11:11:35 +0100299 std::vector<armnnUtils::TContainer> imageDataContainers;
SiCong Li39f46392019-06-21 12:00:04 +0100300 const NormalizationParameters& normParams = GetNormalizationParameters(modelFormat, outputType);
Sadik Armagan8271f812019-04-19 09:55:06 +0100301 try
302 {
SiCong Li39f46392019-06-21 12:00:04 +0100303 switch (outputType)
304 {
305 case armnn::DataType::Signed32:
306 imageDataContainers.push_back(PrepareImageTensor<int>(
307 imagePath, newWidth, newHeight, normParams, batchSize, outputLayout));
308 break;
Derek Lambertif90c56d2020-01-10 17:14:08 +0000309 case armnn::DataType::QAsymmU8:
SiCong Li39f46392019-06-21 12:00:04 +0100310 imageDataContainers.push_back(PrepareImageTensor<uint8_t>(
311 imagePath, newWidth, newHeight, normParams, batchSize, outputLayout));
312 break;
Mike Kellyd7ed6d42021-07-21 09:42:43 +0100313 case armnn::DataType::QAsymmS8:
314 imageDataContainers.push_back(PrepareImageTensor<int8_t>(
315 imagePath, newWidth, newHeight, normParams, batchSize, outputLayout));
316 break;
SiCong Li39f46392019-06-21 12:00:04 +0100317 case armnn::DataType::Float32:
318 default:
319 imageDataContainers.push_back(PrepareImageTensor<float>(
320 imagePath, newWidth, newHeight, normParams, batchSize, outputLayout));
321 break;
322 }
Sadik Armagan8271f812019-04-19 09:55:06 +0100323 }
324 catch (const InferenceTestImageException& e)
325 {
Derek Lamberti08446972019-11-26 16:38:31 +0000326 ARMNN_LOG(fatal) << "Failed to load image file " << imagePath << " with error: " << e.what();
Sadik Armagan8271f812019-04-19 09:55:06 +0100327 return -1;
328 }
329
330 std::ofstream imageTensorFile;
331 imageTensorFile.open(outputPath, std::ofstream::out);
332 if (imageTensorFile.is_open())
333 {
James Ward6d9f5c52020-09-28 11:56:35 +0100334 mapbox::util::apply_visitor(
335 [&imageTensorFile](auto&& imageData){ WriteImageTensorImpl(imageData,imageTensorFile); },
336 imageDataContainers[0]
337 );
338
Sadik Armagan8271f812019-04-19 09:55:06 +0100339 if (!imageTensorFile)
340 {
Derek Lamberti08446972019-11-26 16:38:31 +0000341 ARMNN_LOG(fatal) << "Failed to write to output file" << outputPath;
Sadik Armagan8271f812019-04-19 09:55:06 +0100342 imageTensorFile.close();
343 return -1;
344 }
345 imageTensorFile.close();
346 }
347 else
348 {
Derek Lamberti08446972019-11-26 16:38:31 +0000349 ARMNN_LOG(fatal) << "Failed to open output file" << outputPath;
Sadik Armagan8271f812019-04-19 09:55:06 +0100350 return -1;
351 }
352
353 return 0;
SiCong Li39f46392019-06-21 12:00:04 +0100354}