blob: 568ba1ee9532ecd5027020f9bbb41d36bc9af21a [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>
Francis Murtagh532a29d2020-06-29 11:50:01 +010010#include <Filesystem.hpp>
Sadik Armagan8271f812019-04-19 09:55:06 +010011
Sadik Armagan8271f812019-04-19 09:55:06 +010012#include <boost/program_options.hpp>
SiCong Li39f46392019-06-21 12:00:04 +010013#include <boost/variant.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:
30 bool ValidateInputFile(const std::string& inputFileName)
31 {
32 if (inputFileName.empty())
33 {
34 std::cerr << "No input file name specified" << std::endl;
35 return false;
36 }
37
Francis Murtagh532a29d2020-06-29 11:50:01 +010038 if (!fs::exists(inputFileName))
Sadik Armagan8271f812019-04-19 09:55:06 +010039 {
40 std::cerr << "Input file [" << inputFileName << "] does not exist" << std::endl;
41 return false;
42 }
43
Francis Murtagh532a29d2020-06-29 11:50:01 +010044 if (fs::is_directory(inputFileName))
Sadik Armagan8271f812019-04-19 09:55:06 +010045 {
46 std::cerr << "Input file [" << inputFileName << "] is a directory" << std::endl;
47 return false;
48 }
49
50 return true;
51 }
52
53 bool ValidateLayout(const std::string& layout)
54 {
55 if (layout.empty())
56 {
57 std::cerr << "No layout specified" << std::endl;
58 return false;
59 }
60
SiCong Li39f46392019-06-21 12:00:04 +010061 std::vector<std::string> supportedLayouts = { "NHWC", "NCHW" };
Sadik Armagan8271f812019-04-19 09:55:06 +010062
63 auto iterator = std::find(supportedLayouts.begin(), supportedLayouts.end(), layout);
64 if (iterator == supportedLayouts.end())
65 {
66 std::cerr << "Layout [" << layout << "] is not supported" << std::endl;
67 return false;
68 }
69
70 return true;
71 }
72
73 bool ValidateOutputFile(std::string& outputFileName)
74 {
75 if (outputFileName.empty())
76 {
77 std::cerr << "No output file name specified" << std::endl;
78 return false;
79 }
80
Francis Murtagh532a29d2020-06-29 11:50:01 +010081 if (fs::exists(outputFileName))
Sadik Armagan8271f812019-04-19 09:55:06 +010082 {
83 std::cerr << "Output file [" << outputFileName << "] already exists" << std::endl;
84 return false;
85 }
86
Francis Murtagh532a29d2020-06-29 11:50:01 +010087 if (fs::is_directory(outputFileName))
Sadik Armagan8271f812019-04-19 09:55:06 +010088 {
89 std::cerr << "Output file [" << outputFileName << "] is a directory" << std::endl;
90 return false;
91 }
92
Francis Murtagh532a29d2020-06-29 11:50:01 +010093 fs::path outputPath(outputFileName);
94 if (!fs::exists(outputPath.parent_path()))
Sadik Armagan8271f812019-04-19 09:55:06 +010095 {
96 std::cerr << "Output directory [" << outputPath.parent_path().c_str() << "] does not exist" << std::endl;
97 return false;
98 }
99
100 return true;
101 }
102
103 bool ProcessCommandLine(int argc, char* argv[])
104 {
105 namespace po = boost::program_options;
106
107 po::options_description desc("Options");
108 try
109 {
110 desc.add_options()
111 ("help,h", "Display help messages")
112 ("infile,i", po::value<std::string>(&m_InputFileName)->required(),
113 "Input image file to generate tensor from")
SiCong Li39f46392019-06-21 12:00:04 +0100114 ("model-format,f", po::value<std::string>(&m_ModelFormat)->required(),
SiCong Li90bb7ab2019-07-11 12:02:45 +0100115 "Format of the intended model file that uses the images."
116 "Different formats have different image normalization styles."
117 "Accepted values (caffe, tensorflow, tflite)")
Sadik Armagan8271f812019-04-19 09:55:06 +0100118 ("outfile,o", po::value<std::string>(&m_OutputFileName)->required(),
SiCong Li39f46392019-06-21 12:00:04 +0100119 "Output raw tensor file path")
120 ("output-type,z", po::value<std::string>(&m_OutputType)->default_value("float"),
121 "The data type of the output tensors."
122 "If unset, defaults to \"float\" for all defined inputs. "
123 "Accepted values (float, int or qasymm8)")
SiCong Li90bb7ab2019-07-11 12:02:45 +0100124 ("new-width", po::value<std::string>(&m_NewWidth)->default_value("0"),
SiCong Li39f46392019-06-21 12:00:04 +0100125 "Resize image to new width. Keep original width if unspecified")
SiCong Li90bb7ab2019-07-11 12:02:45 +0100126 ("new-height", po::value<std::string>(&m_NewHeight)->default_value("0"),
SiCong Li39f46392019-06-21 12:00:04 +0100127 "Resize image to new height. Keep original height if unspecified")
128 ("layout,l", po::value<std::string>(&m_Layout)->default_value("NHWC"),
129 "Output data layout, \"NHWC\" or \"NCHW\", default value NHWC");
Sadik Armagan8271f812019-04-19 09:55:06 +0100130 }
131 catch (const std::exception& e)
132 {
133 std::cerr << "Fatal internal error: [" << e.what() << "]" << std::endl;
134 return false;
135 }
136
137 po::variables_map vm;
138
139 try
140 {
141 po::store(po::parse_command_line(argc, argv, desc), vm);
142
143 if (vm.count("help"))
144 {
145 std::cout << desc << std::endl;
146 return false;
147 }
148
149 po::notify(vm);
150 }
151 catch (const po::error& e)
152 {
153 std::cerr << e.what() << std::endl << std::endl;
154 std::cerr << desc << std::endl;
155 return false;
156 }
157
158 if (!ValidateInputFile(m_InputFileName))
159 {
160 return false;
161 }
162
163 if (!ValidateLayout(m_Layout))
164 {
165 return false;
166 }
167
168 if (!ValidateOutputFile(m_OutputFileName))
169 {
170 return false;
171 }
172
173 return true;
174 }
175
176 std::string GetInputFileName() {return m_InputFileName;}
SiCong Li39f46392019-06-21 12:00:04 +0100177 armnn::DataLayout GetLayout()
178 {
179 if (m_Layout == "NHWC")
180 {
181 return armnn::DataLayout::NHWC;
182 }
183 else if (m_Layout == "NCHW")
184 {
185 return armnn::DataLayout::NCHW;
186 }
187 else
188 {
189 throw armnn::Exception("Unsupported data layout: " + m_Layout);
190 }
191 }
Sadik Armagan8271f812019-04-19 09:55:06 +0100192 std::string GetOutputFileName() {return m_OutputFileName;}
SiCong Li39f46392019-06-21 12:00:04 +0100193 unsigned int GetNewWidth() {return static_cast<unsigned int>(std::stoi(m_NewWidth));}
194 unsigned int GetNewHeight() {return static_cast<unsigned int>(std::stoi(m_NewHeight));}
195 SupportedFrontend GetModelFormat()
196 {
197 if (m_ModelFormat == "caffe")
198 {
199 return SupportedFrontend::Caffe;
200 }
201 else if (m_ModelFormat == "tensorflow")
202 {
203 return SupportedFrontend::TensorFlow;
204 }
205 else if (m_ModelFormat == "tflite")
206 {
207 return SupportedFrontend::TFLite;
208 }
209 else
210 {
211 throw armnn::Exception("Unsupported model format" + m_ModelFormat);
212 }
213 }
214 armnn::DataType GetOutputType()
215 {
216 if (m_OutputType == "float")
217 {
218 return armnn::DataType::Float32;
219 }
220 else if (m_OutputType == "int")
221 {
222 return armnn::DataType::Signed32;
223 }
224 else if (m_OutputType == "qasymm8")
225 {
Derek Lambertif90c56d2020-01-10 17:14:08 +0000226 return armnn::DataType::QAsymmU8;
SiCong Li39f46392019-06-21 12:00:04 +0100227 }
228 else
229 {
230 throw armnn::Exception("Unsupported input type" + m_OutputType);
231 }
232 }
Sadik Armagan8271f812019-04-19 09:55:06 +0100233
234private:
235 std::string m_InputFileName;
236 std::string m_Layout;
237 std::string m_OutputFileName;
SiCong Li39f46392019-06-21 12:00:04 +0100238 std::string m_NewWidth;
239 std::string m_NewHeight;
240 std::string m_ModelFormat;
241 std::string m_OutputType;
Sadik Armagan8271f812019-04-19 09:55:06 +0100242};
243
244} // namespace anonymous
245
246int main(int argc, char* argv[])
247{
248 CommandLineProcessor cmdline;
249 if (!cmdline.ProcessCommandLine(argc, argv))
250 {
251 return -1;
252 }
Sadik Armagan8271f812019-04-19 09:55:06 +0100253 const std::string imagePath(cmdline.GetInputFileName());
254 const std::string outputPath(cmdline.GetOutputFileName());
SiCong Li39f46392019-06-21 12:00:04 +0100255 const SupportedFrontend& modelFormat(cmdline.GetModelFormat());
256 const armnn::DataType outputType(cmdline.GetOutputType());
257 const unsigned int newWidth = cmdline.GetNewWidth();
258 const unsigned int newHeight = cmdline.GetNewHeight();
259 const unsigned int batchSize = 1;
260 const armnn::DataLayout outputLayout(cmdline.GetLayout());
Sadik Armagan8271f812019-04-19 09:55:06 +0100261
SiCong Li39f46392019-06-21 12:00:04 +0100262 using TContainer = boost::variant<std::vector<float>, std::vector<int>, std::vector<uint8_t>>;
263 std::vector<TContainer> imageDataContainers;
264 const NormalizationParameters& normParams = GetNormalizationParameters(modelFormat, outputType);
Sadik Armagan8271f812019-04-19 09:55:06 +0100265 try
266 {
SiCong Li39f46392019-06-21 12:00:04 +0100267 switch (outputType)
268 {
269 case armnn::DataType::Signed32:
270 imageDataContainers.push_back(PrepareImageTensor<int>(
271 imagePath, newWidth, newHeight, normParams, batchSize, outputLayout));
272 break;
Derek Lambertif90c56d2020-01-10 17:14:08 +0000273 case armnn::DataType::QAsymmU8:
SiCong Li39f46392019-06-21 12:00:04 +0100274 imageDataContainers.push_back(PrepareImageTensor<uint8_t>(
275 imagePath, newWidth, newHeight, normParams, batchSize, outputLayout));
276 break;
277 case armnn::DataType::Float32:
278 default:
279 imageDataContainers.push_back(PrepareImageTensor<float>(
280 imagePath, newWidth, newHeight, normParams, batchSize, outputLayout));
281 break;
282 }
Sadik Armagan8271f812019-04-19 09:55:06 +0100283 }
284 catch (const InferenceTestImageException& e)
285 {
Derek Lamberti08446972019-11-26 16:38:31 +0000286 ARMNN_LOG(fatal) << "Failed to load image file " << imagePath << " with error: " << e.what();
Sadik Armagan8271f812019-04-19 09:55:06 +0100287 return -1;
288 }
289
290 std::ofstream imageTensorFile;
291 imageTensorFile.open(outputPath, std::ofstream::out);
292 if (imageTensorFile.is_open())
293 {
SiCong Li39f46392019-06-21 12:00:04 +0100294 boost::apply_visitor([&imageTensorFile](auto&& imageData) { WriteImageTensorImpl(imageData, imageTensorFile); },
295 imageDataContainers[0]);
Sadik Armagan8271f812019-04-19 09:55:06 +0100296 if (!imageTensorFile)
297 {
Derek Lamberti08446972019-11-26 16:38:31 +0000298 ARMNN_LOG(fatal) << "Failed to write to output file" << outputPath;
Sadik Armagan8271f812019-04-19 09:55:06 +0100299 imageTensorFile.close();
300 return -1;
301 }
302 imageTensorFile.close();
303 }
304 else
305 {
Derek Lamberti08446972019-11-26 16:38:31 +0000306 ARMNN_LOG(fatal) << "Failed to open output file" << outputPath;
Sadik Armagan8271f812019-04-19 09:55:06 +0100307 return -1;
308 }
309
310 return 0;
SiCong Li39f46392019-06-21 12:00:04 +0100311}