aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFrancis Murtagh <francis.murtagh@arm.com>2019-06-18 12:30:37 +0100
committerFrancis Murtagh <francis.murtagh@arm.com>2019-06-19 10:38:22 +0000
commitbee4bc944aa50782ff22cb4a31fbc611212a5e89 (patch)
tree94fe0195732c6bb3110f7920965e1656daddc9dd
parent0c64f461e071f281dab14d6b9dde85a0235d0c25 (diff)
downloadarmnn-bee4bc944aa50782ff22cb4a31fbc611212a5e89.tar.gz
IVGCVSW-3299 Add Uint8 Support to Model Accuracy Checker
* Seperate ExecuteNetwork main function into standalone application * Include NetworkExecutionUtils header and remove duplicate functions * Add uint8 and int32 support to ModelAccuracyChecker Change-Id: I5fb4bc147232f8388f37eea7db5130b04fd215d1 Signed-off-by: Francis Murtagh <francis.murtagh@arm.com>
-rw-r--r--tests/CMakeLists.txt15
-rw-r--r--tests/ExecuteNetwork/ExecuteNetwork.cpp672
-rw-r--r--tests/ModelAccuracyTool-Armnn/ModelAccuracyTool-Armnn.cpp108
-rw-r--r--tests/NetworkExecutionUtils/NetworkExecutionUtils.hpp676
4 files changed, 735 insertions, 736 deletions
diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt
index dfcf4b48e0..19612543b6 100644
--- a/tests/CMakeLists.txt
+++ b/tests/CMakeLists.txt
@@ -294,6 +294,21 @@ endif()
if(BUILD_ACCURACY_TOOL)
macro(AccuracyTool executorName)
target_link_libraries(${executorName} ${CMAKE_THREAD_LIBS_INIT})
+ if (BUILD_ARMNN_SERIALIZER)
+ target_link_libraries(${executorName} armnnSerializer)
+ endif()
+ if (BUILD_CAFFE_PARSER)
+ target_link_libraries(${executorName} armnnCaffeParser)
+ endif()
+ if (BUILD_TF_PARSER)
+ target_link_libraries(${executorName} armnnTfParser)
+ endif()
+ if (BUILD_TF_LITE_PARSER)
+ target_link_libraries(${executorName} armnnTfLiteParser)
+ endif()
+ if (BUILD_ONNX_PARSER)
+ target_link_libraries(${executorName} armnnOnnxParser)
+ endif()
if(OPENCL_LIBRARIES)
target_link_libraries(${executorName} ${OPENCL_LIBRARIES})
endif()
diff --git a/tests/ExecuteNetwork/ExecuteNetwork.cpp b/tests/ExecuteNetwork/ExecuteNetwork.cpp
index 8ca69d8292..60353dbf3f 100644
--- a/tests/ExecuteNetwork/ExecuteNetwork.cpp
+++ b/tests/ExecuteNetwork/ExecuteNetwork.cpp
@@ -2,678 +2,8 @@
// Copyright © 2017 Arm Ltd. All rights reserved.
// SPDX-License-Identifier: MIT
//
-#include <armnn/ArmNN.hpp>
-#include <armnn/TypesUtils.hpp>
-#if defined(ARMNN_SERIALIZER)
-#include "armnnDeserializer/IDeserializer.hpp"
-#endif
-#if defined(ARMNN_CAFFE_PARSER)
-#include "armnnCaffeParser/ICaffeParser.hpp"
-#endif
-#if defined(ARMNN_TF_PARSER)
-#include "armnnTfParser/ITfParser.hpp"
-#endif
-#if defined(ARMNN_TF_LITE_PARSER)
-#include "armnnTfLiteParser/ITfLiteParser.hpp"
-#endif
-#if defined(ARMNN_ONNX_PARSER)
-#include "armnnOnnxParser/IOnnxParser.hpp"
-#endif
-#include "CsvReader.hpp"
-#include "../InferenceTest.hpp"
-
-#include <Logging.hpp>
-#include <Profiling.hpp>
-
-#include <boost/algorithm/string/trim.hpp>
-#include <boost/algorithm/string/split.hpp>
-#include <boost/algorithm/string/classification.hpp>
-#include <boost/program_options.hpp>
-#include <boost/variant.hpp>
-
-#include <iostream>
-#include <fstream>
-#include <functional>
-#include <future>
-#include <algorithm>
-#include <iterator>
-
-namespace
-{
-
-// Configure boost::program_options for command-line parsing and validation.
-namespace po = boost::program_options;
-
-template<typename T, typename TParseElementFunc>
-std::vector<T> ParseArrayImpl(std::istream& stream, TParseElementFunc parseElementFunc, const char * chars = "\t ,:")
-{
- std::vector<T> result;
- // Processes line-by-line.
- std::string line;
- while (std::getline(stream, line))
- {
- std::vector<std::string> tokens;
- try
- {
- // Coverity fix: boost::split() may throw an exception of type boost::bad_function_call.
- boost::split(tokens, line, boost::algorithm::is_any_of(chars), boost::token_compress_on);
- }
- catch (const std::exception& e)
- {
- BOOST_LOG_TRIVIAL(error) << "An error occurred when splitting tokens: " << e.what();
- continue;
- }
- for (const std::string& token : tokens)
- {
- if (!token.empty()) // See https://stackoverflow.com/questions/10437406/
- {
- try
- {
- result.push_back(parseElementFunc(token));
- }
- catch (const std::exception&)
- {
- BOOST_LOG_TRIVIAL(error) << "'" << token << "' is not a valid number. It has been ignored.";
- }
- }
- }
- }
-
- return result;
-}
-
-bool CheckOption(const po::variables_map& vm,
- const char* option)
-{
- // Check that the given option is valid.
- 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)
-{
- // Check that the given options are valid.
- 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", "input-tensor-data");
- CheckOptionDependency(vm, "model-path", "output-name");
- CheckOptionDependency(vm, "input-tensor-shape", "model-path");
-}
-
-template<armnn::DataType NonQuantizedType>
-auto ParseDataArray(std::istream & stream);
-
-template<armnn::DataType QuantizedType>
-auto ParseDataArray(std::istream& stream,
- const float& quantizationScale,
- const int32_t& quantizationOffset);
-
-template<>
-auto ParseDataArray<armnn::DataType::Float32>(std::istream & stream)
-{
- return ParseArrayImpl<float>(stream, [](const std::string& s) { return std::stof(s); });
-}
-
-template<>
-auto ParseDataArray<armnn::DataType::Signed32>(std::istream & stream)
-{
- return ParseArrayImpl<int>(stream, [](const std::string & s) { return std::stoi(s); });
-}
-
-template<>
-auto ParseDataArray<armnn::DataType::QuantisedAsymm8>(std::istream& stream,
- const float& quantizationScale,
- const int32_t& quantizationOffset)
-{
- return ParseArrayImpl<uint8_t>(stream,
- [&quantizationScale, &quantizationOffset](const std::string & s)
- {
- return boost::numeric_cast<uint8_t>(
- armnn::Quantize<u_int8_t>(std::stof(s),
- quantizationScale,
- quantizationOffset));
- });
-}
-
-std::vector<unsigned int> ParseArray(std::istream& stream)
-{
- return ParseArrayImpl<unsigned int>(stream,
- [](const std::string& s) { return boost::numeric_cast<unsigned int>(std::stoi(s)); });
-}
-
-std::vector<std::string> ParseStringList(const std::string & inputString, const char * delimiter)
-{
- std::stringstream stream(inputString);
- return ParseArrayImpl<std::string>(stream, [](const std::string& s) { return boost::trim_copy(s); }, delimiter);
-}
-
-void RemoveDuplicateDevices(std::vector<armnn::BackendId>& computeDevices)
-{
- // Mark the duplicate devices as 'Undefined'.
- for (auto i = computeDevices.begin(); i != computeDevices.end(); ++i)
- {
- for (auto j = std::next(i); j != computeDevices.end(); ++j)
- {
- if (*j == *i)
- {
- *j = armnn::Compute::Undefined;
- }
- }
- }
-
- // Remove 'Undefined' devices.
- computeDevices.erase(std::remove(computeDevices.begin(), computeDevices.end(), armnn::Compute::Undefined),
- computeDevices.end());
-}
-
-struct TensorPrinter : public boost::static_visitor<>
-{
- TensorPrinter(const std::string& binding, const armnn::TensorInfo& info)
- : m_OutputBinding(binding)
- , m_Scale(info.GetQuantizationScale())
- , m_Offset(info.GetQuantizationOffset())
- {}
-
- void operator()(const std::vector<float>& values)
- {
- ForEachValue(values, [](float value){
- printf("%f ", value);
- });
- }
-
- void operator()(const std::vector<uint8_t>& values)
- {
- auto& scale = m_Scale;
- auto& offset = m_Offset;
- ForEachValue(values, [&scale, &offset](uint8_t value)
- {
- printf("%f ", armnn::Dequantize(value, scale, offset));
- });
- }
-
- void operator()(const std::vector<int>& values)
- {
- ForEachValue(values, [](int value)
- {
- printf("%d ", value);
- });
- }
-
-private:
- template<typename Container, typename Delegate>
- void ForEachValue(const Container& c, Delegate delegate)
- {
- std::cout << m_OutputBinding << ": ";
- for (const auto& value : c)
- {
- delegate(value);
- }
- printf("\n");
- }
-
- std::string m_OutputBinding;
- float m_Scale=0.0f;
- int m_Offset=0;
-};
-
-
-} // namespace
-
-template<typename TParser, typename TDataType>
-int MainImpl(const char* modelPath,
- bool isModelBinary,
- const std::vector<armnn::BackendId>& computeDevices,
- const std::vector<string>& inputNames,
- const std::vector<std::unique_ptr<armnn::TensorShape>>& inputTensorShapes,
- const std::vector<string>& inputTensorDataFilePaths,
- const std::vector<string>& inputTypes,
- const std::vector<string>& outputTypes,
- const std::vector<string>& outputNames,
- bool enableProfiling,
- bool enableFp16TurboMode,
- const double& thresholdTime,
- const size_t subgraphId,
- const std::shared_ptr<armnn::IRuntime>& runtime = nullptr)
-{
- using TContainer = boost::variant<std::vector<float>, std::vector<int>, std::vector<unsigned char>>;
-
- std::vector<TContainer> inputDataContainers;
-
- try
- {
- // Creates an InferenceModel, which will parse the model and load it into an IRuntime.
- typename InferenceModel<TParser, TDataType>::Params params;
- params.m_ModelPath = modelPath;
- params.m_IsModelBinary = isModelBinary;
- params.m_ComputeDevices = computeDevices;
-
- for(const std::string& inputName: inputNames)
- {
- params.m_InputBindings.push_back(inputName);
- }
-
- for(unsigned int i = 0; i < inputTensorShapes.size(); ++i)
- {
- params.m_InputShapes.push_back(*inputTensorShapes[i]);
- }
-
- for(const std::string& outputName: outputNames)
- {
- params.m_OutputBindings.push_back(outputName);
- }
-
- params.m_SubgraphId = subgraphId;
- params.m_EnableFp16TurboMode = enableFp16TurboMode;
- InferenceModel<TParser, TDataType> model(params, enableProfiling, runtime);
-
- for(unsigned int i = 0; i < inputTensorDataFilePaths.size(); ++i)
- {
- std::ifstream inputTensorFile(inputTensorDataFilePaths[i]);
-
- if (inputTypes[i].compare("float") == 0)
- {
- inputDataContainers.push_back(
- ParseDataArray<armnn::DataType::Float32>(inputTensorFile));
- }
- else if (inputTypes[i].compare("int") == 0)
- {
- inputDataContainers.push_back(
- ParseDataArray<armnn::DataType::Signed32>(inputTensorFile));
- }
- else if (inputTypes[i].compare("qasymm8") == 0)
- {
- auto inputBinding = model.GetInputBindingInfo();
- inputDataContainers.push_back(
- ParseDataArray<armnn::DataType::QuantisedAsymm8>(inputTensorFile,
- inputBinding.second.GetQuantizationScale(),
- inputBinding.second.GetQuantizationOffset()));
- }
- else
- {
- BOOST_LOG_TRIVIAL(fatal) << "Unsupported tensor data type \"" << inputTypes[i] << "\". ";
- return EXIT_FAILURE;
- }
-
- inputTensorFile.close();
- }
-
- const size_t numOutputs = params.m_OutputBindings.size();
- std::vector<TContainer> outputDataContainers;
-
- for (unsigned int i = 0; i < numOutputs; ++i)
- {
- if (outputTypes[i].compare("float") == 0)
- {
- outputDataContainers.push_back(std::vector<float>(model.GetOutputSize(i)));
- }
- else if (outputTypes[i].compare("int") == 0)
- {
- outputDataContainers.push_back(std::vector<int>(model.GetOutputSize(i)));
- }
- else if (outputTypes[i].compare("qasymm8") == 0)
- {
- outputDataContainers.push_back(std::vector<uint8_t>(model.GetOutputSize(i)));
- }
- else
- {
- BOOST_LOG_TRIVIAL(fatal) << "Unsupported tensor data type \"" << outputTypes[i] << "\". ";
- return EXIT_FAILURE;
- }
- }
-
- // model.Run returns the inference time elapsed in EnqueueWorkload (in milliseconds)
- auto inference_duration = model.Run(inputDataContainers, outputDataContainers);
-
- // Print output tensors
- const auto& infosOut = model.GetOutputBindingInfos();
- for (size_t i = 0; i < numOutputs; i++)
- {
- const armnn::TensorInfo& infoOut = infosOut[i].second;
- TensorPrinter printer(params.m_OutputBindings[i], infoOut);
- boost::apply_visitor(printer, outputDataContainers[i]);
- }
-
- BOOST_LOG_TRIVIAL(info) << "\nInference time: " << std::setprecision(2)
- << std::fixed << inference_duration.count() << " ms";
-
- // If thresholdTime == 0.0 (default), then it hasn't been supplied at command line
- if (thresholdTime != 0.0)
- {
- BOOST_LOG_TRIVIAL(info) << "Threshold time: " << std::setprecision(2)
- << std::fixed << thresholdTime << " ms";
- auto thresholdMinusInference = thresholdTime - inference_duration.count();
- BOOST_LOG_TRIVIAL(info) << "Threshold time - Inference time: " << std::setprecision(2)
- << std::fixed << thresholdMinusInference << " ms" << "\n";
-
- if (thresholdMinusInference < 0)
- {
- BOOST_LOG_TRIVIAL(fatal) << "Elapsed inference time is greater than provided threshold time.\n";
- return EXIT_FAILURE;
- }
- }
-
-
- }
- catch (armnn::Exception const& e)
- {
- BOOST_LOG_TRIVIAL(fatal) << "Armnn Error: " << e.what();
- return EXIT_FAILURE;
- }
-
- return EXIT_SUCCESS;
-}
-
-// This will run a test
-int RunTest(const std::string& format,
- const std::string& inputTensorShapesStr,
- const vector<armnn::BackendId>& computeDevice,
- const std::string& path,
- const std::string& inputNames,
- const std::string& inputTensorDataFilePaths,
- const std::string& inputTypes,
- const std::string& outputTypes,
- const std::string& outputNames,
- bool enableProfiling,
- bool enableFp16TurboMode,
- const double& thresholdTime,
- const size_t subgraphId,
- const std::shared_ptr<armnn::IRuntime>& runtime = nullptr)
-{
- std::string modelFormat = boost::trim_copy(format);
- std::string modelPath = boost::trim_copy(path);
- std::vector<std::string> inputNamesVector = ParseStringList(inputNames, ",");
- std::vector<std::string> inputTensorShapesVector = ParseStringList(inputTensorShapesStr, ";");
- std::vector<std::string> inputTensorDataFilePathsVector = ParseStringList(
- inputTensorDataFilePaths, ",");
- std::vector<std::string> outputNamesVector = ParseStringList(outputNames, ",");
- std::vector<std::string> inputTypesVector = ParseStringList(inputTypes, ",");
- std::vector<std::string> outputTypesVector = ParseStringList(outputTypes, ",");
-
- // Parse model binary flag from the model-format string we got from the command-line
- bool isModelBinary;
- if (modelFormat.find("bin") != std::string::npos)
- {
- isModelBinary = true;
- }
- else if (modelFormat.find("txt") != std::string::npos || modelFormat.find("text") != std::string::npos)
- {
- isModelBinary = false;
- }
- else
- {
- BOOST_LOG_TRIVIAL(fatal) << "Unknown model format: '" << modelFormat << "'. Please include 'binary' or 'text'";
- return EXIT_FAILURE;
- }
-
- if ((inputTensorShapesVector.size() != 0) && (inputTensorShapesVector.size() != inputNamesVector.size()))
- {
- BOOST_LOG_TRIVIAL(fatal) << "input-name and input-tensor-shape must have the same amount of elements.";
- return EXIT_FAILURE;
- }
-
- if ((inputTensorDataFilePathsVector.size() != 0) &&
- (inputTensorDataFilePathsVector.size() != inputNamesVector.size()))
- {
- BOOST_LOG_TRIVIAL(fatal) << "input-name and input-tensor-data must have the same amount of elements.";
- return EXIT_FAILURE;
- }
-
- if (inputTypesVector.size() == 0)
- {
- //Defaults the value of all inputs to "float"
- inputTypesVector.assign(inputNamesVector.size(), "float");
- }
- if (outputTypesVector.size() == 0)
- {
- //Defaults the value of all outputs to "float"
- outputTypesVector.assign(outputNamesVector.size(), "float");
- }
- else if ((inputTypesVector.size() != 0) && (inputTypesVector.size() != inputNamesVector.size()))
- {
- BOOST_LOG_TRIVIAL(fatal) << "input-name and input-type must have the same amount of elements.";
- return EXIT_FAILURE;
- }
-
- // Parse input tensor shape from the string we got from the command-line.
- std::vector<std::unique_ptr<armnn::TensorShape>> inputTensorShapes;
-
- if (!inputTensorShapesVector.empty())
- {
- inputTensorShapes.reserve(inputTensorShapesVector.size());
-
- for(const std::string& shape : inputTensorShapesVector)
- {
- std::stringstream ss(shape);
- std::vector<unsigned int> dims = ParseArray(ss);
-
- try
- {
- // Coverity fix: An exception of type armnn::InvalidArgumentException is thrown and never caught.
- inputTensorShapes.push_back(std::make_unique<armnn::TensorShape>(dims.size(), dims.data()));
- }
- catch (const armnn::InvalidArgumentException& e)
- {
- BOOST_LOG_TRIVIAL(fatal) << "Cannot create tensor shape: " << e.what();
- return EXIT_FAILURE;
- }
- }
- }
-
- // Check that threshold time is not less than zero
- if (thresholdTime < 0)
- {
- BOOST_LOG_TRIVIAL(fatal) << "Threshold time supplied as a commoand line argument is less than zero.";
- return EXIT_FAILURE;
- }
-
- // Forward to implementation based on the parser type
- if (modelFormat.find("armnn") != std::string::npos)
- {
-#if defined(ARMNN_SERIALIZER)
- return MainImpl<armnnDeserializer::IDeserializer, float>(
- modelPath.c_str(), isModelBinary, computeDevice,
- inputNamesVector, inputTensorShapes,
- inputTensorDataFilePathsVector, inputTypesVector,
- outputTypesVector, outputNamesVector, enableProfiling,
- enableFp16TurboMode, thresholdTime, subgraphId, runtime);
-#else
- BOOST_LOG_TRIVIAL(fatal) << "Not built with serialization support.";
- return EXIT_FAILURE;
-#endif
- }
- else if (modelFormat.find("caffe") != std::string::npos)
- {
-#if defined(ARMNN_CAFFE_PARSER)
- return MainImpl<armnnCaffeParser::ICaffeParser, float>(modelPath.c_str(), isModelBinary, computeDevice,
- inputNamesVector, inputTensorShapes,
- inputTensorDataFilePathsVector, inputTypesVector,
- outputTypesVector, outputNamesVector, enableProfiling,
- enableFp16TurboMode, thresholdTime, subgraphId, runtime);
-#else
- BOOST_LOG_TRIVIAL(fatal) << "Not built with Caffe parser support.";
- return EXIT_FAILURE;
-#endif
- }
- else if (modelFormat.find("onnx") != std::string::npos)
-{
-#if defined(ARMNN_ONNX_PARSER)
- return MainImpl<armnnOnnxParser::IOnnxParser, float>(modelPath.c_str(), isModelBinary, computeDevice,
- inputNamesVector, inputTensorShapes,
- inputTensorDataFilePathsVector, inputTypesVector,
- outputTypesVector, outputNamesVector, enableProfiling,
- enableFp16TurboMode, thresholdTime, subgraphId, runtime);
-#else
- BOOST_LOG_TRIVIAL(fatal) << "Not built with Onnx parser support.";
- return EXIT_FAILURE;
-#endif
- }
- else if (modelFormat.find("tensorflow") != std::string::npos)
- {
-#if defined(ARMNN_TF_PARSER)
- return MainImpl<armnnTfParser::ITfParser, float>(modelPath.c_str(), isModelBinary, computeDevice,
- inputNamesVector, inputTensorShapes,
- inputTensorDataFilePathsVector, inputTypesVector,
- outputTypesVector, outputNamesVector, enableProfiling,
- enableFp16TurboMode, thresholdTime, subgraphId, runtime);
-#else
- BOOST_LOG_TRIVIAL(fatal) << "Not built with Tensorflow parser support.";
- return EXIT_FAILURE;
-#endif
- }
- else if(modelFormat.find("tflite") != std::string::npos)
- {
-#if defined(ARMNN_TF_LITE_PARSER)
- if (! isModelBinary)
- {
- BOOST_LOG_TRIVIAL(fatal) << "Unknown model format: '" << modelFormat << "'. Only 'binary' format supported \
- for tflite files";
- return EXIT_FAILURE;
- }
- return MainImpl<armnnTfLiteParser::ITfLiteParser, float>(modelPath.c_str(), isModelBinary, computeDevice,
- inputNamesVector, inputTensorShapes,
- inputTensorDataFilePathsVector, inputTypesVector,
- outputTypesVector, outputNamesVector, enableProfiling,
- enableFp16TurboMode, thresholdTime, subgraphId,
- runtime);
-#else
- BOOST_LOG_TRIVIAL(fatal) << "Unknown model format: '" << modelFormat <<
- "'. Please include 'caffe', 'tensorflow', 'tflite' or 'onnx'";
- return EXIT_FAILURE;
-#endif
- }
- else
- {
- BOOST_LOG_TRIVIAL(fatal) << "Unknown model format: '" << modelFormat <<
- "'. Please include 'caffe', 'tensorflow', 'tflite' or 'onnx'";
- return EXIT_FAILURE;
- }
-}
-
-int RunCsvTest(const armnnUtils::CsvRow &csvRow, const std::shared_ptr<armnn::IRuntime>& runtime,
- const bool enableProfiling, const bool enableFp16TurboMode, const double& thresholdTime)
-{
- std::string modelFormat;
- std::string modelPath;
- std::string inputNames;
- std::string inputTensorShapes;
- std::string inputTensorDataFilePaths;
- std::string outputNames;
- std::string inputTypes;
- std::string outputTypes;
-
- size_t subgraphId = 0;
-
- const std::string backendsMessage = std::string("The preferred order of devices to run layers on by default. ")
- + std::string("Possible choices: ")
- + armnn::BackendRegistryInstance().GetBackendIdsAsString();
-
- po::options_description desc("Options");
- try
- {
- desc.add_options()
- ("model-format,f", po::value(&modelFormat),
- "armnn-binary, caffe-binary, caffe-text, tflite-binary, onnx-binary, onnx-text, tensorflow-binary or "
- "tensorflow-text.")
- ("model-path,m", po::value(&modelPath), "Path to model file, e.g. .armnn, .caffemodel, .prototxt, "
- ".tflite, .onnx")
- ("compute,c", po::value<std::vector<armnn::BackendId>>()->multitoken(),
- backendsMessage.c_str())
- ("input-name,i", po::value(&inputNames), "Identifier of the input tensors in the network separated by comma.")
- ("subgraph-number,n", po::value<size_t>(&subgraphId)->default_value(0), "Id of the subgraph to be "
- "executed. Defaults to 0.")
- ("input-tensor-shape,s", po::value(&inputTensorShapes),
- "The shape of the input tensors in the network as a flat array of integers separated by comma. "
- "Several shapes can be passed separating them by semicolon. "
- "This parameter is optional, depending on the network.")
- ("input-tensor-data,d", po::value(&inputTensorDataFilePaths),
- "Path to files containing the input data as a flat array separated by whitespace. "
- "Several paths can be passed separating them by comma.")
- ("input-type,y",po::value(&inputTypes), "The type of the input tensors in the network separated by comma. "
- "If unset, defaults to \"float\" for all defined inputs. "
- "Accepted values (float, int or qasymm8).")
- ("output-type,z",po::value(&outputTypes), "The type of the output tensors in the network separated by comma. "
- "If unset, defaults to \"float\" for all defined outputs. "
- "Accepted values (float, int or qasymm8).")
- ("output-name,o", po::value(&outputNames),
- "Identifier of the output tensors in the network separated by comma.");
- }
- catch (const std::exception& e)
- {
- // Coverity points out that default_value(...) can throw a bad_lexical_cast,
- // and that desc.add_options() can throw boost::io::too_few_args.
- // They really won't in any of these cases.
- BOOST_ASSERT_MSG(false, "Caught unexpected exception");
- BOOST_LOG_TRIVIAL(fatal) << "Fatal internal error: " << e.what();
- return EXIT_FAILURE;
- }
-
- std::vector<const char*> clOptions;
- clOptions.reserve(csvRow.values.size());
- for (const std::string& value : csvRow.values)
- {
- clOptions.push_back(value.c_str());
- }
-
- po::variables_map vm;
- try
- {
- po::store(po::parse_command_line(static_cast<int>(clOptions.size()), clOptions.data(), desc), vm);
-
- po::notify(vm);
-
- CheckOptionDependencies(vm);
- }
- catch (const po::error& e)
- {
- std::cerr << e.what() << std::endl << std::endl;
- std::cerr << desc << std::endl;
- return EXIT_FAILURE;
- }
-
- // Get the preferred order of compute devices.
- std::vector<armnn::BackendId> computeDevices = vm["compute"].as<std::vector<armnn::BackendId>>();
-
- // Remove duplicates from the list of compute devices.
- RemoveDuplicateDevices(computeDevices);
-
- // Check that the specified compute devices are valid.
- std::string invalidBackends;
- if (!CheckRequestedBackendsAreValid(computeDevices, armnn::Optional<std::string&>(invalidBackends)))
- {
- BOOST_LOG_TRIVIAL(fatal) << "The list of preferred devices contains invalid backend IDs: "
- << invalidBackends;
- return EXIT_FAILURE;
- }
-
- return RunTest(modelFormat, inputTensorShapes, computeDevices, modelPath, inputNames,
- inputTensorDataFilePaths, inputTypes, outputTypes, outputNames,
- enableProfiling, enableFp16TurboMode, thresholdTime, subgraphId);
-}
+#include "../NetworkExecutionUtils/NetworkExecutionUtils.hpp"
// MAIN
int main(int argc, const char* argv[])
diff --git a/tests/ModelAccuracyTool-Armnn/ModelAccuracyTool-Armnn.cpp b/tests/ModelAccuracyTool-Armnn/ModelAccuracyTool-Armnn.cpp
index 3abfb3c2ec..aec4d70271 100644
--- a/tests/ModelAccuracyTool-Armnn/ModelAccuracyTool-Armnn.cpp
+++ b/tests/ModelAccuracyTool-Armnn/ModelAccuracyTool-Armnn.cpp
@@ -4,9 +4,9 @@
//
#include "ModelAccuracyChecker.hpp"
-#include "../InferenceTest.hpp"
#include "../ImagePreprocessor.hpp"
#include "armnnDeserializer/IDeserializer.hpp"
+#include "../NetworkExecutionUtils/NetworkExecutionUtils.hpp"
#include <boost/filesystem.hpp>
#include <boost/range/iterator_range.hpp>
@@ -14,70 +14,8 @@
using namespace armnn::test;
-namespace po = boost::program_options;
-
-bool CheckOption(const po::variables_map& vm,
- const char* option)
-{
- // Check that the given option is valid.
- if (option == nullptr)
- {
- return false;
- }
-
- // Check whether 'option' is provided.
- return vm.find(option) != vm.end();
-}
-
-template<typename T, typename TParseElementFunc>
-std::vector<T> ParseArrayImpl(std::istream& stream, TParseElementFunc parseElementFunc, const char * chars = "\t ,:")
-{
- std::vector<T> result;
- // Processes line-by-line.
- std::string line;
- while (std::getline(stream, line))
- {
- std::vector<std::string> tokens;
- try
- {
- // Coverity fix: boost::split() may throw an exception of type boost::bad_function_call.
- boost::split(tokens, line, boost::algorithm::is_any_of(chars), boost::token_compress_on);
- }
- catch (const std::exception& e)
- {
- BOOST_LOG_TRIVIAL(error) << "An error occurred when splitting tokens: " << e.what();
- continue;
- }
- for (const std::string& token : tokens)
- {
- if (!token.empty()) // See https://stackoverflow.com/questions/10437406/
- {
- try
- {
- result.push_back(parseElementFunc(token));
- }
- catch (const std::exception&)
- {
- BOOST_LOG_TRIVIAL(error) << "'" << token << "' is not a valid number. It has been ignored.";
- }
- }
- }
- }
-
- return result;
-}
-
map<std::string, int> LoadValidationLabels(const string & validationLabelPath);
-template<armnn::DataType NonQuantizedType>
-auto ParseDataArray(std::istream & stream);
-
-template<>
-auto ParseDataArray<armnn::DataType::Float32>(std::istream & stream)
-{
- return ParseArrayImpl<float>(stream, [](const std::string& s) { return std::stof(s); });
-}
-
int main(int argc, char* argv[])
{
try
@@ -94,6 +32,7 @@ int main(int argc, char* argv[])
std::vector<armnn::BackendId> defaultBackends = {armnn::Compute::CpuAcc, armnn::Compute::CpuRef};
std::string modelPath;
std::string dataDir;
+ std::string inputType = "float";
std::string inputName;
std::string outputName;
std::string validationLabelPath;
@@ -112,6 +51,9 @@ int main(int argc, char* argv[])
backendsMessage.c_str())
("data-dir,d", po::value<std::string>(&dataDir)->required(),
"Path to directory containing the ImageNet test data")
+ ("input-type,y", po::value(&inputType), "The data type of the input tensors."
+ "If unset, defaults to \"float\" for all defined inputs. "
+ "Accepted values (float, int or qasymm8)")
("input-name,i", po::value<std::string>(&inputName)->required(),
"Identifier of the input tensors in the network separated by comma.")
("output-name,o", po::value<std::string>(&outputName)->required(),
@@ -217,14 +159,50 @@ int main(int argc, char* argv[])
if(ValidateDirectory(dataDir))
{
+ InferenceModel<armnnDeserializer::IDeserializer, float>::Params params;
+ params.m_ModelPath = modelPath;
+ params.m_IsModelBinary = true;
+ params.m_ComputeDevices = computeDevice;
+ params.m_InputBindings.push_back(inputName);
+ params.m_OutputBindings.push_back(outputName);
+
+ using TParser = armnnDeserializer::IDeserializer;
+ InferenceModel<TParser, float> model(params, false);
for (auto & imageEntry : boost::make_iterator_range(directory_iterator(pathToDataDir), {}))
{
cout << "Processing image: " << imageEntry << "\n";
std::ifstream inputTensorFile(imageEntry.path().string());
vector<TContainer> inputDataContainers;
- inputDataContainers.push_back(ParseDataArray<armnn::DataType::Float32>(inputTensorFile));
- vector<TContainer> outputDataContainers = {vector<float>(1001)};
+ vector<TContainer> outputDataContainers;
+
+ if (inputType.compare("float") == 0)
+ {
+ inputDataContainers.push_back(
+ ParseDataArray<armnn::DataType::Float32>(inputTensorFile));
+ outputDataContainers = {vector<float>(1001)};
+ }
+ else if (inputType.compare("int") == 0)
+ {
+ inputDataContainers.push_back(
+ ParseDataArray<armnn::DataType::Signed32>(inputTensorFile));
+ outputDataContainers = {vector<int>(1001)};
+ }
+ else if (inputType.compare("qasymm8") == 0)
+ {
+ auto inputBinding = model.GetInputBindingInfo();
+ inputDataContainers.push_back(
+ ParseDataArray<armnn::DataType::QuantisedAsymm8>(
+ inputTensorFile,
+ inputBinding.second.GetQuantizationScale(),
+ inputBinding.second.GetQuantizationOffset()));
+ outputDataContainers = {vector<uint8_t >(1001)};
+ }
+ else
+ {
+ BOOST_LOG_TRIVIAL(fatal) << "Unsupported tensor data type \"" << inputType << "\". ";
+ return EXIT_FAILURE;
+ }
status = runtime->EnqueueWorkload(networkId,
armnnUtils::MakeInputTensors(inputBindings, inputDataContainers),
diff --git a/tests/NetworkExecutionUtils/NetworkExecutionUtils.hpp b/tests/NetworkExecutionUtils/NetworkExecutionUtils.hpp
new file mode 100644
index 0000000000..9d7e368dad
--- /dev/null
+++ b/tests/NetworkExecutionUtils/NetworkExecutionUtils.hpp
@@ -0,0 +1,676 @@
+//
+// Copyright © 2017 Arm Ltd. All rights reserved.
+// SPDX-License-Identifier: MIT
+//
+#include <armnn/ArmNN.hpp>
+#include <armnn/TypesUtils.hpp>
+
+#if defined(ARMNN_SERIALIZER)
+#include "armnnDeserializer/IDeserializer.hpp"
+#endif
+#if defined(ARMNN_CAFFE_PARSER)
+#include "armnnCaffeParser/ICaffeParser.hpp"
+#endif
+#if defined(ARMNN_TF_PARSER)
+#include "armnnTfParser/ITfParser.hpp"
+#endif
+#if defined(ARMNN_TF_LITE_PARSER)
+#include "armnnTfLiteParser/ITfLiteParser.hpp"
+#endif
+#if defined(ARMNN_ONNX_PARSER)
+#include "armnnOnnxParser/IOnnxParser.hpp"
+#endif
+#include "CsvReader.hpp"
+#include "../InferenceTest.hpp"
+
+#include <Logging.hpp>
+#include <Profiling.hpp>
+
+#include <boost/algorithm/string/trim.hpp>
+#include <boost/algorithm/string/split.hpp>
+#include <boost/algorithm/string/classification.hpp>
+#include <boost/program_options.hpp>
+#include <boost/variant.hpp>
+
+#include <iostream>
+#include <fstream>
+#include <functional>
+#include <future>
+#include <algorithm>
+#include <iterator>
+
+namespace
+{
+
+// Configure boost::program_options for command-line parsing and validation.
+namespace po = boost::program_options;
+
+template<typename T, typename TParseElementFunc>
+std::vector<T> ParseArrayImpl(std::istream& stream, TParseElementFunc parseElementFunc, const char * chars = "\t ,:")
+{
+ std::vector<T> result;
+ // Processes line-by-line.
+ std::string line;
+ while (std::getline(stream, line))
+ {
+ std::vector<std::string> tokens;
+ try
+ {
+ // Coverity fix: boost::split() may throw an exception of type boost::bad_function_call.
+ boost::split(tokens, line, boost::algorithm::is_any_of(chars), boost::token_compress_on);
+ }
+ catch (const std::exception& e)
+ {
+ BOOST_LOG_TRIVIAL(error) << "An error occurred when splitting tokens: " << e.what();
+ continue;
+ }
+ for (const std::string& token : tokens)
+ {
+ if (!token.empty()) // See https://stackoverflow.com/questions/10437406/
+ {
+ try
+ {
+ result.push_back(parseElementFunc(token));
+ }
+ catch (const std::exception&)
+ {
+ BOOST_LOG_TRIVIAL(error) << "'" << token << "' is not a valid number. It has been ignored.";
+ }
+ }
+ }
+ }
+
+ return result;
+}
+
+bool CheckOption(const po::variables_map& vm,
+ const char* option)
+{
+ // Check that the given option is valid.
+ 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)
+{
+ // Check that the given options are valid.
+ 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", "input-tensor-data");
+ CheckOptionDependency(vm, "model-path", "output-name");
+ CheckOptionDependency(vm, "input-tensor-shape", "model-path");
+}
+
+template<armnn::DataType NonQuantizedType>
+auto ParseDataArray(std::istream & stream);
+
+template<armnn::DataType QuantizedType>
+auto ParseDataArray(std::istream& stream,
+ const float& quantizationScale,
+ const int32_t& quantizationOffset);
+
+template<>
+auto ParseDataArray<armnn::DataType::Float32>(std::istream & stream)
+{
+ return ParseArrayImpl<float>(stream, [](const std::string& s) { return std::stof(s); });
+}
+
+template<>
+auto ParseDataArray<armnn::DataType::Signed32>(std::istream & stream)
+{
+ return ParseArrayImpl<int>(stream, [](const std::string & s) { return std::stoi(s); });
+}
+
+template<>
+auto ParseDataArray<armnn::DataType::QuantisedAsymm8>(std::istream& stream,
+ const float& quantizationScale,
+ const int32_t& quantizationOffset)
+{
+ return ParseArrayImpl<uint8_t>(stream,
+ [&quantizationScale, &quantizationOffset](const std::string & s)
+ {
+ return boost::numeric_cast<uint8_t>(
+ armnn::Quantize<u_int8_t>(std::stof(s),
+ quantizationScale,
+ quantizationOffset));
+ });
+}
+
+std::vector<unsigned int> ParseArray(std::istream& stream)
+{
+ return ParseArrayImpl<unsigned int>(stream,
+ [](const std::string& s) { return boost::numeric_cast<unsigned int>(std::stoi(s)); });
+}
+
+std::vector<std::string> ParseStringList(const std::string & inputString, const char * delimiter)
+{
+ std::stringstream stream(inputString);
+ return ParseArrayImpl<std::string>(stream, [](const std::string& s) { return boost::trim_copy(s); }, delimiter);
+}
+
+void RemoveDuplicateDevices(std::vector<armnn::BackendId>& computeDevices)
+{
+ // Mark the duplicate devices as 'Undefined'.
+ for (auto i = computeDevices.begin(); i != computeDevices.end(); ++i)
+ {
+ for (auto j = std::next(i); j != computeDevices.end(); ++j)
+ {
+ if (*j == *i)
+ {
+ *j = armnn::Compute::Undefined;
+ }
+ }
+ }
+
+ // Remove 'Undefined' devices.
+ computeDevices.erase(std::remove(computeDevices.begin(), computeDevices.end(), armnn::Compute::Undefined),
+ computeDevices.end());
+}
+
+struct TensorPrinter : public boost::static_visitor<>
+{
+ TensorPrinter(const std::string& binding, const armnn::TensorInfo& info)
+ : m_OutputBinding(binding)
+ , m_Scale(info.GetQuantizationScale())
+ , m_Offset(info.GetQuantizationOffset())
+ {}
+
+ void operator()(const std::vector<float>& values)
+ {
+ ForEachValue(values, [](float value){
+ printf("%f ", value);
+ });
+ }
+
+ void operator()(const std::vector<uint8_t>& values)
+ {
+ auto& scale = m_Scale;
+ auto& offset = m_Offset;
+ ForEachValue(values, [&scale, &offset](uint8_t value)
+ {
+ printf("%f ", armnn::Dequantize(value, scale, offset));
+ });
+ }
+
+ void operator()(const std::vector<int>& values)
+ {
+ ForEachValue(values, [](int value)
+ {
+ printf("%d ", value);
+ });
+ }
+
+private:
+ template<typename Container, typename Delegate>
+ void ForEachValue(const Container& c, Delegate delegate)
+ {
+ std::cout << m_OutputBinding << ": ";
+ for (const auto& value : c)
+ {
+ delegate(value);
+ }
+ printf("\n");
+ }
+
+ std::string m_OutputBinding;
+ float m_Scale=0.0f;
+ int m_Offset=0;
+};
+
+
+} // namespace
+
+template<typename TParser, typename TDataType>
+int MainImpl(const char* modelPath,
+ bool isModelBinary,
+ const std::vector<armnn::BackendId>& computeDevices,
+ const std::vector<string>& inputNames,
+ const std::vector<std::unique_ptr<armnn::TensorShape>>& inputTensorShapes,
+ const std::vector<string>& inputTensorDataFilePaths,
+ const std::vector<string>& inputTypes,
+ const std::vector<string>& outputTypes,
+ const std::vector<string>& outputNames,
+ bool enableProfiling,
+ bool enableFp16TurboMode,
+ const double& thresholdTime,
+ const size_t subgraphId,
+ const std::shared_ptr<armnn::IRuntime>& runtime = nullptr)
+{
+ using TContainer = boost::variant<std::vector<float>, std::vector<int>, std::vector<unsigned char>>;
+
+ std::vector<TContainer> inputDataContainers;
+
+ try
+ {
+ // Creates an InferenceModel, which will parse the model and load it into an IRuntime.
+ typename InferenceModel<TParser, TDataType>::Params params;
+ params.m_ModelPath = modelPath;
+ params.m_IsModelBinary = isModelBinary;
+ params.m_ComputeDevices = computeDevices;
+
+ for(const std::string& inputName: inputNames)
+ {
+ params.m_InputBindings.push_back(inputName);
+ }
+
+ for(unsigned int i = 0; i < inputTensorShapes.size(); ++i)
+ {
+ params.m_InputShapes.push_back(*inputTensorShapes[i]);
+ }
+
+ for(const std::string& outputName: outputNames)
+ {
+ params.m_OutputBindings.push_back(outputName);
+ }
+
+ params.m_SubgraphId = subgraphId;
+ params.m_EnableFp16TurboMode = enableFp16TurboMode;
+ InferenceModel<TParser, TDataType> model(params, enableProfiling, runtime);
+
+ for(unsigned int i = 0; i < inputTensorDataFilePaths.size(); ++i)
+ {
+ std::ifstream inputTensorFile(inputTensorDataFilePaths[i]);
+
+ if (inputTypes[i].compare("float") == 0)
+ {
+ inputDataContainers.push_back(
+ ParseDataArray<armnn::DataType::Float32>(inputTensorFile));
+ }
+ else if (inputTypes[i].compare("int") == 0)
+ {
+ inputDataContainers.push_back(
+ ParseDataArray<armnn::DataType::Signed32>(inputTensorFile));
+ }
+ else if (inputTypes[i].compare("qasymm8") == 0)
+ {
+ auto inputBinding = model.GetInputBindingInfo();
+ inputDataContainers.push_back(
+ ParseDataArray<armnn::DataType::QuantisedAsymm8>(inputTensorFile,
+ inputBinding.second.GetQuantizationScale(),
+ inputBinding.second.GetQuantizationOffset()));
+ }
+ else
+ {
+ BOOST_LOG_TRIVIAL(fatal) << "Unsupported tensor data type \"" << inputTypes[i] << "\". ";
+ return EXIT_FAILURE;
+ }
+
+ inputTensorFile.close();
+ }
+
+ const size_t numOutputs = params.m_OutputBindings.size();
+ std::vector<TContainer> outputDataContainers;
+
+ for (unsigned int i = 0; i < numOutputs; ++i)
+ {
+ if (outputTypes[i].compare("float") == 0)
+ {
+ outputDataContainers.push_back(std::vector<float>(model.GetOutputSize(i)));
+ }
+ else if (outputTypes[i].compare("int") == 0)
+ {
+ outputDataContainers.push_back(std::vector<int>(model.GetOutputSize(i)));
+ }
+ else if (outputTypes[i].compare("qasymm8") == 0)
+ {
+ outputDataContainers.push_back(std::vector<uint8_t>(model.GetOutputSize(i)));
+ }
+ else
+ {
+ BOOST_LOG_TRIVIAL(fatal) << "Unsupported tensor data type \"" << outputTypes[i] << "\". ";
+ return EXIT_FAILURE;
+ }
+ }
+
+ // model.Run returns the inference time elapsed in EnqueueWorkload (in milliseconds)
+ auto inference_duration = model.Run(inputDataContainers, outputDataContainers);
+
+ // Print output tensors
+ const auto& infosOut = model.GetOutputBindingInfos();
+ for (size_t i = 0; i < numOutputs; i++)
+ {
+ const armnn::TensorInfo& infoOut = infosOut[i].second;
+ TensorPrinter printer(params.m_OutputBindings[i], infoOut);
+ boost::apply_visitor(printer, outputDataContainers[i]);
+ }
+
+ BOOST_LOG_TRIVIAL(info) << "\nInference time: " << std::setprecision(2)
+ << std::fixed << inference_duration.count() << " ms";
+
+ // If thresholdTime == 0.0 (default), then it hasn't been supplied at command line
+ if (thresholdTime != 0.0)
+ {
+ BOOST_LOG_TRIVIAL(info) << "Threshold time: " << std::setprecision(2)
+ << std::fixed << thresholdTime << " ms";
+ auto thresholdMinusInference = thresholdTime - inference_duration.count();
+ BOOST_LOG_TRIVIAL(info) << "Threshold time - Inference time: " << std::setprecision(2)
+ << std::fixed << thresholdMinusInference << " ms" << "\n";
+
+ if (thresholdMinusInference < 0)
+ {
+ BOOST_LOG_TRIVIAL(fatal) << "Elapsed inference time is greater than provided threshold time.\n";
+ return EXIT_FAILURE;
+ }
+ }
+
+
+ }
+ catch (armnn::Exception const& e)
+ {
+ BOOST_LOG_TRIVIAL(fatal) << "Armnn Error: " << e.what();
+ return EXIT_FAILURE;
+ }
+
+ return EXIT_SUCCESS;
+}
+
+// This will run a test
+int RunTest(const std::string& format,
+ const std::string& inputTensorShapesStr,
+ const vector<armnn::BackendId>& computeDevice,
+ const std::string& path,
+ const std::string& inputNames,
+ const std::string& inputTensorDataFilePaths,
+ const std::string& inputTypes,
+ const std::string& outputTypes,
+ const std::string& outputNames,
+ bool enableProfiling,
+ bool enableFp16TurboMode,
+ const double& thresholdTime,
+ const size_t subgraphId,
+ const std::shared_ptr<armnn::IRuntime>& runtime = nullptr)
+{
+ std::string modelFormat = boost::trim_copy(format);
+ std::string modelPath = boost::trim_copy(path);
+ std::vector<std::string> inputNamesVector = ParseStringList(inputNames, ",");
+ std::vector<std::string> inputTensorShapesVector = ParseStringList(inputTensorShapesStr, ";");
+ std::vector<std::string> inputTensorDataFilePathsVector = ParseStringList(
+ inputTensorDataFilePaths, ",");
+ std::vector<std::string> outputNamesVector = ParseStringList(outputNames, ",");
+ std::vector<std::string> inputTypesVector = ParseStringList(inputTypes, ",");
+ std::vector<std::string> outputTypesVector = ParseStringList(outputTypes, ",");
+
+ // Parse model binary flag from the model-format string we got from the command-line
+ bool isModelBinary;
+ if (modelFormat.find("bin") != std::string::npos)
+ {
+ isModelBinary = true;
+ }
+ else if (modelFormat.find("txt") != std::string::npos || modelFormat.find("text") != std::string::npos)
+ {
+ isModelBinary = false;
+ }
+ else
+ {
+ BOOST_LOG_TRIVIAL(fatal) << "Unknown model format: '" << modelFormat << "'. Please include 'binary' or 'text'";
+ return EXIT_FAILURE;
+ }
+
+ if ((inputTensorShapesVector.size() != 0) && (inputTensorShapesVector.size() != inputNamesVector.size()))
+ {
+ BOOST_LOG_TRIVIAL(fatal) << "input-name and input-tensor-shape must have the same amount of elements.";
+ return EXIT_FAILURE;
+ }
+
+ if ((inputTensorDataFilePathsVector.size() != 0) &&
+ (inputTensorDataFilePathsVector.size() != inputNamesVector.size()))
+ {
+ BOOST_LOG_TRIVIAL(fatal) << "input-name and input-tensor-data must have the same amount of elements.";
+ return EXIT_FAILURE;
+ }
+
+ if (inputTypesVector.size() == 0)
+ {
+ //Defaults the value of all inputs to "float"
+ inputTypesVector.assign(inputNamesVector.size(), "float");
+ }
+ if (outputTypesVector.size() == 0)
+ {
+ //Defaults the value of all outputs to "float"
+ outputTypesVector.assign(outputNamesVector.size(), "float");
+ }
+ else if ((inputTypesVector.size() != 0) && (inputTypesVector.size() != inputNamesVector.size()))
+ {
+ BOOST_LOG_TRIVIAL(fatal) << "input-name and input-type must have the same amount of elements.";
+ return EXIT_FAILURE;
+ }
+
+ // Parse input tensor shape from the string we got from the command-line.
+ std::vector<std::unique_ptr<armnn::TensorShape>> inputTensorShapes;
+
+ if (!inputTensorShapesVector.empty())
+ {
+ inputTensorShapes.reserve(inputTensorShapesVector.size());
+
+ for(const std::string& shape : inputTensorShapesVector)
+ {
+ std::stringstream ss(shape);
+ std::vector<unsigned int> dims = ParseArray(ss);
+
+ try
+ {
+ // Coverity fix: An exception of type armnn::InvalidArgumentException is thrown and never caught.
+ inputTensorShapes.push_back(std::make_unique<armnn::TensorShape>(dims.size(), dims.data()));
+ }
+ catch (const armnn::InvalidArgumentException& e)
+ {
+ BOOST_LOG_TRIVIAL(fatal) << "Cannot create tensor shape: " << e.what();
+ return EXIT_FAILURE;
+ }
+ }
+ }
+
+ // Check that threshold time is not less than zero
+ if (thresholdTime < 0)
+ {
+ BOOST_LOG_TRIVIAL(fatal) << "Threshold time supplied as a commoand line argument is less than zero.";
+ return EXIT_FAILURE;
+ }
+
+ // Forward to implementation based on the parser type
+ if (modelFormat.find("armnn") != std::string::npos)
+ {
+#if defined(ARMNN_SERIALIZER)
+ return MainImpl<armnnDeserializer::IDeserializer, float>(
+ modelPath.c_str(), isModelBinary, computeDevice,
+ inputNamesVector, inputTensorShapes,
+ inputTensorDataFilePathsVector, inputTypesVector,
+ outputTypesVector, outputNamesVector, enableProfiling,
+ enableFp16TurboMode, thresholdTime, subgraphId, runtime);
+#else
+ BOOST_LOG_TRIVIAL(fatal) << "Not built with serialization support.";
+ return EXIT_FAILURE;
+#endif
+ }
+ else if (modelFormat.find("caffe") != std::string::npos)
+ {
+#if defined(ARMNN_CAFFE_PARSER)
+ return MainImpl<armnnCaffeParser::ICaffeParser, float>(modelPath.c_str(), isModelBinary, computeDevice,
+ inputNamesVector, inputTensorShapes,
+ inputTensorDataFilePathsVector, inputTypesVector,
+ outputTypesVector, outputNamesVector, enableProfiling,
+ enableFp16TurboMode, thresholdTime, subgraphId, runtime);
+#else
+ BOOST_LOG_TRIVIAL(fatal) << "Not built with Caffe parser support.";
+ return EXIT_FAILURE;
+#endif
+ }
+ else if (modelFormat.find("onnx") != std::string::npos)
+{
+#if defined(ARMNN_ONNX_PARSER)
+ return MainImpl<armnnOnnxParser::IOnnxParser, float>(modelPath.c_str(), isModelBinary, computeDevice,
+ inputNamesVector, inputTensorShapes,
+ inputTensorDataFilePathsVector, inputTypesVector,
+ outputTypesVector, outputNamesVector, enableProfiling,
+ enableFp16TurboMode, thresholdTime, subgraphId, runtime);
+#else
+ BOOST_LOG_TRIVIAL(fatal) << "Not built with Onnx parser support.";
+ return EXIT_FAILURE;
+#endif
+ }
+ else if (modelFormat.find("tensorflow") != std::string::npos)
+ {
+#if defined(ARMNN_TF_PARSER)
+ return MainImpl<armnnTfParser::ITfParser, float>(modelPath.c_str(), isModelBinary, computeDevice,
+ inputNamesVector, inputTensorShapes,
+ inputTensorDataFilePathsVector, inputTypesVector,
+ outputTypesVector, outputNamesVector, enableProfiling,
+ enableFp16TurboMode, thresholdTime, subgraphId, runtime);
+#else
+ BOOST_LOG_TRIVIAL(fatal) << "Not built with Tensorflow parser support.";
+ return EXIT_FAILURE;
+#endif
+ }
+ else if(modelFormat.find("tflite") != std::string::npos)
+ {
+#if defined(ARMNN_TF_LITE_PARSER)
+ if (! isModelBinary)
+ {
+ BOOST_LOG_TRIVIAL(fatal) << "Unknown model format: '" << modelFormat << "'. Only 'binary' format supported \
+ for tflite files";
+ return EXIT_FAILURE;
+ }
+ return MainImpl<armnnTfLiteParser::ITfLiteParser, float>(modelPath.c_str(), isModelBinary, computeDevice,
+ inputNamesVector, inputTensorShapes,
+ inputTensorDataFilePathsVector, inputTypesVector,
+ outputTypesVector, outputNamesVector, enableProfiling,
+ enableFp16TurboMode, thresholdTime, subgraphId,
+ runtime);
+#else
+ BOOST_LOG_TRIVIAL(fatal) << "Unknown model format: '" << modelFormat <<
+ "'. Please include 'caffe', 'tensorflow', 'tflite' or 'onnx'";
+ return EXIT_FAILURE;
+#endif
+ }
+ else
+ {
+ BOOST_LOG_TRIVIAL(fatal) << "Unknown model format: '" << modelFormat <<
+ "'. Please include 'caffe', 'tensorflow', 'tflite' or 'onnx'";
+ return EXIT_FAILURE;
+ }
+}
+
+int RunCsvTest(const armnnUtils::CsvRow &csvRow, const std::shared_ptr<armnn::IRuntime>& runtime,
+ const bool enableProfiling, const bool enableFp16TurboMode, const double& thresholdTime)
+{
+ std::string modelFormat;
+ std::string modelPath;
+ std::string inputNames;
+ std::string inputTensorShapes;
+ std::string inputTensorDataFilePaths;
+ std::string outputNames;
+ std::string inputTypes;
+ std::string outputTypes;
+
+ size_t subgraphId = 0;
+
+ const std::string backendsMessage = std::string("The preferred order of devices to run layers on by default. ")
+ + std::string("Possible choices: ")
+ + armnn::BackendRegistryInstance().GetBackendIdsAsString();
+
+ po::options_description desc("Options");
+ try
+ {
+ desc.add_options()
+ ("model-format,f", po::value(&modelFormat),
+ "armnn-binary, caffe-binary, caffe-text, tflite-binary, onnx-binary, onnx-text, tensorflow-binary or "
+ "tensorflow-text.")
+ ("model-path,m", po::value(&modelPath), "Path to model file, e.g. .armnn, .caffemodel, .prototxt, "
+ ".tflite, .onnx")
+ ("compute,c", po::value<std::vector<armnn::BackendId>>()->multitoken(),
+ backendsMessage.c_str())
+ ("input-name,i", po::value(&inputNames), "Identifier of the input tensors in the network separated by comma.")
+ ("subgraph-number,n", po::value<size_t>(&subgraphId)->default_value(0), "Id of the subgraph to be "
+ "executed. Defaults to 0.")
+ ("input-tensor-shape,s", po::value(&inputTensorShapes),
+ "The shape of the input tensors in the network as a flat array of integers separated by comma. "
+ "Several shapes can be passed separating them by semicolon. "
+ "This parameter is optional, depending on the network.")
+ ("input-tensor-data,d", po::value(&inputTensorDataFilePaths),
+ "Path to files containing the input data as a flat array separated by whitespace. "
+ "Several paths can be passed separating them by comma.")
+ ("input-type,y",po::value(&inputTypes), "The type of the input tensors in the network separated by comma. "
+ "If unset, defaults to \"float\" for all defined inputs. "
+ "Accepted values (float, int or qasymm8).")
+ ("output-type,z",po::value(&outputTypes), "The type of the output tensors in the network separated by comma. "
+ "If unset, defaults to \"float\" for all defined outputs. "
+ "Accepted values (float, int or qasymm8).")
+ ("output-name,o", po::value(&outputNames),
+ "Identifier of the output tensors in the network separated by comma.");
+ }
+ catch (const std::exception& e)
+ {
+ // Coverity points out that default_value(...) can throw a bad_lexical_cast,
+ // and that desc.add_options() can throw boost::io::too_few_args.
+ // They really won't in any of these cases.
+ BOOST_ASSERT_MSG(false, "Caught unexpected exception");
+ BOOST_LOG_TRIVIAL(fatal) << "Fatal internal error: " << e.what();
+ return EXIT_FAILURE;
+ }
+
+ std::vector<const char*> clOptions;
+ clOptions.reserve(csvRow.values.size());
+ for (const std::string& value : csvRow.values)
+ {
+ clOptions.push_back(value.c_str());
+ }
+
+ po::variables_map vm;
+ try
+ {
+ po::store(po::parse_command_line(static_cast<int>(clOptions.size()), clOptions.data(), desc), vm);
+
+ po::notify(vm);
+
+ CheckOptionDependencies(vm);
+ }
+ catch (const po::error& e)
+ {
+ std::cerr << e.what() << std::endl << std::endl;
+ std::cerr << desc << std::endl;
+ return EXIT_FAILURE;
+ }
+
+ // Get the preferred order of compute devices.
+ std::vector<armnn::BackendId> computeDevices = vm["compute"].as<std::vector<armnn::BackendId>>();
+
+ // Remove duplicates from the list of compute devices.
+ RemoveDuplicateDevices(computeDevices);
+
+ // Check that the specified compute devices are valid.
+ std::string invalidBackends;
+ if (!CheckRequestedBackendsAreValid(computeDevices, armnn::Optional<std::string&>(invalidBackends)))
+ {
+ BOOST_LOG_TRIVIAL(fatal) << "The list of preferred devices contains invalid backend IDs: "
+ << invalidBackends;
+ return EXIT_FAILURE;
+ }
+
+ return RunTest(modelFormat, inputTensorShapes, computeDevices, modelPath, inputNames,
+ inputTensorDataFilePaths, inputTypes, outputTypes, outputNames,
+ enableProfiling, enableFp16TurboMode, thresholdTime, subgraphId);
+} \ No newline at end of file