Discussion:
[meta-arago] [PATCH v4] armnn: bump SRCREV and add a demo example
Qin Su
2018-11-29 21:47:15 UTC
Permalink
Updated to the latest version of 18.08.
Updated to generate versioned library.
Created a demo example with mobileNet.
Added LIC_FILES_CHKSUM for mobilenet network topology.
Enabled the use of arm-compute-library shared libraries.

Signed-off-by: Qin Su <***@ti.com>
---
Changes in PACTH v4:
Removed Unicode "FEFF" from 0006-armnn-mobilenet-test-example.patch

Changes in PATCH v3:
1. Fixed patch files permission issue
2. Fixed LIC_FILES_CHKSUM issue of using Apache-2.0 LICENSE for mobilenet

Changes in PATCH v2:
1. Added Upstream-Status in patch files
2. Added "name=armnn" and "name=mobilenet"
3. Removed "dev-so" from INSANE_SKIP_${PN}
---

...0003-add-more-test-command-line-arguments.patch | 75 ++
.../armnn/0004-generate-versioned-library.patch | 61 ++
.../0005-add-armnn-mobilenet-test-example.patch | 67 ++
.../armnn/0006-armnn-mobilenet-test-example.patch | 886 +++++++++++++++++++++
...-enable-use-of-arm-compute-shared-library.patch | 31 +
.../recipes-support/armnn/armnn_git.bb | 43 +-
6 files changed, 1153 insertions(+), 10 deletions(-)
create mode 100644 meta-arago-extras/recipes-support/armnn/armnn/0003-add-more-test-command-line-arguments.patch
create mode 100644 meta-arago-extras/recipes-support/armnn/armnn/0004-generate-versioned-library.patch
create mode 100644 meta-arago-extras/recipes-support/armnn/armnn/0005-add-armnn-mobilenet-test-example.patch
create mode 100644 meta-arago-extras/recipes-support/armnn/armnn/0006-armnn-mobilenet-test-example.patch
create mode 100644 meta-arago-extras/recipes-support/armnn/armnn/0007-enable-use-of-arm-compute-shared-library.patch

