* [PATCH v4] armnn: bump SRCREV and add a demo example
@ 2018-11-29 21:47 Qin Su
2018-12-03 21:34 ` Denys Dmytriyenko
0 siblings, 1 reply; 2+ messages in thread
From: Qin Su @ 2018-11-29 21:47 UTC (permalink / raw)
To: meta-arago
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 <qsu@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 <qsu@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 <qsu@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 <qsu@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 <qsu@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 <qsu@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 <qsu@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 <qsu@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 <qsu@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 <qsu@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 <qsu@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
^ permalink raw reply related [flat|nested] 2+ messages in thread* Re: [PATCH v4] armnn: bump SRCREV and add a demo example
2018-11-29 21:47 [PATCH v4] armnn: bump SRCREV and add a demo example Qin Su
@ 2018-12-03 21:34 ` Denys Dmytriyenko
0 siblings, 0 replies; 2+ messages in thread
From: Denys Dmytriyenko @ 2018-12-03 21:34 UTC (permalink / raw)
To: Qin Su; +Cc: meta-arago
Got this QA error:
ERROR: QA Issue: armnn rdepends on arm-compute-library-dev [dev-deps]
On Thu, Nov 29, 2018 at 04:47:15PM -0500, Qin Su wrote:
> 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 <qsu@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 <qsu@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 <qsu@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 <qsu@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 <qsu@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 <qsu@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 <qsu@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 <qsu@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 <qsu@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 <qsu@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 <qsu@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
>
> _______________________________________________
> meta-arago mailing list
> meta-arago@arago-project.org
> http://arago-project.org/cgi-bin/mailman/listinfo/meta-arago
^ permalink raw reply [flat|nested] 2+ messages in thread
end of thread, other threads:[~2018-12-03 21:34 UTC | newest]
Thread overview: 2+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2018-11-29 21:47 [PATCH v4] armnn: bump SRCREV and add a demo example Qin Su
2018-12-03 21:34 ` Denys Dmytriyenko
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.