diff options
author | Colm Donelan <Colm.Donelan@arm.com> | 2020-10-06 15:15:33 +0100 |
---|---|---|
committer | Colm Donelan <Colm.Donelan@arm.com> | 2020-10-06 15:26:43 +0100 |
commit | b524ca0303ccaf452c3d59d66fa5cbb12975f4a8 (patch) | |
tree | fb0a7db3a7bc4e5a3ed550d79012329454bbc62a | |
parent | 9e51cdefb1f6f99b2bf36529d382595cde1f443c (diff) | |
download | armnn-b524ca0303ccaf452c3d59d66fa5cbb12975f4a8.tar.gz |
IVGCVSW-5213 Switch armnnConverter over to cxxopts
* Swap out boost::program_options with cxxopts.
* Remove const from argv declaration to conform to cxxopts.
* Clean up CLI handling around multiple inputs and shapes.
Each input must be specified with its own -i param.
Each corresponding -s parameter must be specified too.
Signed-off-by: Colm Donelan <Colm.Donelan@arm.com>
Change-Id: I01c2683dbca51b2322c98913731ac82a59e400a5
-rw-r--r-- | src/armnnConverter/ArmnnConverter.cpp | 175 |
1 files changed, 81 insertions, 94 deletions
diff --git a/src/armnnConverter/ArmnnConverter.cpp b/src/armnnConverter/ArmnnConverter.cpp index c8b9ba9f28..b5d3256c3c 100644 --- a/src/armnnConverter/ArmnnConverter.cpp +++ b/src/armnnConverter/ArmnnConverter.cpp @@ -22,10 +22,18 @@ #include <HeapProfiling.hpp> #include <armnn/utility/NumericCast.hpp> -#include "armnn/utility/StringUtils.hpp" +#include <armnn/utility/StringUtils.hpp> + +/* + * Historically we use the ',' character to separate dimensions in a tensor shape. However, cxxopts will read this + * an an array of values which is fine until we have multiple tensors specified. This lumps the values of all shapes + * together in a single array and we cannot break it up again. We'll change the vector delimiter to a '.'. We do this + * as close as possible to the usage of cxxopts to avoid polluting other possible uses. + */ +#define CXXOPTS_VECTOR_DELIMITER '.' +#include <cxxopts/cxxopts.hpp> #include <fmt/format.h> -#include <boost/program_options.hpp> #include <cstdlib> #include <fstream> @@ -34,8 +42,6 @@ namespace { -namespace po = boost::program_options; - armnn::TensorShape ParseTensorShape(std::istream& stream) { std::vector<unsigned int> result; @@ -63,46 +69,7 @@ armnn::TensorShape ParseTensorShape(std::istream& stream) return armnn::TensorShape(armnn::numeric_cast<unsigned int>(result.size()), result.data()); } -bool CheckOption(const po::variables_map& vm, - const char* option) -{ - if (option == nullptr) - { - return false; - } - - // Check whether 'option' is provided. - return vm.find(option) != vm.end(); -} - -void CheckOptionDependency(const po::variables_map& vm, - const char* option, - const char* required) -{ - if (option == nullptr || required == nullptr) - { - throw po::error("Invalid option to check dependency for"); - } - - // Check that if 'option' is provided, 'required' is also provided. - if (CheckOption(vm, option) && !vm[option].defaulted()) - { - if (CheckOption(vm, required) == 0 || vm[required].defaulted()) - { - throw po::error(std::string("Option '") + option + "' requires option '" + required + "'."); - } - } -} - -void CheckOptionDependencies(const po::variables_map& vm) -{ - CheckOptionDependency(vm, "model-path", "model-format"); - CheckOptionDependency(vm, "model-path", "input-name"); - CheckOptionDependency(vm, "model-path", "output-name"); - CheckOptionDependency(vm, "input-tensor-shape", "model-path"); -} - -int ParseCommandLineArgs(int argc, const char* argv[], +int ParseCommandLineArgs(int argc, char* argv[], std::string& modelFormat, std::string& modelPath, std::vector<std::string>& inputNames, @@ -110,64 +77,92 @@ int ParseCommandLineArgs(int argc, const char* argv[], std::vector<std::string>& outputNames, std::string& outputPath, bool& isModelBinary) { - po::options_description desc("Options"); - - desc.add_options() - ("help", "Display usage information") - ("model-format,f", po::value(&modelFormat)->required(),"Format of the model file" + cxxopts::Options options("ArmNNConverter", "Convert a neural network model from provided file to ArmNN format."); + try + { + std::string modelFormatDescription("Format of the model file"); #if defined(ARMNN_CAFFE_PARSER) - ", caffe-binary, caffe-text" + modelFormatDescription += ", caffe-binary, caffe-text"; #endif #if defined(ARMNN_ONNX_PARSER) - ", onnx-binary, onnx-text" + modelFormatDescription += ", onnx-binary, onnx-text"; #endif #if defined(ARMNN_TF_PARSER) - ", tensorflow-binary, tensorflow-text" + modelFormatDescription += ", tensorflow-binary, tensorflow-text"; #endif #if defined(ARMNN_TF_LITE_PARSER) - ", tflite-binary" + modelFormatDescription += ", tflite-binary"; #endif - ".") - ("model-path,m", po::value(&modelPath)->required(), "Path to model file.") - ("input-name,i", po::value<std::vector<std::string>>()->multitoken(), - "Identifier of the input tensors in the network, separated by whitespace.") - ("input-tensor-shape,s", po::value<std::vector<std::string>>()->multitoken(), - "The shape of the input tensor in the network as a flat array of integers, separated by comma." - " Multiple shapes are separated by whitespace." - " This parameter is optional, depending on the network.") - ("output-name,o", po::value<std::vector<std::string>>()->multitoken(), - "Identifier of the output tensor in the network.") - ("output-path,p", po::value(&outputPath)->required(), "Path to serialize the network to."); - - po::variables_map vm; - try - { - po::store(po::parse_command_line(argc, argv, desc), vm); - - if (CheckOption(vm, "help") || argc <= 1) - { - std::cout << "Convert a neural network model from provided file to ArmNN format." << std::endl; - std::cout << std::endl; - std::cout << desc << std::endl; - exit(EXIT_SUCCESS); - } - po::notify(vm); + modelFormatDescription += "."; + options.add_options() + ("help", "Display usage information") + ("f,model-format", modelFormatDescription, cxxopts::value<std::string>(modelFormat)) + ("m,model-path", "Path to model file.", cxxopts::value<std::string>(modelPath)) + + ("i,input-name", "Identifier of the input tensors in the network. " + "Each input must be specified separately.", + cxxopts::value<std::vector<std::string>>(inputNames)) + ("s,input-tensor-shape", + "The shape of the input tensor in the network as a flat array of integers, " + "separated by comma. Each input shape must be specified separately after the input name. " + "This parameter is optional, depending on the network.", + cxxopts::value<std::vector<std::string>>(inputTensorShapeStrs)) + + ("o,output-name", "Identifier of the output tensor in the network.", + cxxopts::value<std::vector<std::string>>(outputNames)) + ("p,output-path", + "Path to serialize the network to.", cxxopts::value<std::string>(outputPath)); } - catch (const po::error& e) + catch (const std::exception& e) { - std::cerr << e.what() << std::endl << std::endl; - std::cerr << desc << std::endl; + std::cerr << e.what() << std::endl << options.help() << std::endl; return EXIT_FAILURE; } - try { - CheckOptionDependencies(vm); + cxxopts::ParseResult result = options.parse(argc, argv); + if (result.count("help")) + { + std::cerr << options.help() << std::endl; + return EXIT_SUCCESS; + } + // Check for mandatory single options. + std::string mandatorySingleParameters[] = { "model-format", "model-path", "output-name", "output-path" }; + bool somethingsMissing = false; + for (auto param : mandatorySingleParameters) + { + if (result.count(param) != 1) + { + std::cerr << "Parameter \'--" << param << "\' is required but missing." << std::endl; + somethingsMissing = true; + } + } + // Check at least one "input-name" option. + if (result.count("input-name") == 0) + { + std::cerr << "Parameter \'--" << "input-name" << "\' must be specified at least once." << std::endl; + somethingsMissing = true; + } + // If input-tensor-shape is specified then there must be a 1:1 match with input-name. + if (result.count("input-tensor-shape") > 0) + { + if (result.count("input-tensor-shape") != result.count("input-name")) + { + std::cerr << "When specifying \'input-tensor-shape\' a matching number of \'input-name\' parameters " + "must be specified." << std::endl; + somethingsMissing = true; + } + } + + if (somethingsMissing) + { + std::cerr << options.help() << std::endl; + return EXIT_FAILURE; + } } - catch (const po::error& e) + catch (const cxxopts::OptionException& e) { std::cerr << e.what() << std::endl << std::endl; - std::cerr << desc << std::endl; return EXIT_FAILURE; } @@ -185,14 +180,6 @@ int ParseCommandLineArgs(int argc, const char* argv[], return EXIT_FAILURE; } - if (!vm["input-tensor-shape"].empty()) - { - inputTensorShapeStrs = vm["input-tensor-shape"].as<std::vector<std::string>>(); - } - - inputNames = vm["input-name"].as<std::vector<std::string>>(); - outputNames = vm["output-name"].as<std::vector<std::string>>(); - return EXIT_SUCCESS; } @@ -346,7 +333,7 @@ private: } // anonymous namespace -int main(int argc, const char* argv[]) +int main(int argc, char* argv[]) { #if (!defined(ARMNN_CAFFE_PARSER) \ |