diff --git a/meta-arago-extras/recipes-support/armnn/armnn/0003-add-more-test-command-line-arguments.patch b/meta-arago-extras/recipes-support/armnn/armnn/0003-add-more-test-command-line-arguments.patch
new file mode 100644
index 0000000..bcc4a65
--- /dev/null
+++ b/meta-arago-extras/recipes-support/armnn/armnn/0003-add-more-test-command-line-arguments.patch
@@ -0,0 +1,75 @@
+From ba19ce3c785fb92077f6309db1125f1ef32cb08a Mon Sep 17 00:00:00 2001
+From: Qin Su <***@ti.com>
+Date: Wed, 21 Nov 2018 15:14:24 -0500
+Subject: [PATCH] add more test command line arguments
+
+Upstream-Status: Inappropriate [TI only test code]
+Signed-off-by: Qin Su <***@ti.com>
+---
+ tests/InferenceTest.inl | 50 +++++++++++++++++++++++++++++++++++++++++++++++++
+ 1 file changed, 50 insertions(+)
+
+diff --git a/tests/InferenceTest.inl b/tests/InferenceTest.inl
+index 16df7ba..5808edb 100644
+--- a/tests/InferenceTest.inl
++++ b/tests/InferenceTest.inl
+@@ -299,6 +299,56 @@ int ClassifierInferenceTestMain(int argc,
+ TConstructDatabaseCallable constructDatabase,
+ const armnn::TensorShape* inputTensorShape)
+ {
++ int count;
++ const char *p_input;
++ char inmodelname[500];
++ char outtensorname[500];
++
++ /* parse command line */
++ for (count = 1; count < argc; count++)
++ {
++ if (*(argv[count]) == '+')
++ {
++ p_input = argv[count] + 1;
++ switch (*(p_input))
++ {
++ case 'i':
++ case 'I':
++ strcpy(inmodelname, p_input + 2);
++ modelFilename = &inmodelname[0];
++ std::cout << "Input model = " << modelFilename << std::endl;
++ break;
++ case 'o':
++ case 'O':
++ strcpy(outtensorname, p_input + 2);
++ outputBindingName = &outtensorname[0];
++ std::cout << "out tensor name = " << outputBindingName << std::endl;
++ break;
++ default:
++ break;
++ }
++ }
++ else if (*(argv[count]) == '-')
++ {
++ p_input = argv[count] + 1;
++ switch (*(p_input))
++ {
++ case '-':
++ p_input = argv[count] + 2;
++ case 'h':
++ case 'H':
++ std::cout <<"\nAdditional Options: " << std::endl;
++ std::cout <<" +i Set user specified inference model name." << std::endl;
++ std::cout <<" If not set, default name is used." << std::endl;
++ std::cout <<" +o Set user specified output tensor name." << std::endl;
++ std::cout <<" If not set, default name is used.\n" << std::endl;
++ break;
++ default:
++ break;
++ }
++ }
++ }
++
+ return InferenceTestMain(argc, argv, defaultTestCaseIds,
+ [=]
+ ()
+--
+1.9.1
+
diff --git a/meta-arago-extras/recipes-support/armnn/armnn/0004-generate-versioned-library.patch b/meta-arago-extras/recipes-support/armnn/armnn/0004-generate-versioned-library.patch
new file mode 100644
index 0000000..57e8a4b
--- /dev/null
+++ b/meta-arago-extras/recipes-support/armnn/armnn/0004-generate-versioned-library.patch
@@ -0,0 +1,61 @@
+From 548f8e308b15e396241149e1c89ce7ffecf6242c Mon Sep 17 00:00:00 2001
+From: Qin Su <***@ti.com>
+Date: Tue, 27 Nov 2018 18:08:06 -0500
+Subject: [PATCH] generate versioned library
+
+Upstream-Status: Inappropriate [TI only test code]
+
+Signed-off-by: Qin Su <***@ti.com>
+---
+ CMakeLists.txt | 7 ++++++-
+ 1 file changed, 6 insertions(+), 1 deletion(-)
+
+diff --git a/CMakeLists.txt b/CMakeLists.txt
+index c06a869..cc11476 100644
+--- a/CMakeLists.txt
++++ b/CMakeLists.txt
+@@ -73,7 +73,7 @@ if(BUILD_CAFFE_PARSER)
+
+ target_link_libraries(armnnCaffeParser armnn)
+ target_link_libraries(armnnCaffeParser ${PROTOBUF_LIBRARIES})
+-
++ set_target_properties( armnnCaffeParser PROPERTIES VERSION ${GENERIC_LIB_VERSION} SOVERSION ${GENERIC_LIB_SOVERSION} )
+ endif()
+
+ if(BUILD_ONNX_PARSER)
+@@ -97,6 +97,7 @@ if(BUILD_ONNX_PARSER)
+
+ # Protobuf
+ target_link_libraries(armnnOnnxParser ${PROTOBUF_LIBRARIES})
++ set_target_properties( armnnOnnxParser PROPERTIES VERSION ${GENERIC_LIB_VERSION} SOVERSION ${GENERIC_LIB_SOVERSION} )
+ endif()
+
+ if(BUILD_TF_PARSER)
+@@ -120,6 +121,7 @@ if(BUILD_TF_PARSER)
+
+ # Protobuf (use the specific version tensorflow wants)
+ target_link_libraries(armnnTfParser ${PROTOBUF_LIBRARIES})
++ set_target_properties( armnnTfParser PROPERTIES VERSION ${GENERIC_LIB_VERSION} SOVERSION ${GENERIC_LIB_SOVERSION} )
+ endif()
+
+ if(BUILD_TF_LITE_PARSER)
+@@ -136,6 +138,7 @@ if(BUILD_TF_LITE_PARSER)
+
+ target_link_libraries(armnnTfLiteParser ${Boost_FILESYSTEM_LIBRARY} ${Boost_THREAD_LIBRARY})
+ target_link_libraries(armnnTfLiteParser armnn ${FLATBUFFERS_LIBRARY})
++ set_target_properties( armnnTfLiteParser PROPERTIES VERSION ${GENERIC_LIB_VERSION} SOVERSION ${GENERIC_LIB_SOVERSION} )
+ endif()
+
+ # ArmNN source files required for all build options
+@@ -610,6 +613,8 @@ if(PROFILING_BACKEND_STREAMLINE)
+ target_link_libraries(armnn pthread)
+ endif()
+
++set_target_properties( armnn PROPERTIES VERSION ${GENERIC_LIB_VERSION} SOVERSION ${GENERIC_LIB_SOVERSION} )
++
+ if(BUILD_UNIT_TESTS)
+ set(unittest_sources)
+ list(APPEND unittest_sources
+--
+1.9.1
+
diff --git a/meta-arago-extras/recipes-support/armnn/armnn/0005-add-armnn-mobilenet-test-example.patch b/meta-arago-extras/recipes-support/armnn/armnn/0005-add-armnn-mobilenet-test-example.patch
new file mode 100644
index 0000000..c996ede
--- /dev/null
+++ b/meta-arago-extras/recipes-support/armnn/armnn/0005-add-armnn-mobilenet-test-example.patch
@@ -0,0 +1,67 @@
+From 47ce3bcdb8e43ef517dcd8db7477a0514212ba4f Mon Sep 17 00:00:00 2001
+From: Qin Su <***@ti.com>
+Date: Tue, 27 Nov 2018 18:11:46 -0500
+Subject: [PATCH] add armnn mobilenet test example
+
+Upstream-Status: Inappropriate [TI only test code]
+
+Signed-off-by: Qin Su <***@ti.com>
+---
+ tests/CMakeLists.txt | 40 ++++++++++++++++++++++++++++++++++++++++
+ 1 file changed, 40 insertions(+)
+
+diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt
+index 0979d55..7c0cf2a 100644
+--- a/tests/CMakeLists.txt
++++ b/tests/CMakeLists.txt
+@@ -1,3 +1,6 @@
++find_package( OpenCV REQUIRED )
++include_directories( ${OpenCV_INCLUDE_DIRS} )
++
+ # UnitTests
+ include(CheckIncludeFiles)
+
+@@ -226,3 +229,40 @@ if (BUILD_CAFFE_PARSER OR BUILD_TF_PARSER OR BUILD_TF_LITE_PARSER OR BUILD_ONNX_
+ ${Boost_PROGRAM_OPTIONS_LIBRARY})
+ addDllCopyCommands(ExecuteNetwork)
+ endif()
++
++if (BUILD_ARMNN_EXAMPLES)
++ set(ArmnnExamples_sources
++ ArmnnExamples/ArmnnExamples.cpp)
++
++ add_executable_ex(ArmnnExamples ${ArmnnExamples_sources})
++
++ target_include_directories(ArmnnExamples PRIVATE ../src/armnnUtils)
++ target_include_directories(ArmnnExamples PRIVATE ../src/armnn)
++
++ if (BUILD_CAFFE_PARSER)
++ target_link_libraries(ArmnnExamples armnnCaffeParser)
++ endif()
++ if (BUILD_TF_PARSER)
++ target_link_libraries(ArmnnExamples armnnTfParser)
++ endif()
++
++ if (BUILD_TF_LITE_PARSER)
++ target_link_libraries(ArmnnExamples armnnTfLiteParser)
++ endif()
++ if (BUILD_ONNX_PARSER)
++ target_link_libraries(ArmnnExamples armnnOnnxParser)
++ endif()
++
++ target_link_libraries(ArmnnExamples armnn)
++ target_link_libraries(ArmnnExamples ${CMAKE_THREAD_LIBS_INIT})
++ if(OPENCL_LIBRARIES)
++ target_link_libraries(ArmnnExamples ${OPENCL_LIBRARIES})
++ endif()
++
++ target_link_libraries(ArmnnExamples
++ ${Boost_SYSTEM_LIBRARY}
++ ${Boost_FILESYSTEM_LIBRARY}
++ ${Boost_PROGRAM_OPTIONS_LIBRARY}
++ ${OpenCV_LIBS})
++ addDllCopyCommands(ArmnnExamples)
++endif()
+--
+1.9.1
+
diff --git a/meta-arago-extras/recipes-support/armnn/armnn/0006-armnn-mobilenet-test-example.patch b/meta-arago-extras/recipes-support/armnn/armnn/0006-armnn-mobilenet-test-example.patch
new file mode 100644
index 0000000..e744587
--- /dev/null
+++ b/meta-arago-extras/recipes-support/armnn/armnn/0006-armnn-mobilenet-test-example.patch
@@ -0,0 +1,886 @@
+From 5da1d1c637a782926f99403d092f01edc028d99d Mon Sep 17 00:00:00 2001
+From: Qin Su <***@ti.com>
+Date: Wed, 21 Nov 2018 16:18:01 -0500
+Subject: [PATCH] armnn mobilenet test example
+
+Upstream-Status: Inappropriate [TI only test code]
+Signed-off-by: Qin Su <***@ti.com>
+---
+ tests/ArmnnExamples/ArmnnExamples.cpp | 865 ++++++++++++++++++++++++++++++++++
+ 1 file changed, 865 insertions(+)
+ create mode 100644 tests/ArmnnExamples/ArmnnExamples.cpp
+
+diff --git a/tests/ArmnnExamples/ArmnnExamples.cpp b/tests/ArmnnExamples/ArmnnExamples.cpp
+new file mode 100644
+index 0000000..89faf4f
+--- /dev/null
++++ b/tests/ArmnnExamples/ArmnnExamples.cpp
+@@ -0,0 +1,865 @@
++/******************************************************************************
++ * Copyright (c) 2018, Texas Instruments Incorporated - http://www.ti.com/
++ * All rights reserved.
++ *
++ * Redistribution and use in source and binary forms, with or without
++ * modification, are permitted provided that the following conditions are met:
++ * * Redistributions of source code must retain the above copyright
++ * notice, this list of conditions and the following disclaimer.
++ * * Redistributions in binary form must reproduce the above copyright
++ * notice, this list of conditions and the following disclaimer in the
++ * documentation and/or other materials provided with the distribution.
++ * * Neither the name of Texas Instruments Incorporated nor the
++ * names of its contributors may be used to endorse or promote products
++ * derived from this software without specific prior written permission.
++ *
++ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
++ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
++ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
++ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
++ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
++ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
++ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
++ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
++ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
++ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
++ * THE POSSIBILITY OF SUCH DAMAGE.
++ *****************************************************************************///
++// Copyright © 2017 Arm Ltd. All rights reserved.
++// See LICENSE file in the project root for full license information.
++//
++#include "armnn/ArmNN.hpp"
++
++#include <utility>
++#include <armnn/TypesUtils.hpp>
++
++#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 <iostream>
++#include <fstream>
++#include <functional>
++#include <future>
++#include <algorithm>
++#include <iterator>
++#include<vector>
++
++#include <signal.h>
++#include "opencv2/core.hpp"
++#include "opencv2/imgproc.hpp"
++#include "opencv2/highgui.hpp"
++#include "opencv2/videoio.hpp"
++#include <time.h>
++
++using namespace cv;
++
++#define INPUT_IMAGE 0
++#define INPUT_VIDEO 1
++#define INPUT_CAMERA 2
++
++Mat test_image;
++Rect rectCrop;
++
++time_point<high_resolution_clock> predictStart;
++time_point<high_resolution_clock> predictEnd;
++
++void imagenetCallBackFunc(int event, int x, int y, int flags, void* userdata)
++{
++ if ( event == EVENT_RBUTTONDOWN )
++ {
++ std::cout << "Right button of the mouse is clicked - position (" << x << ", " << y << ")" << " ... prepare to exit!" << std::endl;
++ exit(0);
++ }
++}
++
++inline float Lerpfloat(float a, float b, float w)
++{
++ return w * b + (1.f - w) * a;
++}
++
++// Load a single image
++struct ImageData
++{
++ unsigned int m_width;
++ unsigned int m_height;
++ unsigned int m_chnum;
++ unsigned int m_size;
++ std::vector<uint8_t> m_image;
++};
++
++std::unique_ptr<ImageData> loadImageData(std::string image_path, VideoCapture &cap, cv::Mat img, int input_type)
++{
++ //cv::Mat img;
++ if (input_type == INPUT_IMAGE)
++ {
++ /* use OpenCV to get the image */
++ img = cv::imread(image_path, CV_LOAD_IMAGE_COLOR);
++ }
++ cv::cvtColor(img, img, CV_BGR2RGB); //convert image format from BGR(openCV format) to RGB (armnn required format).
++
++ // store image and label in Image
++ std::unique_ptr<ImageData> ret(new ImageData);
++ ret->m_width = static_cast<unsigned int>(img.cols);
++ ret->m_height = static_cast<unsigned int>(img.rows);
++ ret->m_chnum = static_cast<unsigned int>(img.channels());
++ ret->m_size = static_cast<unsigned int>(img.cols*img.rows*img.channels());
++ ret->m_image.resize(ret->m_size);
++
++ for (unsigned int i = 0; i < ret->m_size; i++)
++ {
++ ret->m_image[i] = static_cast<uint8_t>(img.data[i]);
++ }
++ return ret;
++}
++
++std::vector<float> ResizeBilinear(std::vector<uint8_t> input,
++ const unsigned int inWidth,
++ const unsigned int inHeight,
++ const unsigned int inChnum,
++ const unsigned int outputWidth,
++ const unsigned int outputHeight)
++{
++ std::vector<float> out;
++ out.resize(outputWidth * outputHeight * 3);
++
++ // We follow the definition of TensorFlow and AndroidNN: the top-left corner of a texel in the output
++ // image is projected into the input image to figure out the interpolants and weights. Note that this
++ // will yield different results than if projecting the centre of output texels.
++
++ const unsigned int inputWidth = inWidth;
++ const unsigned int inputHeight = inHeight;
++
++ // How much to scale pixel coordinates in the output image to get the corresponding pixel coordinates
++ // in the input image.
++ const float scaleY = boost::numeric_cast<float>(inputHeight) / boost::numeric_cast<float>(outputHeight);
++ const float scaleX = boost::numeric_cast<float>(inputWidth) / boost::numeric_cast<float>(outputWidth);
++
++ uint8_t rgb_x0y0[3];
++ uint8_t rgb_x1y0[3];
++ uint8_t rgb_x0y1[3];
++ uint8_t rgb_x1y1[3];
++ unsigned int pixelOffset00, pixelOffset10, pixelOffset01, pixelOffset11;
++ for (unsigned int y = 0; y < outputHeight; ++y)
++ {
++ // Corresponding real-valued height coordinate in input image.
++ const float iy = boost::numeric_cast<float>(y) * scaleY;
++ // Discrete height coordinate of top-left texel (in the 2x2 texel area used for interpolation).
++ const float fiy = floorf(iy);
++ const unsigned int y0 = boost::numeric_cast<unsigned int>(fiy);
++
++ // Interpolation weight (range [0,1])
++ const float yw = iy - fiy;
++
++ for (unsigned int x = 0; x < outputWidth; ++x)
++ {
++ // Real-valued and discrete width coordinates in input image.
++ const float ix = boost::numeric_cast<float>(x) * scaleX;
++ const float fix = floorf(ix);
++ const unsigned int x0 = boost::numeric_cast<unsigned int>(fix);
++
++ // Interpolation weight (range [0,1]).
++ const float xw = ix - fix;
++
++ // Discrete width/height coordinates of texels below and to the right of (x0, y0).
++ const unsigned int x1 = std::min(x0 + 1, inputWidth - 1u);
++ const unsigned int y1 = std::min(y0 + 1, inputHeight - 1u);
++
++ pixelOffset00 = x0 * inChnum + y0 * inputWidth * inChnum;
++ pixelOffset10 = x1 * inChnum + y0 * inputWidth * inChnum;
++ pixelOffset01 = x0 * inChnum + y1 * inputWidth * inChnum;
++ pixelOffset11 = x1 * inChnum + y1 * inputWidth * inChnum;
++ for (unsigned int c = 0; c < 3; ++c)
++ {
++ rgb_x0y0[c] = input[pixelOffset00+c];
++ rgb_x1y0[c] = input[pixelOffset10+c];
++ rgb_x0y1[c] = input[pixelOffset01+c];
++ rgb_x1y1[c] = input[pixelOffset11+c];
++ }
++
++ for (unsigned c=0; c<3; ++c)
++ {
++ const float ly0 = Lerpfloat(float(rgb_x0y0[c]), float(rgb_x1y0[c]), xw);
++ const float ly1 = Lerpfloat(float(rgb_x0y1[c]), float(rgb_x1y1[c]), xw);
++ const float l = Lerpfloat(ly0, ly1, yw);
++ out[(3*((y*outputWidth)+x)) + c] = static_cast<float>(l)/255.0f;
++ }
++ }
++ }
++ return out;
++}
++
++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)
++{
++ 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("\t ,;:"), 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<typename T>
++std::vector<T> ParseArray(std::istream& stream);
++
++template<>
++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)); });
++}
++
++void RemoveDuplicateDevices(std::vector<armnn::Compute>& 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());
++}
++
++bool CheckDevicesAreValid(const std::vector<armnn::Compute>& computeDevices)
++{
++ return (!computeDevices.empty()
++ && std::none_of(computeDevices.begin(), computeDevices.end(),
++ [](armnn::Compute c){ return c == armnn::Compute::Undefined; }));
++}
++
++} // namespace
++
++template<typename TParser, typename TDataType>
++int MainImpl(const char* modelPath,
++ bool isModelBinary,
++ const std::vector<armnn::Compute>& computeDevice,
++ const char* inputName,
++ const armnn::TensorShape* inputTensorShape,
++ const char* inputTensorDataFilePath,
++ const char* outputName,
++ bool enableProfiling,
++ const size_t number_frame,
++ const std::shared_ptr<armnn::IRuntime>& runtime = nullptr)
++{
++ // Loads input tensor.
++ std::vector<uint8_t> input;
++ std::vector<float> input_resized;
++
++ try
++ {
++ // Creates an InferenceModel, which will parse the model and load it into an IRuntime.
++ typename InferenceModel<TParser, TDataType>::Params params;
++ //const armnn::TensorShape inputTensorShape({ 1, 224, 224 3});
++ params.m_ModelPath = modelPath;
++ params.m_IsModelBinary = isModelBinary;
++ params.m_ComputeDevice = computeDevice;
++ params.m_InputBinding = inputName;
++ params.m_InputTensorShape = inputTensorShape;
++ params.m_OutputBinding = outputName;
++ params.m_EnableProfiling = enableProfiling;
++ params.m_SubgraphId = 0;
++ InferenceModel<TParser, TDataType> model(params, runtime);
++
++ VideoCapture cap;
++ int input_type = INPUT_IMAGE;
++ std::string filename = inputTensorDataFilePath;
++
++ size_t i = filename.rfind("camera_live_input", filename.length());
++ if (i != string::npos)
++ {
++ cap = VideoCapture(1);
++ namedWindow("ARMNN MobileNet Example", WINDOW_AUTOSIZE | CV_GUI_NORMAL);
++ input_type = INPUT_CAMERA; //camera input
++ }
++ else if((filename.substr(filename.find_last_of(".") + 1) == "mp4") ||
++ (filename.substr(filename.find_last_of(".") + 1) == "mov") ||
++ (filename.substr(filename.find_last_of(".") + 1) == "avi") )
++ {
++ cap = VideoCapture(inputTensorDataFilePath);
++ if (! cap.isOpened())
++ {
++ std::cout << "Cannot open video input: " << inputTensorDataFilePath << std::endl;
++ return (-1);
++ }
++
++ namedWindow("ARMNN MobileNet Example", WINDOW_AUTOSIZE | CV_GUI_NORMAL);
++ input_type = INPUT_VIDEO; //video clip input
++ }
++ if (input_type != INPUT_IMAGE)
++ {
++ //set the callback function for any mouse event. Used for right click mouse to exit the program.
++ setMouseCallback("ARMNN MobileNet Example", imagenetCallBackFunc, NULL);
++ }
++
++ for (unsigned int i=0; i < number_frame; i++)
++ {
++ if (input_type != INPUT_IMAGE)
++ {
++ cap.grab();
++ cap.retrieve(test_image);
++ }
++ std::unique_ptr<ImageData> inputData = loadImageData(inputTensorDataFilePath, cap, test_image, input_type);
++ input.resize(inputData->m_size);
++
++ input = std::move(inputData->m_image);
++ input_resized = ResizeBilinear(input, inputData->m_width, inputData->m_height, inputData->m_chnum, 224, 224);
++ // Executes the model.
++ std::vector<TDataType> output(model.GetOutputSize());
++
++ predictStart = high_resolution_clock::now();
++
++ model.Run(input_resized, output);
++
++ predictEnd = high_resolution_clock::now();
++
++ // duration<double> will convert the time difference into seconds as a double by default.
++ double timeTakenS = duration<double>(predictEnd - predictStart).count();
++ double preformance_ret = static_cast<double>(1.0/timeTakenS);
++
++ // Convert 1-hot output to an integer label and print
++ int label = static_cast<int>(std::distance(output.begin(), std::max_element(output.begin(), output.end())));
++ std::fstream file("/usr/share/arm/armnn/models/labels.txt");
++ std::string predict_target_name;
++ for (int i=0; i <= label; i++)
++ {
++ std::getline(file, predict_target_name);
++ }
++ std::cout << "Predicted: " << predict_target_name << std::endl;
++ std::cout << "Performance (FPS): " << preformance_ret << std::endl;
++
++ if (input_type != INPUT_IMAGE)
++ {
++ //convert image format back to BGR for OpenCV imshow from RGB format required by armnn.
++ cv::cvtColor(test_image, test_image, CV_RGB2BGR);
++ // output identified object name on top of input image
++ cv::putText(test_image, predict_target_name,
++ cv::Point(rectCrop.x + 5,rectCrop.y + 20), // Coordinates
++ cv::FONT_HERSHEY_COMPLEX_SMALL, // Font
++ 1.0, // Scale. 2.0 = 2x bigger
++ cv::Scalar(0,0,255), // Color
++ 1, // Thickness
++ 8); // Line type
++
++ // output preformance in FPS on top of input image
++ std::string preformance_ret_string = "Performance (FPS): " + boost::lexical_cast<std::string>(preformance_ret);
++ cv::putText(test_image, preformance_ret_string,
++ cv::Point(rectCrop.x + 5,rectCrop.y + 40), // Coordinates
++ cv::FONT_HERSHEY_COMPLEX_SMALL, // Font
++ 1.0, // Scale. 2.0 = 2x bigger
++ cv::Scalar(0,0,255), // Color
++ 1, // Thickness
++ 8); // Line type
++
++ cv::imshow("ARMNN MobileNet Example", test_image);
++ waitKey(2);
++ }
++ }
++ }
++ 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& modelFormat,
++ const std::string& inputTensorShapeStr,
++ const vector<armnn::Compute>& computeDevice,
++ const std::string& modelPath,
++ const std::string& inputName,
++ const std::string& inputTensorDataFilePath,
++ const std::string& outputName,
++ bool enableProfiling,
++ const size_t subgraphId,
++ const std::shared_ptr<armnn::IRuntime>& runtime = nullptr)
++{
++ // 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;
++ }
++
++ // Parse input tensor shape from the string we got from the command-line.
++ std::unique_ptr<armnn::TensorShape> inputTensorShape;
++ if (!inputTensorShapeStr.empty())
++ {
++ std::stringstream ss(inputTensorShapeStr);
++ std::vector<unsigned int> dims = ParseArray<unsigned int>(ss);
++
++ try
++ {
++ // Coverity fix: An exception of type armnn::InvalidArgumentException is thrown and never caught.
++ inputTensorShape = 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;
++ }
++ }
++
++ // Forward to implementation based on the parser type
++ if (modelFormat.find("caffe") != std::string::npos)
++ {
++#if defined(ARMNN_CAFFE_PARSER)
++ return MainImpl<armnnCaffeParser::ICaffeParser, float>(modelPath.c_str(), isModelBinary, computeDevice,
++ inputName.c_str(), inputTensorShape.get(),
++ inputTensorDataFilePath.c_str(), outputName.c_str(),
++ enableProfiling, 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,
++ inputName.c_str(), inputTensorShape.get(),
++ inputTensorDataFilePath.c_str(), outputName.c_str(),
++ enableProfiling, 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,
++ inputName.c_str(), inputTensorShape.get(),
++ inputTensorDataFilePath.c_str(), outputName.c_str(),
++ enableProfiling, 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,
++ inputName.c_str(), inputTensorShape.get(),
++ inputTensorDataFilePath.c_str(), outputName.c_str(),
++ enableProfiling, 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)
++{
++ std::string modelFormat;
++ std::string modelPath;
++ std::string inputName;
++ std::string inputTensorShapeStr;
++ std::string inputTensorDataFilePath;
++ std::string outputName;
++
++ size_t subgraphId = 0;
++
++ po::options_description desc("Options");
++ try
++ {
++ desc.add_options()
++ ("model-format,f", po::value(&modelFormat),
++ "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. .caffemodel, .prototxt, .tflite,"
++ " .onnx")
++ ("compute,c", po::value<std::vector<armnn::Compute>>()->multitoken(),
++ "The preferred order of devices to run layers on by default. Possible choices: CpuAcc, CpuRef, GpuAcc")
++ ("input-name,i", po::value(&inputName), "Identifier of the input tensor in the network.")
++ ("input-tensor-shape,s", po::value(&inputTensorShapeStr),
++ "The shape of the input tensor in the network as a flat array of integers separated by whitespace. "
++ "This parameter is optional, depending on the network.")
++ ("input-tensor-data,d", po::value(&inputTensorDataFilePath),
++ "Input test file name. It can be image/video clip file name or use 'camera_live_input' to select camera input.")
++ ("output-name,o", po::value(&outputName), "Identifier of the output tensor in the network.")
++ ("event-based-profiling,e", po::bool_switch()->default_value(false),
++ "Enables built in profiler. If unset, defaults to off.")
++ ("number-frame", po::value<size_t>(&subgraphId)->default_value(1), "Number of frames to process " );
++ }
++ 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;
++ }
++
++ // Remove leading and trailing whitespaces from the parsed arguments.
++ boost::trim(modelFormat);
++ boost::trim(modelPath);
++ boost::trim(inputName);
++ boost::trim(inputTensorShapeStr);
++ boost::trim(inputTensorDataFilePath);
++ boost::trim(outputName);
++
++ // Get the value of the switch arguments.
++ bool enableProfiling = vm["event-based-profiling"].as<bool>();
++
++ // Get the preferred order of compute devices.
++ std::vector<armnn::Compute> computeDevices = vm["compute"].as<std::vector<armnn::Compute>>();
++
++ // Remove duplicates from the list of compute devices.
++ RemoveDuplicateDevices(computeDevices);
++
++ // Check that the specified compute devices are valid.
++ if (!CheckDevicesAreValid(computeDevices))
++ {
++ BOOST_LOG_TRIVIAL(fatal) << "The list of preferred devices contains an invalid compute";
++ return EXIT_FAILURE;
++ }
++
++ return RunTest(modelFormat, inputTensorShapeStr, computeDevices,
++ modelPath, inputName, inputTensorDataFilePath, outputName, enableProfiling, subgraphId, runtime);
++}
++
++int main(int argc, const char* argv[])
++{
++ // Configures logging for both the ARMNN library and this test program.
++#ifdef NDEBUG
++ armnn::LogSeverity level = armnn::LogSeverity::Info;
++#else
++ armnn::LogSeverity level = armnn::LogSeverity::Debug;
++#endif
++ armnn::ConfigureLogging(true, true, level);
++ armnnUtils::ConfigureLogging(boost::log::core::get().get(), true, true, level);
++
++ std::string testCasesFile;
++
++ std::string modelFormat = "tensorflow-binary";
++ std::string modelPath = "/usr/share/arm/armnn/models/mobilenet_v1_1.0_224_frozen.pb";
++ std::string inputName = "input";
++ std::string inputTensorShapeStr = "1 224 224 3";
++ std::string inputTensorDataFilePath = "/usr/share/arm/armnn/testvecs/test2.mp4";
++ std::string outputName = "MobilenetV1/Predictions/Reshape_1";
++ std::vector<armnn::Compute> computeDevices = {armnn::Compute::CpuAcc};
++
++ // Catch ctrl-c to ensure a clean exit
++ signal(SIGABRT, exit);
++ signal(SIGTERM, exit);
++
++ if (argc == 1)
++ {
++ return RunTest(modelFormat, inputTensorShapeStr, computeDevices,
++ modelPath, inputName, inputTensorDataFilePath, outputName, false, 1000);
++ }
++ else
++ {
++ size_t subgraphId = 0;
++ po::options_description desc("Options");
++ try
++ {
++ desc.add_options()
++ ("help", "Display usage information")
++ ("test-cases,t", po::value(&testCasesFile), "Path to a CSV file containing test cases to run. "
++ "If set, further parameters -- with the exception of compute device and concurrency -- will be ignored, "
++ "as they are expected to be defined in the file for each test in particular.")
++ ("concurrent,n", po::bool_switch()->default_value(false),
++ "Whether or not the test cases should be executed in parallel")
++ ("model-format,f", po::value(&modelFormat),
++ "caffe-binary, caffe-text, onnx-binary, onnx-text, tflite-binary, tensorflow-binary or tensorflow-text.")
++ ("model-path,m", po::value(&modelPath), "Path to model file, e.g. .caffemodel, .prototxt,"
++ " .tflite, .onnx")
++ ("compute,c", po::value<std::vector<armnn::Compute>>()->multitoken(),
++ "The preferred order of devices to run layers on by default. Possible choices: CpuAcc, CpuRef, GpuAcc")
++ ("input-name,i", po::value(&inputName), "Identifier of the input tensor in the network.")
++ ("input-tensor-shape,s", po::value(&inputTensorShapeStr),
++ "The shape of the input tensor in the network as a flat array of integers separated by whitespace. "
++ "This parameter is optional, depending on the network.")
++ ("input-tensor-data,d", po::value(&inputTensorDataFilePath),
++ "Input test file name. It can be image/video clip file name or use 'camera_live_input' to select camera input.")
++ ("output-name,o", po::value(&outputName), "Identifier of the output tensor in the network.")
++ ("event-based-profiling,e", po::bool_switch()->default_value(false),
++ "Enables built in profiler. If unset, defaults to off.")
++ ("number_frame", po::value<size_t>(&subgraphId)->default_value(1), "Number of frames to process.");
++ }
++ 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;
++ }
++
++ // Parses the command-line.
++ po::variables_map vm;
++ try
++ {
++ po::store(po::parse_command_line(argc, argv, desc), vm);
++
++ if (CheckOption(vm, "help") || argc <= 1)
++ {
++ std::cout << "Executes a neural network model using the provided input tensor. " << std::endl;
++ std::cout << "Prints the resulting output tensor." << std::endl;
++ std::cout << std::endl;
++ std::cout << desc << std::endl;
++ return EXIT_SUCCESS;
++ }
++
++ po::notify(vm);
++ }
++ catch (const po::error& e)
++ {
++ std::cerr << e.what() << std::endl << std::endl;
++ std::cerr << desc << std::endl;
++ return EXIT_FAILURE;
++ }
++
++ // Get the value of the switch arguments.
++ bool concurrent = vm["concurrent"].as<bool>();
++ bool enableProfiling = vm["event-based-profiling"].as<bool>();
++
++ // Check whether we have to load test cases from a file.
++ if (CheckOption(vm, "test-cases"))
++ {
++ // Check that the file exists.
++ if (!boost::filesystem::exists(testCasesFile))
++ {
++ BOOST_LOG_TRIVIAL(fatal) << "Given file \"" << testCasesFile << "\" does not exist";
++ return EXIT_FAILURE;
++ }
++
++ // Parse CSV file and extract test cases
++ armnnUtils::CsvReader reader;
++ std::vector<armnnUtils::CsvRow> testCases = reader.ParseFile(testCasesFile);
++
++ // Check that there is at least one test case to run
++ if (testCases.empty())
++ {
++ BOOST_LOG_TRIVIAL(fatal) << "Given file \"" << testCasesFile << "\" has no test cases";
++ return EXIT_FAILURE;
++ }
++
++ // Create runtime
++ armnn::IRuntime::CreationOptions options;
++ std::shared_ptr<armnn::IRuntime> runtime(armnn::IRuntime::Create(options));
++
++ const std::string executableName("ExecuteNetwork");
++
++ // Check whether we need to run the test cases concurrently
++ if (concurrent)
++ {
++ std::vector<std::future<int>> results;
++ results.reserve(testCases.size());
++
++ // Run each test case in its own thread
++ for (auto& testCase : testCases)
++ {
++ testCase.values.insert(testCase.values.begin(), executableName);
++ results.push_back(std::async(std::launch::async, RunCsvTest, std::cref(testCase), std::cref(runtime)));
++ }
++
++ // Check results
++ for (auto& result : results)
++ {
++ if (result.get() != EXIT_SUCCESS)
++ {
++ return EXIT_FAILURE;
++ }
++ }
++ }
++ else
++ {
++ // Run tests sequentially
++ for (auto& testCase : testCases)
++ {
++ testCase.values.insert(testCase.values.begin(), executableName);
++ if (RunCsvTest(testCase, runtime) != EXIT_SUCCESS)
++ {
++ return EXIT_FAILURE;
++ }
++ }
++ }
++
++ return EXIT_SUCCESS;
++ }
++ else // Run single test
++ {
++ // Get the preferred order of compute devices.
++ std::vector<armnn::Compute> computeDevices = vm["compute"].as<std::vector<armnn::Compute>>();
++
++ // Remove duplicates from the list of compute devices.
++ RemoveDuplicateDevices(computeDevices);
++ // Check that the specified compute devices are valid.
++ if (!CheckDevicesAreValid(computeDevices))
++ {
++ BOOST_LOG_TRIVIAL(fatal) << "The list of preferred devices contains an invalid compute";
++ return EXIT_FAILURE;
++ }
++
++ try
++ {
++ CheckOptionDependencies(vm);
++ }
++ catch (const po::error& e)
++ {
++ std::cerr << e.what() << std::endl << std::endl;
++ std::cerr << desc << std::endl;
++ return EXIT_FAILURE;
++ }
++ return RunTest(modelFormat, inputTensorShapeStr, computeDevices,
++ modelPath, inputName, inputTensorDataFilePath, outputName, enableProfiling, subgraphId);
++ }
++ }
++}
++
+--
+1.9.1
+
diff --git a/meta-arago-extras/recipes-support/armnn/armnn/0007-enable-use-of-arm-compute-shared-library.patch b/meta-arago-extras/recipes-support/armnn/armnn/0007-enable-use-of-arm-compute-shared-library.patch
new file mode 100644
index 0000000..7d0d7e5
--- /dev/null
+++ b/meta-arago-extras/recipes-support/armnn/armnn/0007-enable-use-of-arm-compute-shared-library.patch
@@ -0,0 +1,31 @@
+From 38e8e4bc03a4c1ee801f6af50be94ccd971bf3eb Mon Sep 17 00:00:00 2001
+From: Qin Su <***@ti.com>
+Date: Tue, 27 Nov 2018 18:15:49 -0500
+Subject: [PATCH] enable use of arm compute shared library
+
+Upstream-Status: Inappropriate [configuration]
+
+Signed-off-by: Qin Su <***@ti.com>
+---
+ cmake/GlobalConfig.cmake | 5 +++++
+ 1 file changed, 5 insertions(+)
+
+diff --git a/cmake/GlobalConfig.cmake b/cmake/GlobalConfig.cmake
+index 491d87a..4cf40a2 100644
+--- a/cmake/GlobalConfig.cmake
++++ b/cmake/GlobalConfig.cmake
+@@ -285,6 +285,11 @@ if(ARMCOMPUTENEON OR ARMCOMPUTECL)
+ find_library(ARMCOMPUTE_CORE_LIBRARY_DEBUG NAMES arm_compute_core-static)
+ find_library(ARMCOMPUTE_CORE_LIBRARY_RELEASE NAMES arm_compute_core-static)
+
++ find_library(ARMCOMPUTE_LIBRARY_DEBUG NAMES arm_compute)
++ find_library(ARMCOMPUTE_LIBRARY_RELEASE NAMES arm_compute)
++ find_library(ARMCOMPUTE_CORE_LIBRARY_DEBUG NAMES arm_compute_core)
++ find_library(ARMCOMPUTE_CORE_LIBRARY_RELEASE NAMES arm_compute_core)
++
+ set(ARMCOMPUTE_LIBRARIES
+ debug ${ARMCOMPUTE_LIBRARY_DEBUG} ${ARMCOMPUTE_CORE_LIBRARY_DEBUG}
+ optimized ${ARMCOMPUTE_LIBRARY_RELEASE} ${ARMCOMPUTE_CORE_LIBRARY_RELEASE} )
+--
+1.9.1
+
diff --git a/meta-arago-extras/recipes-support/armnn/armnn_git.bb b/meta-arago-extras/recipes-support/armnn/armnn_git.bb
index 726a9da..b3ac84d 100644
--- a/meta-arago-extras/recipes-support/armnn/armnn_git.bb
+++ b/meta-arago-extras/recipes-support/armnn/armnn_git.bb
@@ -1,23 +1,39 @@
SUMMARY = "ARM Neural Network SDK"
DESCRIPTION = "Linux software and tools to enable machine learning (Caffe/Tensorflow) workloads on power-efficient devices"
-LICENSE = "MIT"
-LIC_FILES_CHKSUM = "file://LICENSE;md5=3e14a924c16f7d828b8335a59da64074"
+LICENSE = "MIT & Apache-2.0"
+# Apache-2.0 license applies to mobilenet tarball
+LIC_FILES_CHKSUM = "file://LICENSE;md5=3e14a924c16f7d828b8335a59da64074 \
+ file://${COMMON_LICENSE_DIR}/Apache-2.0;md5=89aea4e17d99a7cacdbeed46a0096b10"

