// // Copyright © 2017 Arm Ltd. All rights reserved. // SPDX-License-Identifier: MIT // #include "ImageTensorGenerator.hpp" #include "../InferenceTestImage.hpp" #include #include #include #include #include #include #include #include #include #include #include namespace { // parses the command line to extract // * the input image file -i the input image file path (must exist) // * the layout -l the data layout output generated with (optional - default value is NHWC) // * the output file -o the output raw tensor file path (must not already exist) class CommandLineProcessor { public: bool ValidateInputFile(const std::string& inputFileName) { if (inputFileName.empty()) { std::cerr << "No input file name specified" << std::endl; return false; } if (!boost::filesystem::exists(inputFileName)) { std::cerr << "Input file [" << inputFileName << "] does not exist" << std::endl; return false; } if (boost::filesystem::is_directory(inputFileName)) { std::cerr << "Input file [" << inputFileName << "] is a directory" << std::endl; return false; } return true; } bool ValidateLayout(const std::string& layout) { if (layout.empty()) { std::cerr << "No layout specified" << std::endl; return false; } std::vector supportedLayouts = { "NHWC", "NCHW" }; auto iterator = std::find(supportedLayouts.begin(), supportedLayouts.end(), layout); if (iterator == supportedLayouts.end()) { std::cerr << "Layout [" << layout << "] is not supported" << std::endl; return false; } return true; } bool ValidateOutputFile(std::string& outputFileName) { if (outputFileName.empty()) { std::cerr << "No output file name specified" << std::endl; return false; } if (boost::filesystem::exists(outputFileName)) { std::cerr << "Output file [" << outputFileName << "] already exists" << std::endl; return false; } if (boost::filesystem::is_directory(outputFileName)) { std::cerr << "Output file [" << outputFileName << "] is a directory" << std::endl; return false; } boost::filesystem::path outputPath(outputFileName); if (!boost::filesystem::exists(outputPath.parent_path())) { std::cerr << "Output directory [" << outputPath.parent_path().c_str() << "] does not exist" << std::endl; return false; } return true; } bool ProcessCommandLine(int argc, char* argv[]) { namespace po = boost::program_options; po::options_description desc("Options"); try { desc.add_options() ("help,h", "Display help messages") ("infile,i", po::value(&m_InputFileName)->required(), "Input image file to generate tensor from") ("model-format,f", po::value(&m_ModelFormat)->required(), "Format of the intended model file that uses the images." "Different formats have different image normalization styles." "Accepted values (caffe, tensorflow, tflite)") ("outfile,o", po::value(&m_OutputFileName)->required(), "Output raw tensor file path") ("output-type,z", po::value(&m_OutputType)->default_value("float"), "The data type of the output tensors." "If unset, defaults to \"float\" for all defined inputs. " "Accepted values (float, int or qasymm8)") ("new-width", po::value(&m_NewWidth)->default_value("0"), "Resize image to new width. Keep original width if unspecified") ("new-height", po::value(&m_NewHeight)->default_value("0"), "Resize image to new height. Keep original height if unspecified") ("layout,l", po::value(&m_Layout)->default_value("NHWC"), "Output data layout, \"NHWC\" or \"NCHW\", default value NHWC"); } catch (const std::exception& e) { std::cerr << "Fatal internal error: [" << e.what() << "]" << std::endl; return false; } po::variables_map vm; try { po::store(po::parse_command_line(argc, argv, desc), vm); if (vm.count("help")) { std::cout << desc << std::endl; return false; } po::notify(vm); } catch (const po::error& e) { std::cerr << e.what() << std::endl << std::endl; std::cerr << desc << std::endl; return false; } if (!ValidateInputFile(m_InputFileName)) { return false; } if (!ValidateLayout(m_Layout)) { return false; } if (!ValidateOutputFile(m_OutputFileName)) { return false; } return true; } std::string GetInputFileName() {return m_InputFileName;} armnn::DataLayout GetLayout() { if (m_Layout == "NHWC") { return armnn::DataLayout::NHWC; } else if (m_Layout == "NCHW") { return armnn::DataLayout::NCHW; } else { throw armnn::Exception("Unsupported data layout: " + m_Layout); } } std::string GetOutputFileName() {return m_OutputFileName;} unsigned int GetNewWidth() {return static_cast(std::stoi(m_NewWidth));} unsigned int GetNewHeight() {return static_cast(std::stoi(m_NewHeight));} SupportedFrontend GetModelFormat() { if (m_ModelFormat == "caffe") { return SupportedFrontend::Caffe; } else if (m_ModelFormat == "tensorflow") { return SupportedFrontend::TensorFlow; } else if (m_ModelFormat == "tflite") { return SupportedFrontend::TFLite; } else { throw armnn::Exception("Unsupported model format" + m_ModelFormat); } } armnn::DataType GetOutputType() { if (m_OutputType == "float") { return armnn::DataType::Float32; } else if (m_OutputType == "int") { return armnn::DataType::Signed32; } else if (m_OutputType == "qasymm8") { return armnn::DataType::QuantisedAsymm8; } else { throw armnn::Exception("Unsupported input type" + m_OutputType); } } private: std::string m_InputFileName; std::string m_Layout; std::string m_OutputFileName; std::string m_NewWidth; std::string m_NewHeight; std::string m_ModelFormat; std::string m_OutputType; }; } // namespace anonymous int main(int argc, char* argv[]) { CommandLineProcessor cmdline; if (!cmdline.ProcessCommandLine(argc, argv)) { return -1; } const std::string imagePath(cmdline.GetInputFileName()); const std::string outputPath(cmdline.GetOutputFileName()); const SupportedFrontend& modelFormat(cmdline.GetModelFormat()); const armnn::DataType outputType(cmdline.GetOutputType()); const unsigned int newWidth = cmdline.GetNewWidth(); const unsigned int newHeight = cmdline.GetNewHeight(); const unsigned int batchSize = 1; const armnn::DataLayout outputLayout(cmdline.GetLayout()); using TContainer = boost::variant, std::vector, std::vector>; std::vector imageDataContainers; const NormalizationParameters& normParams = GetNormalizationParameters(modelFormat, outputType); try { switch (outputType) { case armnn::DataType::Signed32: imageDataContainers.push_back(PrepareImageTensor( imagePath, newWidth, newHeight, normParams, batchSize, outputLayout)); break; case armnn::DataType::QuantisedAsymm8: imageDataContainers.push_back(PrepareImageTensor( imagePath, newWidth, newHeight, normParams, batchSize, outputLayout)); break; case armnn::DataType::Float32: default: imageDataContainers.push_back(PrepareImageTensor( imagePath, newWidth, newHeight, normParams, batchSize, outputLayout)); break; } } catch (const InferenceTestImageException& e) { BOOST_LOG_TRIVIAL(fatal) << "Failed to load image file " << imagePath << " with error: " << e.what(); return -1; } std::ofstream imageTensorFile; imageTensorFile.open(outputPath, std::ofstream::out); if (imageTensorFile.is_open()) { boost::apply_visitor([&imageTensorFile](auto&& imageData) { WriteImageTensorImpl(imageData, imageTensorFile); }, imageDataContainers[0]); if (!imageTensorFile) { BOOST_LOG_TRIVIAL(fatal) << "Failed to write to output file" << outputPath; imageTensorFile.close(); return -1; } imageTensorFile.close(); } else { BOOST_LOG_TRIVIAL(fatal) << "Failed to open output file" << outputPath; return -1; } return 0; }