blob: a2110f9cf3f87c3268515aa653ef7e45623fa07b [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."
Kevin Mayeb03e0f2021-04-28 16:16:22 +0100167 "Accepted value (tflite)",
Matthew Sloyanbf88b6f2020-09-25 17:22:00 +0100168 cxxopts::value<std::string>(m_ModelFormat))
169 ("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. "
175 "Accepted values (float, int or qasymm8)",
176 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 }
Matthew Sloyanbf88b6f2020-09-25 17:22:00 +0100208 catch (const cxxopts::OptionException& 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 }
257 else if (m_OutputType == "qasymm8")
258 {
Derek Lambertif90c56d2020-01-10 17:14:08 +0000259 return armnn::DataType::QAsymmU8;
SiCong Li39f46392019-06-21 12:00:04 +0100260 }
261 else
262 {
263 throw armnn::Exception("Unsupported input type" + m_OutputType);
264 }
265 }
Sadik Armagan8271f812019-04-19 09:55:06 +0100266
267private:
268 std::string m_InputFileName;
269 std::string m_Layout;
270 std::string m_OutputFileName;
SiCong Li39f46392019-06-21 12:00:04 +0100271 std::string m_NewWidth;
272 std::string m_NewHeight;
273 std::string m_ModelFormat;
274 std::string m_OutputType;
Sadik Armagan8271f812019-04-19 09:55:06 +0100275};
276
277} // namespace anonymous
278
279int main(int argc, char* argv[])
280{
281 CommandLineProcessor cmdline;
282 if (!cmdline.ProcessCommandLine(argc, argv))
283 {
284 return -1;
285 }
Sadik Armagan8271f812019-04-19 09:55:06 +0100286 const std::string imagePath(cmdline.GetInputFileName());
287 const std::string outputPath(cmdline.GetOutputFileName());
SiCong Li39f46392019-06-21 12:00:04 +0100288 const SupportedFrontend& modelFormat(cmdline.GetModelFormat());
289 const armnn::DataType outputType(cmdline.GetOutputType());
290 const unsigned int newWidth = cmdline.GetNewWidth();
291 const unsigned int newHeight = cmdline.GetNewHeight();
292 const unsigned int batchSize = 1;
293 const armnn::DataLayout outputLayout(cmdline.GetLayout());
Sadik Armagan8271f812019-04-19 09:55:06 +0100294
James Ward6d9f5c52020-09-28 11:56:35 +0100295 using TContainer = mapbox::util::variant<std::vector<float>, std::vector<int>, std::vector<uint8_t>>;
SiCong Li39f46392019-06-21 12:00:04 +0100296 std::vector<TContainer> imageDataContainers;
297 const NormalizationParameters& normParams = GetNormalizationParameters(modelFormat, outputType);
Sadik Armagan8271f812019-04-19 09:55:06 +0100298 try
299 {
SiCong Li39f46392019-06-21 12:00:04 +0100300 switch (outputType)
301 {
302 case armnn::DataType::Signed32:
303 imageDataContainers.push_back(PrepareImageTensor<int>(
304 imagePath, newWidth, newHeight, normParams, batchSize, outputLayout));
305 break;
Derek Lambertif90c56d2020-01-10 17:14:08 +0000306 case armnn::DataType::QAsymmU8:
SiCong Li39f46392019-06-21 12:00:04 +0100307 imageDataContainers.push_back(PrepareImageTensor<uint8_t>(
308 imagePath, newWidth, newHeight, normParams, batchSize, outputLayout));
309 break;
310 case armnn::DataType::Float32:
311 default:
312 imageDataContainers.push_back(PrepareImageTensor<float>(
313 imagePath, newWidth, newHeight, normParams, batchSize, outputLayout));
314 break;
315 }
Sadik Armagan8271f812019-04-19 09:55:06 +0100316 }
317 catch (const InferenceTestImageException& e)
318 {
Derek Lamberti08446972019-11-26 16:38:31 +0000319 ARMNN_LOG(fatal) << "Failed to load image file " << imagePath << " with error: " << e.what();
Sadik Armagan8271f812019-04-19 09:55:06 +0100320 return -1;
321 }
322
323 std::ofstream imageTensorFile;
324 imageTensorFile.open(outputPath, std::ofstream::out);
325 if (imageTensorFile.is_open())
326 {
James Ward6d9f5c52020-09-28 11:56:35 +0100327 mapbox::util::apply_visitor(
328 [&imageTensorFile](auto&& imageData){ WriteImageTensorImpl(imageData,imageTensorFile); },
329 imageDataContainers[0]
330 );
331
Sadik Armagan8271f812019-04-19 09:55:06 +0100332 if (!imageTensorFile)
333 {
Derek Lamberti08446972019-11-26 16:38:31 +0000334 ARMNN_LOG(fatal) << "Failed to write to output file" << outputPath;
Sadik Armagan8271f812019-04-19 09:55:06 +0100335 imageTensorFile.close();
336 return -1;
337 }
338 imageTensorFile.close();
339 }
340 else
341 {
Derek Lamberti08446972019-11-26 16:38:31 +0000342 ARMNN_LOG(fatal) << "Failed to open output file" << outputPath;
Sadik Armagan8271f812019-04-19 09:55:06 +0100343 return -1;
344 }
345
346 return 0;
SiCong Li39f46392019-06-21 12:00:04 +0100347}