-PV = "20180502"
+PV = "18.08"
+PV_MAJOR = "${@d.getVar('PV',d,1).split('.')[0]}"

BRANCH = "master"
-SRCREV = "4c7098bfeab1ffe1cdc77f6c15548d3e73274746"
+SRCREV = "c577f2c6a3b4ddb6ba87a882723c53a248afbeba"
+SRCREV_tidl-api = "7e9a3942ec38efd64d45e34c10cba2f2938f5618"
+
+SRCREV_FORMAT = "armnn"

S = "${WORKDIR}/git"

inherit cmake

SRC_URI = " \
- git://github.com/ARM-software/armnn.git;branch=${BRANCH} \
+ git://github.com/ARM-software/armnn.git;name=armnn;branch=${BRANCH} \
file://0001-stdlib-issue-work-around.patch \
file://0002-enable-use-of-boost-shared-library.patch \
+ file://0003-add-more-test-command-line-arguments.patch \
+ file://0004-generate-versioned-library.patch \
+ file://0005-add-armnn-mobilenet-test-example.patch \
+ file://0006-armnn-mobilenet-test-example.patch \
+ file://0007-enable-use-of-arm-compute-shared-library.patch \
+ http://download.tensorflow.org/models/mobilenet_v1_2018_02_22/mobilenet_v1_1.0_224.tgz;name=mobilenet;subdir=${WORKDIR}/tfmodel;destsuffix=tfmodel \
+ git://git.ti.com/tidl/tidl-api.git;name=tidl-api;branch=${BRANCH};subdir=${WORKDIR}/tidl-api;destsuffix=tidl-api \
"

+SRC_URI[mobilenet.md5sum] = "d5f69cef81ad8afb335d9727a17c462a"
+SRC_URI[mobilenet.sha256sum] = "1ccb74dbd9c5f7aea879120614e91617db9534bdfaa53dfea54b7c14162e126b"
+
DEPENDS = " \
boost \
protobuf \
@@ -25,6 +41,7 @@ DEPENDS = " \
arm-compute-library \
armnn-caffe \
armnn-tensorflow \
+ opencv \
"

RDEPENDS_${PN} = " arm-compute-library protobuf boost "
@@ -32,29 +49,35 @@ RDEPENDS_${PN} = " arm-compute-library protobuf boost "
EXTRA_OECMAKE=" \
-DBUILD_SHARED_LIBS=ON -DREGISTER_INSTALL_PREFIX=OFF \
-DARMCOMPUTE_ROOT=${STAGING_DIR_HOST}${datadir}/arm-compute-library \
- -DARMCOMPUTE_BUILD_DIR=${STAGING_DIR_HOST}${datadir}/arm-compute-library/build \
-DCAFFE_GENERATED_SOURCES=${STAGING_DIR_HOST}${datadir}/armnn-caffe \
-DTF_GENERATED_SOURCES=${STAGING_DIR_HOST}${datadir}/armnn-tensorflow \
-DBUILD_CAFFE_PARSER=1 -DBUILD_TF_PARSER=1 \
-DARMCOMPUTENEON=1 \
-DBUILD_TESTS=1 -DPROFILING=1 \
-DTHIRD_PARTY_INCLUDE_DIRS=${STAGING_DIR_HOST}${includedir} \
+ -DBUILD_ARMNN_EXAMPLES=1 \
+ -DGENERIC_LIB_VERSION=${PV} -DGENERIC_LIB_SOVERSION=${PV_MAJOR} \
"

+TESTVECS_INSTALL_DIR = "${datadir}/arm/armnn"
+
do_install_append() {
CP_ARGS="-Prf --preserve=mode,timestamps --no-preserve=ownership"
install -d ${D}${bindir}
find ${WORKDIR}/build/tests -maxdepth 1 -type f -executable -exec cp $CP_ARGS {} ${D}${bindir} \;
cp $CP_ARGS ${WORKDIR}/build/UnitTests ${D}${bindir}
+ install -d ${D}${TESTVECS_INSTALL_DIR}/models
+ cp ${WORKDIR}/tfmodel/mobilenet_v1_1.0_224_frozen.pb ${D}${TESTVECS_INSTALL_DIR}/models
+ cp ${WORKDIR}/git/tests/TfMobileNet-Armnn/labels.txt ${D}${TESTVECS_INSTALL_DIR}/models
+ install -d ${D}${TESTVECS_INSTALL_DIR}/testvecs
+ cp ${WORKDIR}/tidl-api/examples/classification/clips/*.mp4 ${D}${TESTVECS_INSTALL_DIR}/testvecs
+ cp ${WORKDIR}/tidl-api/examples/classification/images/*.jpg ${D}${TESTVECS_INSTALL_DIR}/testvecs
chrpath -d ${D}${bindir}/*
}

CXXFLAGS += "-fopenmp"
LIBS += "-larmpl_lp64_mp"

-SOLIBS = ".so"
-FILES_SOLIBSDEV = ""
-FILES_${PN} += "{bindir}/*"
-FILES_${PN} += "{libdir}/*"
+FILES_${PN} += "${TESTVECS_INSTALL_DIR}"
FILES_${PN}-dev += "{libdir}/cmake/*"
INSANE_SKIP_${PN}-dev = "dev-elf"
--
1.9.1
Denys Dmytriyenko
2018-12-03 21:34:42 UTC
Permalink
Got this QA error:

ERROR: QA Issue: armnn rdepends on arm-compute-library-dev [dev-deps]
Post by Qin Su
Updated to the latest version of 18.08.
Updated to generate versioned library.
Created a demo example with mobileNet.
Added LIC_FILES_CHKSUM for mobilenet network topology.
Enabled the use of arm-compute-library shared libraries.
---
Removed Unicode "FEFF" from 0006-armnn-mobilenet-test-example.patch
1. Fixed patch files permission issue
2. Fixed LIC_FILES_CHKSUM issue of using Apache-2.0 LICENSE for mobilenet
1. Added Upstream-Status in patch files
2. Added "name=armnn" and "name=mobilenet"
3. Removed "dev-so" from INSANE_SKIP_${PN}
---
...0003-add-more-test-command-line-arguments.patch | 75 ++
.../armnn/0004-generate-versioned-library.patch | 61 ++
.../0005-add-armnn-mobilenet-test-example.patch | 67 ++
.../armnn/0006-armnn-mobilenet-test-example.patch | 886 +++++++++++++++++++++
...-enable-use-of-arm-compute-shared-library.patch | 31 +
.../recipes-support/armnn/armnn_git.bb | 43 +-
6 files changed, 1153 insertions(+), 10 deletions(-)
create mode 100644 meta-arago-extras/recipes-support/armnn/armnn/0003-add-more-test-command-line-arguments.patch
create mode 100644 meta-arago-extras/recipes-support/armnn/armnn/0004-generate-versioned-library.patch
create mode 100644 meta-arago-extras/recipes-support/armnn/armnn/0005-add-armnn-mobilenet-test-example.patch
create mode 100644 meta-arago-extras/recipes-support/armnn/armnn/0006-armnn-mobilenet-test-example.patch
create mode 100644 meta-arago-extras/recipes-support/armnn/armnn/0007-enable-use-of-arm-compute-shared-library.patch
diff --git a/meta-arago-extras/recipes-support/armnn/armnn/0003-add-more-test-command-line-arguments.patch b/meta-arago-extras/recipes-support/armnn/armnn/0003-add-more-test-command-line-arguments.patch
new file mode 100644
index 0000000..bcc4a65
--- /dev/null
+++ b/meta-arago-extras/recipes-support/armnn/armnn/0003-add-more-test-command-line-arguments.patch
@@ -0,0 +1,75 @@
+From ba19ce3c785fb92077f6309db1125f1ef32cb08a Mon Sep 17 00:00:00 2001
+Date: Wed, 21 Nov 2018 15:14:24 -0500
+Subject: [PATCH] add more test command line arguments
+
+Upstream-Status: Inappropriate [TI only test code]
+---
+ tests/InferenceTest.inl | 50 +++++++++++++++++++++++++++++++++++++++++++++++++
+ 1 file changed, 50 insertions(+)
+
+diff --git a/tests/InferenceTest.inl b/tests/InferenceTest.inl
+index 16df7ba..5808edb 100644
+--- a/tests/InferenceTest.inl
++++ b/tests/InferenceTest.inl
+ TConstructDatabaseCallable constructDatabase,
+ const armnn::TensorShape* inputTensorShape)
+ {
++ int count;
++ const char *p_input;
++ char inmodelname[500];
++ char outtensorname[500];
++
++ /* parse command line */
++ for (count = 1; count < argc; count++)
++ {
++ if (*(argv[count]) == '+')
++ {
++ p_input = argv[count] + 1;
++ switch (*(p_input))
++ {
++ strcpy(inmodelname, p_input + 2);
++ modelFilename = &inmodelname[0];
++ std::cout << "Input model = " << modelFilename << std::endl;
++ break;
++ strcpy(outtensorname, p_input + 2);
++ outputBindingName = &outtensorname[0];
++ std::cout << "out tensor name = " << outputBindingName << std::endl;
++ break;
++ break;
++ }
++ }
++ else if (*(argv[count]) == '-')
++ {
++ p_input = argv[count] + 1;
++ switch (*(p_input))
++ {
++ p_input = argv[count] + 2;
++ std::cout <<"\nAdditional Options: " << std::endl;
++ std::cout <<" +i Set user specified inference model name." << std::endl;
++ std::cout <<" If not set, default name is used." << std::endl;
++ std::cout <<" +o Set user specified output tensor name." << std::endl;
++ std::cout <<" If not set, default name is used.\n" << std::endl;
++ break;
++ break;
++ }
++ }
++ }
++
+ return InferenceTestMain(argc, argv, defaultTestCaseIds,
+ [=]
+ ()
+--
+1.9.1
+
diff --git a/meta-arago-extras/recipes-support/armnn/armnn/0004-generate-versioned-library.patch b/meta-arago-extras/recipes-support/armnn/armnn/0004-generate-versioned-library.patch
new file mode 100644
index 0000000..57e8a4b
--- /dev/null
+++ b/meta-arago-extras/recipes-support/armnn/armnn/0004-generate-versioned-library.patch
@@ -0,0 +1,61 @@
+From 548f8e308b15e396241149e1c89ce7ffecf6242c Mon Sep 17 00:00:00 2001
+Date: Tue, 27 Nov 2018 18:08:06 -0500
+Subject: [PATCH] generate versioned library
+
+Upstream-Status: Inappropriate [TI only test code]
+
+---
+ CMakeLists.txt | 7 ++++++-
+ 1 file changed, 6 insertions(+), 1 deletion(-)
+
+diff --git a/CMakeLists.txt b/CMakeLists.txt
+index c06a869..cc11476 100644
+--- a/CMakeLists.txt
++++ b/CMakeLists.txt
+
+ target_link_libraries(armnnCaffeParser armnn)
+ target_link_libraries(armnnCaffeParser ${PROTOBUF_LIBRARIES})
+-
++ set_target_properties( armnnCaffeParser PROPERTIES VERSION ${GENERIC_LIB_VERSION} SOVERSION ${GENERIC_LIB_SOVERSION} )
+ endif()
+
+ if(BUILD_ONNX_PARSER)
+
+ # Protobuf
+ target_link_libraries(armnnOnnxParser ${PROTOBUF_LIBRARIES})
++ set_target_properties( armnnOnnxParser PROPERTIES VERSION ${GENERIC_LIB_VERSION} SOVERSION ${GENERIC_LIB_SOVERSION} )
+ endif()
+
+ if(BUILD_TF_PARSER)
+
+ # Protobuf (use the specific version tensorflow wants)
+ target_link_libraries(armnnTfParser ${PROTOBUF_LIBRARIES})
++ set_target_properties( armnnTfParser PROPERTIES VERSION ${GENERIC_LIB_VERSION} SOVERSION ${GENERIC_LIB_SOVERSION} )
+ endif()
+
+ if(BUILD_TF_LITE_PARSER)
+
+ target_link_libraries(armnnTfLiteParser ${Boost_FILESYSTEM_LIBRARY} ${Boost_THREAD_LIBRARY})
+ target_link_libraries(armnnTfLiteParser armnn ${FLATBUFFERS_LIBRARY})
++ set_target_properties( armnnTfLiteParser PROPERTIES VERSION ${GENERIC_LIB_VERSION} SOVERSION ${GENERIC_LIB_SOVERSION} )
+ endif()
+
+ # ArmNN source files required for all build options
+ target_link_libraries(armnn pthread)
+ endif()
+
++set_target_properties( armnn PROPERTIES VERSION ${GENERIC_LIB_VERSION} SOVERSION ${GENERIC_LIB_SOVERSION} )
++
+ if(BUILD_UNIT_TESTS)
+ set(unittest_sources)
+ list(APPEND unittest_sources
+--
+1.9.1
+
diff --git a/meta-arago-extras/recipes-support/armnn/armnn/0005-add-armnn-mobilenet-test-example.patch b/meta-arago-extras/recipes-support/armnn/armnn/0005-add-armnn-mobilenet-test-example.patch
new file mode 100644
index 0000000..c996ede
--- /dev/null
+++ b/meta-arago-extras/recipes-support/armnn/armnn/0005-add-armnn-mobilenet-test-example.patch
@@ -0,0 +1,67 @@
+From 47ce3bcdb8e43ef517dcd8db7477a0514212ba4f Mon Sep 17 00:00:00 2001
+Date: Tue, 27 Nov 2018 18:11:46 -0500
+Subject: [PATCH] add armnn mobilenet test example
+
+Upstream-Status: Inappropriate [TI only test code]
+
+---
+ tests/CMakeLists.txt | 40 ++++++++++++++++++++++++++++++++++++++++
+ 1 file changed, 40 insertions(+)
+
+diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt
+index 0979d55..7c0cf2a 100644
+--- a/tests/CMakeLists.txt
++++ b/tests/CMakeLists.txt
++find_package( OpenCV REQUIRED )
++include_directories( ${OpenCV_INCLUDE_DIRS} )
++
+ # UnitTests
+ include(CheckIncludeFiles)
+
+ ${Boost_PROGRAM_OPTIONS_LIBRARY})
+ addDllCopyCommands(ExecuteNetwork)
+ endif()
++
++if (BUILD_ARMNN_EXAMPLES)
++ set(ArmnnExamples_sources
++ ArmnnExamples/ArmnnExamples.cpp)
++
++ add_executable_ex(ArmnnExamples ${ArmnnExamples_sources})
++
++ target_include_directories(ArmnnExamples PRIVATE ../src/armnnUtils)
++ target_include_directories(ArmnnExamples PRIVATE ../src/armnn)
++
++ if (BUILD_CAFFE_PARSER)
++ target_link_libraries(ArmnnExamples armnnCaffeParser)
++ endif()
++ if (BUILD_TF_PARSER)
++ target_link_libraries(ArmnnExamples armnnTfParser)
++ endif()
++
++ if (BUILD_TF_LITE_PARSER)
++ target_link_libraries(ArmnnExamples armnnTfLiteParser)
++ endif()
++ if (BUILD_ONNX_PARSER)
++ target_link_libraries(ArmnnExamples armnnOnnxParser)
++ endif()
++
++ target_link_libraries(ArmnnExamples armnn)
++ target_link_libraries(ArmnnExamples ${CMAKE_THREAD_LIBS_INIT})
++ if(OPENCL_LIBRARIES)
++ target_link_libraries(ArmnnExamples ${OPENCL_LIBRARIES})
++ endif()
++
++ target_link_libraries(ArmnnExamples
++ ${Boost_SYSTEM_LIBRARY}
++ ${Boost_FILESYSTEM_LIBRARY}
++ ${Boost_PROGRAM_OPTIONS_LIBRARY}
++ ${OpenCV_LIBS})
++ addDllCopyCommands(ArmnnExamples)
++endif()
+--
+1.9.1
+
diff --git a/meta-arago-extras/recipes-support/armnn/armnn/0006-armnn-mobilenet-test-example.patch b/meta-arago-extras/recipes-support/armnn/armnn/0006-armnn-mobilenet-test-example.patch
new file mode 100644
index 0000000..e744587
--- /dev/null
+++ b/meta-arago-extras/recipes-support/armnn/armnn/0006-armnn-mobilenet-test-example.patch
@@ -0,0 +1,886 @@
+From 5da1d1c637a782926f99403d092f01edc028d99d Mon Sep 17 00:00:00 2001
+Date: Wed, 21 Nov 2018 16:18:01 -0500
+Subject: [PATCH] armnn mobilenet test example
+
+Upstream-Status: Inappropriate [TI only test code]
+---
+ tests/ArmnnExamples/ArmnnExamples.cpp | 865 ++++++++++++++++++++++++++++++++++
+ 1 file changed, 865 insertions(+)
+ create mode 100644 tests/ArmnnExamples/ArmnnExamples.cpp
+
+diff --git a/tests/ArmnnExamples/ArmnnExamples.cpp b/tests/ArmnnExamples/ArmnnExamples.cpp
+new file mode 100644
+index 0000000..89faf4f
+--- /dev/null
++++ b/tests/ArmnnExamples/ArmnnExamples.cpp
++/******************************************************************************
++ * Copyright (c) 2018, Texas Instruments Incorporated - http://www.ti.com/
++ * All rights reserved.
++ *
++ * Redistribution and use in source and binary forms, with or without
++ * * Redistributions of source code must retain the above copyright
++ * notice, this list of conditions and the following disclaimer.
++ * * Redistributions in binary form must reproduce the above copyright
++ * notice, this list of conditions and the following disclaimer in the
++ * documentation and/or other materials provided with the distribution.
++ * * Neither the name of Texas Instruments Incorporated nor the
++ * names of its contributors may be used to endorse or promote products
++ * derived from this software without specific prior written permission.
++ *
++ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
++ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
++ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
++ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
++ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
++ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
++ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
++ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
++ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
++ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
++ * THE POSSIBILITY OF SUCH DAMAGE.
++ *****************************************************************************///
++// Copyright © 2017 Arm Ltd. All rights reserved.
++// See LICENSE file in the project root for full license information.
++//
++#include "armnn/ArmNN.hpp"
++
++#include <utility>
++#include <armnn/TypesUtils.hpp>
++
++#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 <iostream>
++#include <fstream>
++#include <functional>
++#include <future>
++#include <algorithm>
++#include <iterator>
++#include<vector>
++
++#include <signal.h>
++#include "opencv2/core.hpp"
++#include "opencv2/imgproc.hpp"
++#include "opencv2/highgui.hpp"
++#include "opencv2/videoio.hpp"
++#include <time.h>
++
++using namespace cv;
++
++#define INPUT_IMAGE 0
++#define INPUT_VIDEO 1
++#define INPUT_CAMERA 2
++
++Mat test_image;
++Rect rectCrop;
++
++time_point<high_resolution_clock> predictStart;
++time_point<high_resolution_clock> predictEnd;
++
++void imagenetCallBackFunc(int event, int x, int y, int flags, void* userdata)
++{
++ if ( event == EVENT_RBUTTONDOWN )
++ {
++ std::cout << "Right button of the mouse is clicked - position (" << x << ", " << y << ")" << " ... prepare to exit!" << std::endl;
++ exit(0);
++ }
++}
++
++inline float Lerpfloat(float a, float b, float w)
++{
++ return w * b + (1.f - w) * a;
++}
++
++// Load a single image
++struct ImageData
++{
++ unsigned int m_width;
++ unsigned int m_height;
++ unsigned int m_chnum;
++ unsigned int m_size;
++ std::vector<uint8_t> m_image;
++};
++
++std::unique_ptr<ImageData> loadImageData(std::string image_path, VideoCapture &cap, cv::Mat img, int input_type)
++{
++ //cv::Mat img;
++ if (input_type == INPUT_IMAGE)
++ {
++ /* use OpenCV to get the image */
++ img = cv::imread(image_path, CV_LOAD_IMAGE_COLOR);
++ }
++ cv::cvtColor(img, img, CV_BGR2RGB); //convert image format from BGR(openCV format) to RGB (armnn required format).
++
++ // store image and label in Image
++ std::unique_ptr<ImageData> ret(new ImageData);
++ ret->m_width = static_cast<unsigned int>(img.cols);
++ ret->m_height = static_cast<unsigned int>(img.rows);
++ ret->m_chnum = static_cast<unsigned int>(img.channels());
++ ret->m_size = static_cast<unsigned int>(img.cols*img.rows*img.channels());
++ ret->m_image.resize(ret->m_size);
++
++ for (unsigned int i = 0; i < ret->m_size; i++)
++ {
++ ret->m_image[i] = static_cast<uint8_t>(img.data[i]);
++ }
++ return ret;
++}
++
++std::vector<float> ResizeBilinear(std::vector<uint8_t> input,
++ const unsigned int inWidth,
++ const unsigned int inHeight,
++ const unsigned int inChnum,
++ const unsigned int outputWidth,
++ const unsigned int outputHeight)
++{
++ std::vector<float> out;
++ out.resize(outputWidth * outputHeight * 3);
++
++ // We follow the definition of TensorFlow and AndroidNN: the top-left corner of a texel in the output
++ // image is projected into the input image to figure out the interpolants and weights. Note that this
++ // will yield different results than if projecting the centre of output texels.
++
++ const unsigned int inputWidth = inWidth;
++ const unsigned int inputHeight = inHeight;
++
++ // How much to scale pixel coordinates in the output image to get the corresponding pixel coordinates
++ // in the input image.
++ const float scaleY = boost::numeric_cast<float>(inputHeight) / boost::numeric_cast<float>(outputHeight);
++ const float scaleX = boost::numeric_cast<float>(inputWidth) / boost::numeric_cast<float>(outputWidth);
++
++ uint8_t rgb_x0y0[3];
++ uint8_t rgb_x1y0[3];
++ uint8_t rgb_x0y1[3];
++ uint8_t rgb_x1y1[3];
++ unsigned int pixelOffset00, pixelOffset10, pixelOffset01, pixelOffset11;
++ for (unsigned int y = 0; y < outputHeight; ++y)
++ {
++ // Corresponding real-valued height coordinate in input image.
++ const float iy = boost::numeric_cast<float>(y) * scaleY;
++ // Discrete height coordinate of top-left texel (in the 2x2 texel area used for interpolation).
++ const float fiy = floorf(iy);
++ const unsigned int y0 = boost::numeric_cast<unsigned int>(fiy);
++
++ // Interpolation weight (range [0,1])
++ const float yw = iy - fiy;
++
++ for (unsigned int x = 0; x < outputWidth; ++x)
++ {
++ // Real-valued and discrete width coordinates in input image.
++ const float ix = boost::numeric_cast<float>(x) * scaleX;
++ const float fix = floorf(ix);
++ const unsigned int x0 = boost::numeric_cast<unsigned int>(fix);
++
++ // Interpolation weight (range [0,1]).
++ const float xw = ix - fix;
++
++ // Discrete width/height coordinates of texels below and to the right of (x0, y0).
++ const unsigned int x1 = std::min(x0 + 1, inputWidth - 1u);
++ const unsigned int y1 = std::min(y0 + 1, inputHeight - 1u);
++
++ pixelOffset00 = x0 * inChnum + y0 * inputWidth * inChnum;
++ pixelOffset10 = x1 * inChnum + y0 * inputWidth * inChnum;
++ pixelOffset01 = x0 * inChnum + y1 * inputWidth * inChnum;
++ pixelOffset11 = x1 * inChnum + y1 * inputWidth * inChnum;
++ for (unsigned int c = 0; c < 3; ++c)
++ {
++ rgb_x0y0[c] = input[pixelOffset00+c];
++ rgb_x1y0[c] = input[pixelOffset10+c];
++ rgb_x0y1[c] = input[pixelOffset01+c];
++ rgb_x1y1[c] = input[pixelOffset11+c];
++ }
++
++ for (unsigned c=0; c<3; ++c)
++ {
++ const float ly0 = Lerpfloat(float(rgb_x0y0[c]), float(rgb_x1y0[c]), xw);
++ const float ly1 = Lerpfloat(float(rgb_x0y1[c]), float(rgb_x1y1[c]), xw);
++ const float l = Lerpfloat(ly0, ly1, yw);
++ out[(3*((y*outputWidth)+x)) + c] = static_cast<float>(l)/255.0f;
++ }
++ }
++ }
++ return out;
++}
++
++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)
++{
++ 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("\t ,;:"), 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<typename T>
++std::vector<T> ParseArray(std::istream& stream);
++
++template<>
++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)); });
++}
++
++void RemoveDuplicateDevices(std::vector<armnn::Compute>& 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());
++}
++
++bool CheckDevicesAreValid(const std::vector<armnn::Compute>& computeDevices)
++{
++ return (!computeDevices.empty()
++ && std::none_of(computeDevices.begin(), computeDevices.end(),
++ [](armnn::Compute c){ return c == armnn::Compute::Undefined; }));
++}
++
++} // namespace
++
++template<typename TParser, typename TDataType>
++int MainImpl(const char* modelPath,
++ bool isModelBinary,
++ const std::vector<armnn::Compute>& computeDevice,
++ const char* inputName,
++ const armnn::TensorShape* inputTensorShape,
++ const char* inputTensorDataFilePath,
++ const char* outputName,
++ bool enableProfiling,
++ const size_t number_frame,
++ const std::shared_ptr<armnn::IRuntime>& runtime = nullptr)
++{
++ // Loads input tensor.
++ std::vector<uint8_t> input;
++ std::vector<float> input_resized;
++
++ try
++ {
++ // Creates an InferenceModel, which will parse the model and load it into an IRuntime.
++ typename InferenceModel<TParser, TDataType>::Params params;
++ //const armnn::TensorShape inputTensorShape({ 1, 224, 224 3});
++ params.m_ModelPath = modelPath;
++ params.m_IsModelBinary = isModelBinary;
++ params.m_ComputeDevice = computeDevice;
++ params.m_InputBinding = inputName;
++ params.m_InputTensorShape = inputTensorShape;
++ params.m_OutputBinding = outputName;
++ params.m_EnableProfiling = enableProfiling;
++ params.m_SubgraphId = 0;
++ InferenceModel<TParser, TDataType> model(params, runtime);
++
++ VideoCapture cap;
++ int input_type = INPUT_IMAGE;
++ std::string filename = inputTensorDataFilePath;
++
++ size_t i = filename.rfind("camera_live_input", filename.length());
++ if (i != string::npos)
++ {
++ cap = VideoCapture(1);
++ namedWindow("ARMNN MobileNet Example", WINDOW_AUTOSIZE | CV_GUI_NORMAL);
++ input_type = INPUT_CAMERA; //camera input
++ }
++ else if((filename.substr(filename.find_last_of(".") + 1) == "mp4") ||
++ (filename.substr(filename.find_last_of(".") + 1) == "mov") ||
++ (filename.substr(filename.find_last_of(".") + 1) == "avi") )
++ {
++ cap = VideoCapture(inputTensorDataFilePath);
++ if (! cap.isOpened())
++ {
++ std::cout << "Cannot open video input: " << inputTensorDataFilePath << std::endl;
++ return (-1);
++ }
++
++ namedWindow("ARMNN MobileNet Example", WINDOW_AUTOSIZE | CV_GUI_NORMAL);
++ input_type = INPUT_VIDEO; //video clip input
++ }
++ if (input_type != INPUT_IMAGE)
++ {
++ //set the callback function for any mouse event. Used for right click mouse to exit the program.
++ setMouseCallback("ARMNN MobileNet Example", imagenetCallBackFunc, NULL);
++ }
++
++ for (unsigned int i=0; i < number_frame; i++)
++ {
++ if (input_type != INPUT_IMAGE)
++ {
++ cap.grab();
++ cap.retrieve(test_image);
++ }
++ std::unique_ptr<ImageData> inputData = loadImageData(inputTensorDataFilePath, cap, test_image, input_type);
++ input.resize(inputData->m_size);
++
++ input = std::move(inputData->m_image);
++ input_resized = ResizeBilinear(input, inputData->m_width, inputData->m_height, inputData->m_chnum, 224, 224);
++ // Executes the model.
++ std::vector<TDataType> output(model.GetOutputSize());
++
++ predictStart = high_resolution_clock::now();
++
++ model.Run(input_resized, output);
++
++ predictEnd = high_resolution_clock::now();
++
++ // duration<double> will convert the time difference into seconds as a double by default.
++ double timeTakenS = duration<double>(predictEnd - predictStart).count();
++ double preformance_ret = static_cast<double>(1.0/timeTakenS);
++
++ // Convert 1-hot output to an integer label and print
++ int label = static_cast<int>(std::distance(output.begin(), std::max_element(output.begin(), output.end())));
++ std::fstream file("/usr/share/arm/armnn/models/labels.txt");
++ std::string predict_target_name;
++ for (int i=0; i <= label; i++)
++ {
++ std::getline(file, predict_target_name);
++ }
++ std::cout << "Predicted: " << predict_target_name << std::endl;
++ std::cout << "Performance (FPS): " << preformance_ret << std::endl;
++
++ if (input_type != INPUT_IMAGE)
++ {
++ //convert image format back to BGR for OpenCV imshow from RGB format required by armnn.
++ cv::cvtColor(test_image, test_image, CV_RGB2BGR);
++ // output identified object name on top of input image
++ cv::putText(test_image, predict_target_name,
++ cv::Point(rectCrop.x + 5,rectCrop.y + 20), // Coordinates
++ cv::FONT_HERSHEY_COMPLEX_SMALL, // Font
++ 1.0, // Scale. 2.0 = 2x bigger
++ cv::Scalar(0,0,255), // Color
++ 1, // Thickness
++ 8); // Line type
++
++ // output preformance in FPS on top of input image
++ std::string preformance_ret_string = "Performance (FPS): " + boost::lexical_cast<std::string>(preformance_ret);
++ cv::putText(test_image, preformance_ret_string,
++ cv::Point(rectCrop.x + 5,rectCrop.y + 40), // Coordinates
++ cv::FONT_HERSHEY_COMPLEX_SMALL, // Font
++ 1.0, // Scale. 2.0 = 2x bigger
++ cv::Scalar(0,0,255), // Color
++ 1, // Thickness
++ 8); // Line type
++
++ cv::imshow("ARMNN MobileNet Example", test_image);
++ waitKey(2);
++ }
++ }
++ }
++ 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& modelFormat,
++ const std::string& inputTensorShapeStr,
++ const vector<armnn::Compute>& computeDevice,
++ const std::string& modelPath,
++ const std::string& inputName,
++ const std::string& inputTensorDataFilePath,
++ const std::string& outputName,
++ bool enableProfiling,
++ const size_t subgraphId,
++ const std::shared_ptr<armnn::IRuntime>& runtime = nullptr)
++{
++ // 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;
++ }
++
++ // Parse input tensor shape from the string we got from the command-line.
++ std::unique_ptr<armnn::TensorShape> inputTensorShape;
++ if (!inputTensorShapeStr.empty())
++ {
++ std::stringstream ss(inputTensorShapeStr);
++ std::vector<unsigned int> dims = ParseArray<unsigned int>(ss);
++
++ try
++ {
++ // Coverity fix: An exception of type armnn::InvalidArgumentException is thrown and never caught.
++ inputTensorShape = 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;
++ }
++ }
++
++ // Forward to implementation based on the parser type
++ if (modelFormat.find("caffe") != std::string::npos)
++ {
++#if defined(ARMNN_CAFFE_PARSER)
++ return MainImpl<armnnCaffeParser::ICaffeParser, float>(modelPath.c_str(), isModelBinary, computeDevice,
++ inputName.c_str(), inputTensorShape.get(),
++ inputTensorDataFilePath.c_str(), outputName.c_str(),
++ enableProfiling, 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,
++ inputName.c_str(), inputTensorShape.get(),
++ inputTensorDataFilePath.c_str(), outputName.c_str(),
++ enableProfiling, 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,
++ inputName.c_str(), inputTensorShape.get(),
++ inputTensorDataFilePath.c_str(), outputName.c_str(),
++ enableProfiling, 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,
++ inputName.c_str(), inputTensorShape.get(),
++ inputTensorDataFilePath.c_str(), outputName.c_str(),
++ enableProfiling, 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)
++{
++ std::string modelFormat;
++ std::string modelPath;
++ std::string inputName;
++ std::string inputTensorShapeStr;
++ std::string inputTensorDataFilePath;
++ std::string outputName;
++
++ size_t subgraphId = 0;
++
++ po::options_description desc("Options");
++ try
++ {
++ desc.add_options()
++ ("model-format,f", po::value(&modelFormat),
++ "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. .caffemodel, .prototxt, .tflite,"
++ " .onnx")
++ ("compute,c", po::value<std::vector<armnn::Compute>>()->multitoken(),
++ "The preferred order of devices to run layers on by default. Possible choices: CpuAcc, CpuRef, GpuAcc")
++ ("input-name,i", po::value(&inputName), "Identifier of the input tensor in the network.")
++ ("input-tensor-shape,s", po::value(&inputTensorShapeStr),
++ "The shape of the input tensor in the network as a flat array of integers separated by whitespace. "
++ "This parameter is optional, depending on the network.")
++ ("input-tensor-data,d", po::value(&inputTensorDataFilePath),
++ "Input test file name. It can be image/video clip file name or use 'camera_live_input' to select camera input.")
++ ("output-name,o", po::value(&outputName), "Identifier of the output tensor in the network.")
++ ("event-based-profiling,e", po::bool_switch()->default_value(false),
++ "Enables built in profiler. If unset, defaults to off.")
++ ("number-frame", po::value<size_t>(&subgraphId)->default_value(1), "Number of frames to process " );
++ }
++ 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;
++ }
++
++ // Remove leading and trailing whitespaces from the parsed arguments.
++ boost::trim(modelFormat);
++ boost::trim(modelPath);
++ boost::trim(inputName);
++ boost::trim(inputTensorShapeStr);
++ boost::trim(inputTensorDataFilePath);
++ boost::trim(outputName);
++
++ // Get the value of the switch arguments.
++ bool enableProfiling = vm["event-based-profiling"].as<bool>();
++
++ // Get the preferred order of compute devices.
++ std::vector<armnn::Compute> computeDevices = vm["compute"].as<std::vector<armnn::Compute>>();
++
++ // Remove duplicates from the list of compute devices.
++ RemoveDuplicateDevices(computeDevices);
++
++ // Check that the specified compute devices are valid.
++ if (!CheckDevicesAreValid(computeDevices))
++ {
++ BOOST_LOG_TRIVIAL(fatal) << "The list of preferred devices contains an invalid compute";
++ return EXIT_FAILURE;
++ }
++
++ return RunTest(modelFormat, inputTensorShapeStr, computeDevices,
++ modelPath, inputName, inputTensorDataFilePath, outputName, enableProfiling, subgraphId, runtime);
++}
++
++int main(int argc, const char* argv[])
++{
++ // Configures logging for both the ARMNN library and this test program.
++#ifdef NDEBUG
++ armnn::LogSeverity level = armnn::LogSeverity::Info;
++#else
++ armnn::LogSeverity level = armnn::LogSeverity::Debug;
++#endif
++ armnn::ConfigureLogging(true, true, level);
++ armnnUtils::ConfigureLogging(boost::log::core::get().get(), true, true, level);
++
++ std::string testCasesFile;
++
++ std::string modelFormat = "tensorflow-binary";
++ std::string modelPath = "/usr/share/arm/armnn/models/mobilenet_v1_1.0_224_frozen.pb";
++ std::string inputName = "input";
++ std::string inputTensorShapeStr = "1 224 224 3";
++ std::string inputTensorDataFilePath = "/usr/share/arm/armnn/testvecs/test2.mp4";
++ std::string outputName = "MobilenetV1/Predictions/Reshape_1";
++ std::vector<armnn::Compute> computeDevices = {armnn::Compute::CpuAcc};
++
++ // Catch ctrl-c to ensure a clean exit
++ signal(SIGABRT, exit);
++ signal(SIGTERM, exit);
++
++ if (argc == 1)
++ {
++ return RunTest(modelFormat, inputTensorShapeStr, computeDevices,
++ modelPath, inputName, inputTensorDataFilePath, outputName, false, 1000);
++ }
++ else
++ {
++ size_t subgraphId = 0;
++ po::options_description desc("Options");
++ try
++ {
++ desc.add_options()
++ ("help", "Display usage information")
++ ("test-cases,t", po::value(&testCasesFile), "Path to a CSV file containing test cases to run. "
++ "If set, further parameters -- with the exception of compute device and concurrency -- will be ignored, "
++ "as they are expected to be defined in the file for each test in particular.")
++ ("concurrent,n", po::bool_switch()->default_value(false),
++ "Whether or not the test cases should be executed in parallel")
++ ("model-format,f", po::value(&modelFormat),
++ "caffe-binary, caffe-text, onnx-binary, onnx-text, tflite-binary, tensorflow-binary or tensorflow-text.")
++ ("model-path,m", po::value(&modelPath), "Path to model file, e.g. .caffemodel, .prototxt,"
++ " .tflite, .onnx")
++ ("compute,c", po::value<std::vector<armnn::Compute>>()->multitoken(),
++ "The preferred order of devices to run layers on by default. Possible choices: CpuAcc, CpuRef, GpuAcc")
++ ("input-name,i", po::value(&inputName), "Identifier of the input tensor in the network.")
++ ("input-tensor-shape,s", po::value(&inputTensorShapeStr),
++ "The shape of the input tensor in the network as a flat array of integers separated by whitespace. "
++ "This parameter is optional, depending on the network.")
++ ("input-tensor-data,d", po::value(&inputTensorDataFilePath),
++ "Input test file name. It can be image/video clip file name or use 'camera_live_input' to select camera input.")
++ ("output-name,o", po::value(&outputName), "Identifier of the output tensor in the network.")
++ ("event-based-profiling,e", po::bool_switch()->default_value(false),
++ "Enables built in profiler. If unset, defaults to off.")
++ ("number_frame", po::value<size_t>(&subgraphId)->default_value(1), "Number of frames to process.");
++ }
++ 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;
++ }
++
++ // Parses the command-line.
++ po::variables_map vm;
++ try
++ {
++ po::store(po::parse_command_line(argc, argv, desc), vm);
++
++ if (CheckOption(vm, "help") || argc <= 1)
++ {
++ std::cout << "Executes a neural network model using the provided input tensor. " << std::endl;
++ std::cout << "Prints the resulting output tensor." << std::endl;
++ std::cout << std::endl;
++ std::cout << desc << std::endl;
++ return EXIT_SUCCESS;
++ }
++
++ po::notify(vm);
++ }
++ catch (const po::error& e)
++ {
++ std::cerr << e.what() << std::endl << std::endl;
++ std::cerr << desc << std::endl;
++ return EXIT_FAILURE;
++ }
++
++ // Get the value of the switch arguments.
++ bool concurrent = vm["concurrent"].as<bool>();
++ bool enableProfiling = vm["event-based-profiling"].as<bool>();
++
++ // Check whether we have to load test cases from a file.
++ if (CheckOption(vm, "test-cases"))
++ {
++ // Check that the file exists.
++ if (!boost::filesystem::exists(testCasesFile))
++ {
++ BOOST_LOG_TRIVIAL(fatal) << "Given file \"" << testCasesFile << "\" does not exist";
++ return EXIT_FAILURE;
++ }
++
++ // Parse CSV file and extract test cases
++ armnnUtils::CsvReader reader;
++ std::vector<armnnUtils::CsvRow> testCases = reader.ParseFile(testCasesFile);
++
++ // Check that there is at least one test case to run
++ if (testCases.empty())
++ {
++ BOOST_LOG_TRIVIAL(fatal) << "Given file \"" << testCasesFile << "\" has no test cases";
++ return EXIT_FAILURE;
++ }
++
++ // Create runtime
++ armnn::IRuntime::CreationOptions options;
++ std::shared_ptr<armnn::IRuntime> runtime(armnn::IRuntime::Create(options));
++
++ const std::string executableName("ExecuteNetwork");
++
++ // Check whether we need to run the test cases concurrently
++ if (concurrent)
++ {
++ std::vector<std::future<int>> results;
++ results.reserve(testCases.size());
++
++ // Run each test case in its own thread
++ for (auto& testCase : testCases)
++ {
++ testCase.values.insert(testCase.values.begin(), executableName);
++ results.push_back(std::async(std::launch::async, RunCsvTest, std::cref(testCase), std::cref(runtime)));
++ }
++
++ // Check results
++ for (auto& result : results)
++ {
++ if (result.get() != EXIT_SUCCESS)
++ {
++ return EXIT_FAILURE;
++ }
++ }
++ }
++ else
++ {
++ // Run tests sequentially
++ for (auto& testCase : testCases)
++ {
++ testCase.values.insert(testCase.values.begin(), executableName);
++ if (RunCsvTest(testCase, runtime) != EXIT_SUCCESS)
++ {
++ return EXIT_FAILURE;
++ }
++ }
++ }
++
++ return EXIT_SUCCESS;
++ }
++ else // Run single test
++ {
++ // Get the preferred order of compute devices.
++ std::vector<armnn::Compute> computeDevices = vm["compute"].as<std::vector<armnn::Compute>>();
++
++ // Remove duplicates from the list of compute devices.
++ RemoveDuplicateDevices(computeDevices);
++ // Check that the specified compute devices are valid.
++ if (!CheckDevicesAreValid(computeDevices))
++ {
++ BOOST_LOG_TRIVIAL(fatal) << "The list of preferred devices contains an invalid compute";
++ return EXIT_FAILURE;
++ }
++
++ try
++ {
++ CheckOptionDependencies(vm);
++ }
++ catch (const po::error& e)
++ {
++ std::cerr << e.what() << std::endl << std::endl;
++ std::cerr << desc << std::endl;
++ return EXIT_FAILURE;
++ }
++ return RunTest(modelFormat, inputTensorShapeStr, computeDevices,
++ modelPath, inputName, inputTensorDataFilePath, outputName, enableProfiling, subgraphId);
++ }
++ }
++}
++
+--
+1.9.1
+
diff --git a/meta-arago-extras/recipes-support/armnn/armnn/0007-enable-use-of-arm-compute-shared-library.patch b/meta-arago-extras/recipes-support/armnn/armnn/0007-enable-use-of-arm-compute-shared-library.patch
new file mode 100644
index 0000000..7d0d7e5
--- /dev/null
+++ b/meta-arago-extras/recipes-support/armnn/armnn/0007-enable-use-of-arm-compute-shared-library.patch
@@ -0,0 +1,31 @@
+From 38e8e4bc03a4c1ee801f6af50be94ccd971bf3eb Mon Sep 17 00:00:00 2001
+Date: Tue, 27 Nov 2018 18:15:49 -0500
+Subject: [PATCH] enable use of arm compute shared library
+
+Upstream-Status: Inappropriate [configuration]
+
+---
+ cmake/GlobalConfig.cmake | 5 +++++
+ 1 file changed, 5 insertions(+)
+
+diff --git a/cmake/GlobalConfig.cmake b/cmake/GlobalConfig.cmake
+index 491d87a..4cf40a2 100644
+--- a/cmake/GlobalConfig.cmake
++++ b/cmake/GlobalConfig.cmake
+ find_library(ARMCOMPUTE_CORE_LIBRARY_DEBUG NAMES arm_compute_core-static)
+ find_library(ARMCOMPUTE_CORE_LIBRARY_RELEASE NAMES arm_compute_core-static)
+
++ find_library(ARMCOMPUTE_LIBRARY_DEBUG NAMES arm_compute)
++ find_library(ARMCOMPUTE_LIBRARY_RELEASE NAMES arm_compute)
++ find_library(ARMCOMPUTE_CORE_LIBRARY_DEBUG NAMES arm_compute_core)
++ find_library(ARMCOMPUTE_CORE_LIBRARY_RELEASE NAMES arm_compute_core)
++
+ set(ARMCOMPUTE_LIBRARIES
+ debug ${ARMCOMPUTE_LIBRARY_DEBUG} ${ARMCOMPUTE_CORE_LIBRARY_DEBUG}
+ optimized ${ARMCOMPUTE_LIBRARY_RELEASE} ${ARMCOMPUTE_CORE_LIBRARY_RELEASE} )
+--
+1.9.1
+
diff --git a/meta-arago-extras/recipes-support/armnn/armnn_git.bb b/meta-arago-extras/recipes-support/armnn/armnn_git.bb
index 726a9da..b3ac84d 100644
--- a/meta-arago-extras/recipes-support/armnn/armnn_git.bb
+++ b/meta-arago-extras/recipes-support/armnn/armnn_git.bb
@@ -1,23 +1,39 @@
SUMMARY = "ARM Neural Network SDK"
DESCRIPTION = "Linux software and tools to enable machine learning (Caffe/Tensorflow) workloads on power-efficient devices"
-LICENSE = "MIT"
-LIC_FILES_CHKSUM = "file://LICENSE;md5=3e14a924c16f7d828b8335a59da64074"
+LICENSE = "MIT & Apache-2.0"
+# Apache-2.0 license applies to mobilenet tarball
+LIC_FILES_CHKSUM = "file://LICENSE;md5=3e14a924c16f7d828b8335a59da64074 \
+ file://${COMMON_LICENSE_DIR}/Apache-2.0;md5=89aea4e17d99a7cacdbeed46a0096b10"
-PV = "20180502"
+PV = "18.08"
BRANCH = "master"
-SRCREV = "4c7098bfeab1ffe1cdc77f6c15548d3e73274746"
+SRCREV = "c577f2c6a3b4ddb6ba87a882723c53a248afbeba"
+SRCREV_tidl-api = "7e9a3942ec38efd64d45e34c10cba2f2938f5618"
+
+SRCREV_FORMAT = "armnn"
S = "${WORKDIR}/git"
inherit cmake
SRC_URI = " \
- git://github.com/ARM-software/armnn.git;branch=${BRANCH} \
+ git://github.com/ARM-software/armnn.git;name=armnn;branch=${BRANCH} \
file://0001-stdlib-issue-work-around.patch \
file://0002-enable-use-of-boost-shared-library.patch \
+ file://0003-add-more-test-command-line-arguments.patch \
+ file://0004-generate-versioned-library.patch \
+ file://0005-add-armnn-mobilenet-test-example.patch \
+ file://0006-armnn-mobilenet-test-example.patch \
+ file://0007-enable-use-of-arm-compute-shared-library.patch \
+ http://download.tensorflow.org/models/mobilenet_v1_2018_02_22/mobilenet_v1_1.0_224.tgz;name=mobilenet;subdir=${WORKDIR}/tfmodel;destsuffix=tfmodel \
+ git://git.ti.com/tidl/tidl-api.git;name=tidl-api;branch=${BRANCH};subdir=${WORKDIR}/tidl-api;destsuffix=tidl-api \
"
+SRC_URI[mobilenet.md5sum] = "d5f69cef81ad8afb335d9727a17c462a"
+SRC_URI[mobilenet.sha256sum] = "1ccb74dbd9c5f7aea879120614e91617db9534bdfaa53dfea54b7c14162e126b"
+
DEPENDS = " \
boost \
protobuf \
@@ -25,6 +41,7 @@ DEPENDS = " \
arm-compute-library \
armnn-caffe \
armnn-tensorflow \
+ opencv \
"
RDEPENDS_${PN} = " arm-compute-library protobuf boost "
@@ -32,29 +49,35 @@ RDEPENDS_${PN} = " arm-compute-library protobuf boost "
EXTRA_OECMAKE=" \
-DBUILD_SHARED_LIBS=ON -DREGISTER_INSTALL_PREFIX=OFF \
-DARMCOMPUTE_ROOT=${STAGING_DIR_HOST}${datadir}/arm-compute-library \
- -DARMCOMPUTE_BUILD_DIR=${STAGING_DIR_HOST}${datadir}/arm-compute-library/build \
-DCAFFE_GENERATED_SOURCES=${STAGING_DIR_HOST}${datadir}/armnn-caffe \
-DTF_GENERATED_SOURCES=${STAGING_DIR_HOST}${datadir}/armnn-tensorflow \
-DBUILD_CAFFE_PARSER=1 -DBUILD_TF_PARSER=1 \
-DARMCOMPUTENEON=1 \
-DBUILD_TESTS=1 -DPROFILING=1 \
-DTHIRD_PARTY_INCLUDE_DIRS=${STAGING_DIR_HOST}${includedir} \
+ -DBUILD_ARMNN_EXAMPLES=1 \
+ -DGENERIC_LIB_VERSION=${PV} -DGENERIC_LIB_SOVERSION=${PV_MAJOR} \
"
+TESTVECS_INSTALL_DIR = "${datadir}/arm/armnn"
+
do_install_append() {
CP_ARGS="-Prf --preserve=mode,timestamps --no-preserve=ownership"
install -d ${D}${bindir}
find ${WORKDIR}/build/tests -maxdepth 1 -type f -executable -exec cp $CP_ARGS {} ${D}${bindir} \;
cp $CP_ARGS ${WORKDIR}/build/UnitTests ${D}${bindir}
+ install -d ${D}${TESTVECS_INSTALL_DIR}/models
+ cp ${WORKDIR}/tfmodel/mobilenet_v1_1.0_224_frozen.pb ${D}${TESTVECS_INSTALL_DIR}/models
+ cp ${WORKDIR}/git/tests/TfMobileNet-Armnn/labels.txt ${D}${TESTVECS_INSTALL_DIR}/models
+ install -d ${D}${TESTVECS_INSTALL_DIR}/testvecs
+ cp ${WORKDIR}/tidl-api/examples/classification/clips/*.mp4 ${D}${TESTVECS_INSTALL_DIR}/testvecs
+ cp ${WORKDIR}/tidl-api/examples/classification/images/*.jpg ${D}${TESTVECS_INSTALL_DIR}/testvecs
chrpath -d ${D}${bindir}/*
}
CXXFLAGS += "-fopenmp"
LIBS += "-larmpl_lp64_mp"
-SOLIBS = ".so"
-FILES_SOLIBSDEV = ""
-FILES_${PN} += "{bindir}/*"
-FILES_${PN} += "{libdir}/*"
+FILES_${PN} += "${TESTVECS_INSTALL_DIR}"
FILES_${PN}-dev += "{libdir}/cmake/*"
INSANE_SKIP_${PN}-dev = "dev-elf"
--
1.9.1
_______________________________________________
meta-arago mailing list
http://arago-project.org/cgi-bin/mailman/listinfo/meta-arago
Loading...