From 95b930c940c47e2f9a783cf17c87449cab4633c5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89anna=20=C3=93=20Cath=C3=A1in?= Date: Wed, 7 Apr 2021 14:35:25 +0100 Subject: MLECO-1252 ASR sample application using the public ArmNN C++ API. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Change-Id: I98cd505b8772a8c8fa88308121bc94135bb45068 Signed-off-by: Éanna Ó Catháin --- samples/common/cmake/aarch64-toolchain.cmake | 20 ++ .../cmake/arm-linux-gnueabihf-toolchain.cmake | 20 ++ samples/common/cmake/find_armnn.cmake | 35 ++++ samples/common/cmake/find_catch.cmake | 16 ++ samples/common/cmake/find_opencv.cmake | 203 +++++++++++++++++++ .../include/ArmnnUtils/ArmnnNetworkExecutor.hpp | 214 +++++++++++++++++++++ .../common/include/CVUtils/CvVideoFileWriter.hpp | 61 ++++++ .../common/include/CVUtils/CvVideoFrameReader.hpp | 108 +++++++++++ samples/common/include/CVUtils/CvWindowOutput.hpp | 53 +++++ samples/common/include/CVUtils/IFrameOutput.hpp | 48 +++++ samples/common/include/CVUtils/IFrameReader.hpp | 45 +++++ samples/common/include/Utils/CmdArgsParser.hpp | 25 +++ samples/common/include/Utils/Types.hpp | 54 ++++++ samples/common/src/CVUtils/CvVideoFileWriter.cpp | 38 ++++ samples/common/src/CVUtils/CvVideoFrameReader.cpp | 98 ++++++++++ samples/common/src/CVUtils/CvWindowOutput.cpp | 33 ++++ samples/common/src/Utils/CmdArgsParser.cpp | 70 +++++++ 17 files changed, 1141 insertions(+) create mode 100644 samples/common/cmake/aarch64-toolchain.cmake create mode 100644 samples/common/cmake/arm-linux-gnueabihf-toolchain.cmake create mode 100644 samples/common/cmake/find_armnn.cmake create mode 100644 samples/common/cmake/find_catch.cmake create mode 100644 samples/common/cmake/find_opencv.cmake create mode 100644 samples/common/include/ArmnnUtils/ArmnnNetworkExecutor.hpp create mode 100644 samples/common/include/CVUtils/CvVideoFileWriter.hpp create mode 100644 samples/common/include/CVUtils/CvVideoFrameReader.hpp create mode 100644 samples/common/include/CVUtils/CvWindowOutput.hpp create mode 100644 samples/common/include/CVUtils/IFrameOutput.hpp create mode 100644 samples/common/include/CVUtils/IFrameReader.hpp create mode 100644 samples/common/include/Utils/CmdArgsParser.hpp create mode 100644 samples/common/include/Utils/Types.hpp create mode 100644 samples/common/src/CVUtils/CvVideoFileWriter.cpp create mode 100644 samples/common/src/CVUtils/CvVideoFrameReader.cpp create mode 100644 samples/common/src/CVUtils/CvWindowOutput.cpp create mode 100644 samples/common/src/Utils/CmdArgsParser.cpp (limited to 'samples/common') diff --git a/samples/common/cmake/aarch64-toolchain.cmake b/samples/common/cmake/aarch64-toolchain.cmake new file mode 100644 index 0000000000..bdd02f88c0 --- /dev/null +++ b/samples/common/cmake/aarch64-toolchain.cmake @@ -0,0 +1,20 @@ +# Copyright © 2020 Arm Ltd and Contributors. All rights reserved. +# SPDX-License-Identifier: MIT + +# specify the cross compiler +set(GNU_MACHINE "aarch64-linux-gnu") +set(CROSS_PREFIX "aarch64-linux-gnu-") + +set(CMAKE_C_COMPILER ${CROSS_PREFIX}gcc) +set(CMAKE_CXX_COMPILER ${CROSS_PREFIX}g++) +set(CMAKE_AR ${CROSS_PREFIX}ar) +set(CMAKE_STRIP ${CROSS_PREFIX}strip) +set(CMAKE_LINKER ${CROSS_PREFIX}ld) + +set(CMAKE_CROSSCOMPILING true) +set(CMAKE_SYSTEM_NAME Linux) + +set(CMAKE_SYSTEM_PROCESSOR aarch64) + +set(OPENCV_EXTRA_ARGS "-DENABLE_NEON=ON" + "-DCMAKE_TOOLCHAIN_FILE=platforms/linux/aarch64-gnu.toolchain.cmake") \ No newline at end of file diff --git a/samples/common/cmake/arm-linux-gnueabihf-toolchain.cmake b/samples/common/cmake/arm-linux-gnueabihf-toolchain.cmake new file mode 100644 index 0000000000..f66b964c35 --- /dev/null +++ b/samples/common/cmake/arm-linux-gnueabihf-toolchain.cmake @@ -0,0 +1,20 @@ +# Copyright © 2020 Arm Ltd and Contributors. All rights reserved. +# SPDX-License-Identifier: MIT + +# specify the cross compiler +set(GNU_MACHINE "arm-linux-gnueabihf") +set(CROSS_PREFIX "arm-linux-gnueabihf-") + +set(CMAKE_C_COMPILER ${CROSS_PREFIX}gcc) +set(CMAKE_CXX_COMPILER ${CROSS_PREFIX}g++) +set(CMAKE_AR ${CROSS_PREFIX}ar) +set(CMAKE_STRIP ${CROSS_PREFIX}strip) +set(CMAKE_LINKER ${CROSS_PREFIX}ld) + +set(CMAKE_CROSSCOMPILING true) +set(CMAKE_SYSTEM_NAME Linux) + +set(CMAKE_SYSTEM_PROCESSOR arm) + +set(OPENCV_EXTRA_ARGS "-DENABLE_NEON=ON" + "-DCMAKE_TOOLCHAIN_FILE=platforms/linux/arm.toolchain.cmake") \ No newline at end of file diff --git a/samples/common/cmake/find_armnn.cmake b/samples/common/cmake/find_armnn.cmake new file mode 100644 index 0000000000..289e9127f6 --- /dev/null +++ b/samples/common/cmake/find_armnn.cmake @@ -0,0 +1,35 @@ +# Copyright © 2020 Arm Ltd and Contributors. All rights reserved. +# SPDX-License-Identifier: MIT +# Search for ArmNN built libraries in user-provided path first, then current repository, then system + +set(ARMNN_LIB_NAMES "libarmnn.so" + "libarmnnTfLiteParser.so") + +set(ARMNN_LIBS "") + +get_filename_component(PARENT_DIR ${PROJECT_SOURCE_DIR} DIRECTORY) +get_filename_component(REPO_DIR ${PARENT_DIR} DIRECTORY) + +foreach(armnn_lib ${ARMNN_LIB_NAMES}) + find_library(ARMNN_${armnn_lib} + NAMES + ${armnn_lib} + HINTS + ${ARMNN_LIB_DIR} ${REPO_DIR} + PATHS + ${ARMNN_LIB_DIR} ${REPO_DIR} + PATH_SUFFIXES + "lib" + "lib64") + if(ARMNN_${armnn_lib}) + message("Found library ${ARMNN_${armnn_lib}}") + list(APPEND ARMNN_LIBS ${ARMNN_${armnn_lib}}) + get_filename_component(LIB_DIR ${ARMNN_${armnn_lib}} DIRECTORY) + get_filename_component(LIB_PARENT_DIR ${LIB_DIR} DIRECTORY) + set(ARMNN_INCLUDE_DIR ${LIB_PARENT_DIR}/include) + endif() +endforeach() + +if(NOT ARMNN_LIBS) + message(FATAL_ERROR "Could not find ArmNN libraries ${ARMNN_LIB_NAMES}") +endif() diff --git a/samples/common/cmake/find_catch.cmake b/samples/common/cmake/find_catch.cmake new file mode 100644 index 0000000000..584b8073bd --- /dev/null +++ b/samples/common/cmake/find_catch.cmake @@ -0,0 +1,16 @@ +# Copyright © 2020 Arm Ltd and Contributors. All rights reserved. +# SPDX-License-Identifier: MIT + +#Test TPIP +set(TEST_TPIP ${DEPENDENCIES_DIR}/test) +file(MAKE_DIRECTORY ${TEST_TPIP}) +set(TEST_TPIP_INCLUDE ${TEST_TPIP}/include) +file(MAKE_DIRECTORY ${TEST_TPIP_INCLUDE}) + +ExternalProject_Add(catch2-headers + URL https://github.com/catchorg/Catch2/releases/download/v2.11.1/catch.hpp + DOWNLOAD_NO_EXTRACT 1 + CONFIGURE_COMMAND "" + BUILD_COMMAND ${CMAKE_COMMAND} -E copy /catch.hpp ${TEST_TPIP_INCLUDE} + INSTALL_COMMAND "" + ) \ No newline at end of file diff --git a/samples/common/cmake/find_opencv.cmake b/samples/common/cmake/find_opencv.cmake new file mode 100644 index 0000000000..92086e1316 --- /dev/null +++ b/samples/common/cmake/find_opencv.cmake @@ -0,0 +1,203 @@ +# Copyright © 2020 Arm Ltd and Contributors. All rights reserved. +# SPDX-License-Identifier: MIT + +set(OPENCV_VERSION 4.0.0) +set(FFMPEG_VERSION 4.2.1) +set(LIBX264_VERSION stable) + +set(OPENCV_LIB OpenCV${OPENCV_VERSION}) +set(FFMPEG_LIB ffmpeg${FFMPEG_VERSION}) +set(X264_LIB x264${LIBX264_VERSION}) + +set(OPENCV_NAMES + libopencv_core.so.${OPENCV_VERSION} + libopencv_imgproc.so.${OPENCV_VERSION} + libopencv_imgcodecs.so.${OPENCV_VERSION} + libopencv_videoio.so.${OPENCV_VERSION} + libopencv_video.so.${OPENCV_VERSION} + libopencv_highgui.so.${OPENCV_VERSION}) + +set(OPENCV_LIBS) +set(FFMPEG_LIBS) + +foreach(opencv_lib ${OPENCV_NAMES}) + find_library(OPENCV_${opencv_lib} + NAMES + ${opencv_lib} + HINTS + ${OPENCV_LIB_DIR} + PATHS + ${OPENCV_LIB_DIR} + PATH_SUFFIXES + "lib" + "lib64") + if(OPENCV_${opencv_lib}) + message("Found library ${OPENCV_${opencv_lib}}") + list(APPEND OPENCV_LIBS ${OPENCV_${opencv_lib}}) + get_filename_component(OPENCV_LIB_DIR ${OPENCV_${opencv_lib}} DIRECTORY) + get_filename_component(OPENCV_ROOT_DIR ${OPENCV_LIB_DIR} DIRECTORY) + set(OPENCV_INCLUDE_DIR ${OPENCV_ROOT_DIR}/include/opencv4) + endif() +endforeach() + +if(OPENCV_LIBS) + message("OpenCV libraries found") + set(OPENCV_LIBS_FOUND TRUE) +else() + set(OPENCV_ROOT_DIR ${DEPENDENCIES_DIR}/opencv) + set(OPENCV_DEPENDENCIES_ARGS) + set(OPENCV_EXTRA_LINKER_ARGS) + set(OPENCV_PKGCONFIG) + + if(CMAKE_CROSSCOMPILING) + set(FFMPEG_ROOT_DIR ${DEPENDENCIES_DIR}/ffmpeg) + set(LIBX264_ROOT_DIR ${DEPENDENCIES_DIR}/x264) + + if (CMAKE_BUILD_TYPE STREQUAL Debug) + set(CONFIGURE_DEBUG --enable-debug) + set(OPENCV_DEBUG "-DBUILD_WITH_DEBUG_INFO=ON") + endif() + + + ExternalProject_Add(${X264_LIB} + URL "https://code.videolan.org/videolan/x264/-/archive/${LIBX264_VERSION}/x264-${LIBX264_VERSION}.tar.gz" + DOWNLOAD_DIR ${LIBX264_ROOT_DIR} + PREFIX ${LIBX264_ROOT_DIR} + CONFIGURE_COMMAND /configure + --host=${GNU_MACHINE} + --enable-static + --enable-shared + --cross-prefix=${CROSS_PREFIX} + --prefix=${CMAKE_BINARY_DIR} + --extra-ldflags=-static-libstdc++ + --extra-cflags=-fPIC + ${CONFIGURE_DEBUG} + INSTALL_DIR ${CMAKE_BINARY_DIR} + BUILD_COMMAND $(MAKE) + INSTALL_COMMAND $(MAKE) install + ) + + set(FFMPEG_Config + --enable-shared + --enable-cross-compile + --cross-prefix=${CROSS_PREFIX} + --arch=${CMAKE_SYSTEM_PROCESSOR} + --target-os=linux + --prefix=${CMAKE_BINARY_DIR} + --enable-gpl + --enable-nonfree + --enable-libx264 + --extra-cflags=-I${CMAKE_BINARY_DIR}/include + --extra-cflags=-fPIC + --extra-ldflags=-L${CMAKE_BINARY_DIR}/lib + --extra-libs=-ldl + --extra-libs=-static-libstdc++ + ) + + ExternalProject_Add(${FFMPEG_LIB} + URL "https://github.com/FFmpeg/FFmpeg/archive/n${FFMPEG_VERSION}.tar.gz" + URL_HASH MD5=05792c611d1e3ebdf2c7003ff4467390 + DOWNLOAD_DIR ${FFMPEG_ROOT_DIR} + PREFIX ${FFMPEG_ROOT_DIR} + CONFIGURE_COMMAND /configure ${FFMPEG_Config} ${CONFIGURE_DEBUG} + INSTALL_DIR ${CMAKE_BINARY_DIR} + BUILD_COMMAND $(MAKE) VERBOSE=1 + INSTALL_COMMAND $(MAKE) install + ) + + set(OPENCV_DEPENDENCIES_ARGS "-static-libstdc++ -Wl,-rpath,${CMAKE_BINARY_DIR}/lib") + set(OPENCV_EXTRA_LINKER_ARGS "-DOPENCV_EXTRA_EXE_LINKER_FLAGS=${OPENCV_DEPENDENCIES_ARGS}") + + set(OPENCV_PKGCONFIG "PKG_CONFIG_LIBDIR=${CMAKE_BINARY_DIR}/lib/pkgconfig") + + set(FFMPEG_NAMES + libavcodec.so + libavformat.so + libavutil.so + libswscale.so + ) + + foreach(ffmpeg_lib ${FFMPEG_NAMES}) + add_library(FFMPEG_${ffmpeg_lib} SHARED IMPORTED) + set_target_properties(FFMPEG_${ffmpeg_lib} PROPERTIES IMPORTED_LOCATION ${CMAKE_BINARY_DIR}/lib/${ffmpeg_lib}) + list(APPEND OPENCV_LIBS FFMPEG_${ffmpeg_lib}) + endforeach() + + add_library(X264_lib264.so SHARED IMPORTED) + set_target_properties(X264_lib264.so PROPERTIES IMPORTED_LOCATION ${CMAKE_BINARY_DIR}/lib/libx264.so) + list(APPEND OPENCV_LIBS X264_lib264.so) + endif() + + set(OPENCV_CMAKE_ARGS + -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE} + -DCMAKE_C_FLAGS=-fPIC + -DCMAKE_CXX_FLAGS=-fPIC + -DWITH_GTK=OFF + -DWITH_JPEG=ON + -DWITH_IPP=OFF + -DBUILD_opencv_java_bindings_generator=OFF + -DBUILD_opencv_ml=OFF + -DBUILD_opencv_objdetect=OFF + -DBUILD_opencv_photo=OFF + -DBUILD_opencv_python_bindings_generator=OFF + -DBUILD_opencv_stitching=OFF + -DBUILD_opencv_gapi=OFF + -DBUILD_opencv_features2d=OFF + -DBUILD_opencv_dnn=OFF + -DBUILD_opencv_flann=OFF + -DBUILD_opencv_calib3d=OFF + -DBUILD_opencv_python2=OFF + -DBUILD_opencv_python3=OFF + -DBUILD_opencv_java=OFF + -DBUILD_opencv_js=OFF + -DBUILD_opencv_ts=OFF + -DBUILD_JPEG=ON + -DBUILD_JPEG_TURBO_DISABLE=ON + -DBUILD_PNG=ON + -DBUILD_TIFF=ON + -DZLIB_FOUND=OFF + -DBUILD_ZLIB=ON + -DBUILD_PERF_TESTS=OFF + -DBUILD_TESTS=OFF + -DBUILD_DOCS=OFF + -DBUILD_opencv_apps=OFF + -DBUILD_EXAMPLES=OFF + -DWITH_V4L=ON + -DWITH_LIBV4L=OFF + -DWITH_FFMPEG=ON + -DCMAKE_INSTALL_PREFIX=${CMAKE_BINARY_DIR} + -DCMAKE_CXX_COMPILER=${CMAKE_CXX_COMPILER} + -DCMAKE_C_COMPILER=${CMAKE_C_COMPILER} + -DCMAKE_INSTALL_RPATH=\$ORIGIN:\$ORIGIN/lib:\$ORIGIN/../lib + -DCMAKE_SHARED_LINKER_FLAGS=-static-libstdc++ + ${OPENCV_DEBUG} + ) + + ExternalProject_Add(${OPENCV_LIB} + URL "https://codeload.github.com/opencv/opencv/tar.gz/${OPENCV_VERSION}" + URL_HASH MD5=f051c1ff7b327b60123d71b53801b316 + DOWNLOAD_DIR ${OPENCV_ROOT_DIR} + PREFIX ${OPENCV_ROOT_DIR} + CONFIGURE_COMMAND ${OPENCV_PKGCONFIG} + ${CMAKE_COMMAND} ${OPENCV_CMAKE_ARGS} ${OPENCV_EXTRA_ARGS} + ${OPENCV_EXTRA_LINKER_ARGS} ${OPENCV_ROOT_DIR}/src/${OPENCV_LIB} + INSTALL_DIR ${CMAKE_BINARY_DIR} + BUILD_COMMAND $(MAKE) + INSTALL_COMMAND $(MAKE) install + ) + + if(CMAKE_CROSSCOMPILING) + ExternalProject_Add_StepDependencies(${FFMPEG_LIB} build ${X264_LIB}) + ExternalProject_Add_StepDependencies(${OPENCV_LIB} build ${FFMPEG_LIB}) + endif() + + set(OPENCV_INCLUDE_DIR ${CMAKE_BINARY_DIR}/include/opencv4) + set(OPENCV_LIB_DIR ${CMAKE_BINARY_DIR}/lib) + + foreach(opencv_lib ${OPENCV_NAMES}) + add_library(OPENCV_${opencv_lib} SHARED IMPORTED) + set_target_properties(OPENCV_${opencv_lib} PROPERTIES IMPORTED_LOCATION ${OPENCV_LIB_DIR}/${opencv_lib}) + list(APPEND OPENCV_LIBS OPENCV_${opencv_lib}) + endforeach() + +endif() \ No newline at end of file diff --git a/samples/common/include/ArmnnUtils/ArmnnNetworkExecutor.hpp b/samples/common/include/ArmnnUtils/ArmnnNetworkExecutor.hpp new file mode 100644 index 0000000000..96cc1d0184 --- /dev/null +++ b/samples/common/include/ArmnnUtils/ArmnnNetworkExecutor.hpp @@ -0,0 +1,214 @@ +// +// Copyright © 2020 Arm Ltd and Contributors. All rights reserved. +// SPDX-License-Identifier: MIT +// + +#pragma once + +#include "Types.hpp" + +#include "armnn/ArmNN.hpp" +#include "armnnTfLiteParser/ITfLiteParser.hpp" +#include "armnnUtils/DataLayoutIndexed.hpp" +#include + +#include +#include + +namespace common +{ +/** +* @brief Used to load in a network through ArmNN and run inference on it against a given backend. +* +*/ +template +class ArmnnNetworkExecutor +{ +private: + armnn::IRuntimePtr m_Runtime; + armnn::NetworkId m_NetId{}; + mutable InferenceResults m_OutputBuffer; + armnn::InputTensors m_InputTensors; + armnn::OutputTensors m_OutputTensors; + std::vector m_outputBindingInfo; + + std::vector m_outputLayerNamesList; + + armnnTfLiteParser::BindingPointInfo m_inputBindingInfo; + + void PrepareTensors(const void* inputData, const size_t dataBytes); + + template + auto log_as_int(Enumeration value) + -> typename std::underlying_type::type + { + return static_cast::type>(value); + } + +public: + ArmnnNetworkExecutor() = delete; + + /** + * @brief Initializes the network with the given input data. Parsed through TfLiteParser and optimized for a + * given backend. + * + * Note that the output layers names order in m_outputLayerNamesList affects the order of the feature vectors + * in output of the Run method. + * + * * @param[in] modelPath - Relative path to the model file + * * @param[in] backends - The list of preferred backends to run inference on + */ + ArmnnNetworkExecutor(std::string& modelPath, + std::vector& backends); + + /** + * @brief Returns the aspect ratio of the associated model in the order of width, height. + */ + Size GetImageAspectRatio(); + + armnn::DataType GetInputDataType() const; + + float GetQuantizationScale(); + + int GetQuantizationOffset(); + + /** + * @brief Runs inference on the provided input data, and stores the results in the provided InferenceResults object. + * + * @param[in] inputData - input frame data + * @param[in] dataBytes - input data size in bytes + * @param[out] results - Vector of DetectionResult objects used to store the output result. + */ + bool Run(const void* inputData, const size_t dataBytes, common::InferenceResults& outResults); + +}; + +template +ArmnnNetworkExecutor::ArmnnNetworkExecutor(std::string& modelPath, + std::vector& preferredBackends) + : m_Runtime(armnn::IRuntime::Create(armnn::IRuntime::CreationOptions())) +{ + // Import the TensorFlow lite model. + armnnTfLiteParser::ITfLiteParserPtr parser = armnnTfLiteParser::ITfLiteParser::Create(); + armnn::INetworkPtr network = parser->CreateNetworkFromBinaryFile(modelPath.c_str()); + + std::vector inputNames = parser->GetSubgraphInputTensorNames(0); + + m_inputBindingInfo = parser->GetNetworkInputBindingInfo(0, inputNames[0]); + + m_outputLayerNamesList = parser->GetSubgraphOutputTensorNames(0); + + std::vector outputBindings; + for(const std::string& name : m_outputLayerNamesList) + { + m_outputBindingInfo.push_back(std::move(parser->GetNetworkOutputBindingInfo(0, name))); + } + std::vector errorMessages; + // optimize the network. + armnn::IOptimizedNetworkPtr optNet = Optimize(*network, + preferredBackends, + m_Runtime->GetDeviceSpec(), + armnn::OptimizerOptions(), + armnn::Optional&>(errorMessages)); + + if (!optNet) + { + const std::string errorMessage{"ArmnnNetworkExecutor: Failed to optimize network"}; + ARMNN_LOG(error) << errorMessage; + throw armnn::Exception(errorMessage); + } + + // Load the optimized network onto the m_Runtime device + std::string errorMessage; + if (armnn::Status::Success != m_Runtime->LoadNetwork(m_NetId, std::move(optNet), errorMessage)) + { + ARMNN_LOG(error) << errorMessage; + throw armnn::Exception(errorMessage); + } + + //pre-allocate memory for output (the size of it never changes) + for (int it = 0; it < m_outputLayerNamesList.size(); ++it) + { + const armnn::DataType dataType = m_outputBindingInfo[it].second.GetDataType(); + const armnn::TensorShape& tensorShape = m_outputBindingInfo[it].second.GetShape(); + + std::vector oneLayerOutResult; + oneLayerOutResult.resize(tensorShape.GetNumElements(), 0); + m_OutputBuffer.emplace_back(oneLayerOutResult); + + // Make ArmNN output tensors + m_OutputTensors.reserve(m_OutputBuffer.size()); + for (size_t it = 0; it < m_OutputBuffer.size(); ++it) + { + m_OutputTensors.emplace_back(std::make_pair( + m_outputBindingInfo[it].first, + armnn::Tensor(m_outputBindingInfo[it].second, + m_OutputBuffer.at(it).data()) + )); + } + } + +} + +template +armnn::DataType ArmnnNetworkExecutor::GetInputDataType() const +{ + return m_inputBindingInfo.second.GetDataType(); +} + +template +void ArmnnNetworkExecutor::PrepareTensors(const void* inputData, const size_t dataBytes) +{ + assert(m_inputBindingInfo.second.GetNumBytes() >= dataBytes); + m_InputTensors.clear(); + m_InputTensors = {{ m_inputBindingInfo.first, armnn::ConstTensor(m_inputBindingInfo.second, inputData)}}; +} + +template +bool ArmnnNetworkExecutor::Run(const void* inputData, const size_t dataBytes, InferenceResults& outResults) +{ + /* Prepare tensors if they are not ready */ + ARMNN_LOG(debug) << "Preparing tensors..."; + this->PrepareTensors(inputData, dataBytes); + ARMNN_LOG(trace) << "Running inference..."; + + armnn::Status ret = m_Runtime->EnqueueWorkload(m_NetId, m_InputTensors, m_OutputTensors); + + std::stringstream inferenceFinished; + inferenceFinished << "Inference finished with code {" << log_as_int(ret) << "}\n"; + + ARMNN_LOG(trace) << inferenceFinished.str(); + + if (ret == armnn::Status::Failure) + { + ARMNN_LOG(error) << "Failed to perform inference."; + } + + outResults.reserve(m_outputLayerNamesList.size()); + outResults = m_OutputBuffer; + + return (armnn::Status::Success == ret); +} + +template +float ArmnnNetworkExecutor::GetQuantizationScale() +{ + return this->m_inputBindingInfo.second.GetQuantizationScale(); +} + +template +int ArmnnNetworkExecutor::GetQuantizationOffset() +{ + return this->m_inputBindingInfo.second.GetQuantizationOffset(); +} + +template +Size ArmnnNetworkExecutor::GetImageAspectRatio() +{ + const auto shape = m_inputBindingInfo.second.GetShape(); + assert(shape.GetNumDimensions() == 4); + armnnUtils::DataLayoutIndexed nhwc(armnn::DataLayout::NHWC); + return Size(shape[nhwc.GetWidthIndex()], + shape[nhwc.GetHeightIndex()]); +} +}// namespace common \ No newline at end of file diff --git a/samples/common/include/CVUtils/CvVideoFileWriter.hpp b/samples/common/include/CVUtils/CvVideoFileWriter.hpp new file mode 100644 index 0000000000..30348f09cc --- /dev/null +++ b/samples/common/include/CVUtils/CvVideoFileWriter.hpp @@ -0,0 +1,61 @@ +// +// Copyright © 2020 Arm Ltd and Contributors. All rights reserved. +// SPDX-License-Identifier: MIT +// + +#pragma once + +#include "IFrameOutput.hpp" +#include + +namespace common +{ + +class CvVideoFileWriter : public IFrameOutput { +public: + /** + * @brief Default constructor. + * + * Underlying open cv video writer object will be instantiated. + */ + CvVideoFileWriter() = default; + + ~CvVideoFileWriter() override = default; + + /** + * @brief Initialises video file writer. + * + * Opens opencv writer with given params. FFMPEG backend is used. + * + * @param outputVideo path to the video file. + * @param encoding cv::CAP_PROP_FOURCC code. + * @param fps target frame rate. + * @param width target frame width. + * @param height target frame height. + * + */ + void Init(const std::string& outputVideo, int encoding, double fps, int width, int height); + + /** + * Writes frame to the file using opencv writer. + * + * @param frame data to write. + */ + void WriteFrame(std::shared_ptr& frame) override; + + /** + * Releases opencv writer. + */ + void Close() override; + + /** + * Checks if opencv writer was successfully opened. + * @return true is underlying writer is ready to be used, false otherwise. + */ + bool IsReady() const override; + +private: + cv::VideoWriter m_cvWriter{}; + bool m_ready = false; +}; +}// namespace common \ No newline at end of file diff --git a/samples/common/include/CVUtils/CvVideoFrameReader.hpp b/samples/common/include/CVUtils/CvVideoFrameReader.hpp new file mode 100644 index 0000000000..96d94f4079 --- /dev/null +++ b/samples/common/include/CVUtils/CvVideoFrameReader.hpp @@ -0,0 +1,108 @@ +// +// Copyright © 2020 Arm Ltd and Contributors. All rights reserved. +// SPDX-License-Identifier: MIT +// +#pragma once + + +#include "IFrameReader.hpp" +#include + +namespace common +{ + +class CvVideoFrameReader : + public IFrameReader +{ +public: + /** + * @brief Default constructor. + * + * Underlying open cv video capture object will be instantiated. + */ + CvVideoFrameReader() = default; + + ~CvVideoFrameReader() override = default; + + /** + *@brief Initialises reader to capture frames from video file. + * + * @param source path to the video file or image sequence. + * + * @throws std::runtime_error if init failed + */ + void Init(const std::string& source); + + std::shared_ptr ReadFrame() override; + + bool IsExhausted(const std::shared_ptr & frame) const override; + + /** + * Returns effective video frame width supported by the source/set by the user. + * Must be called after Init method. + * @return frame width + */ + int GetSourceWidth() const; + + /** + * Returns effective video frame height supported by the source/set by the user. + * Must be called after Init method. + * @return frame height + */ + int GetSourceHeight() const; + + /** + * Returns effective fps value supported by the source/set by the user. + * @return fps value + */ + double GetSourceFps() const; + + /** + * Will query OpenCV to convert images to RGB + * Copy is actually default behaviour, but the set function needs to be called + * in order to know whether OpenCV supports conversion from our source format. + * @return boolean, + * true: OpenCV returns RGB + * false: OpenCV returns the fourcc format from GetSourceEncoding + */ + bool ConvertToRGB(); + + /** + * Returns 4-character code of codec. + * @return codec name + */ + std::string GetSourceEncoding() const; + + /** + * Get the fourcc int from its string name. + * @return codec int + */ + int GetSourceEncodingInt() const; + + int GetFrameCount() const; + +private: + cv::VideoCapture m_capture; + + void CheckIsOpen(const std::string& source); +}; + +class CvVideoFrameReaderRgbWrapper : + public IFrameReader +{ +public: + CvVideoFrameReaderRgbWrapper() = delete; + CvVideoFrameReaderRgbWrapper(const CvVideoFrameReaderRgbWrapper& o) = delete; + CvVideoFrameReaderRgbWrapper(CvVideoFrameReaderRgbWrapper&& o) = delete; + + CvVideoFrameReaderRgbWrapper(std::unique_ptr reader); + + std::shared_ptr ReadFrame() override; + + bool IsExhausted(const std::shared_ptr& frame) const override; + +private: + std::unique_ptr m_reader; +}; + +}// namespace common \ No newline at end of file diff --git a/samples/common/include/CVUtils/CvWindowOutput.hpp b/samples/common/include/CVUtils/CvWindowOutput.hpp new file mode 100644 index 0000000000..4b9ae3b743 --- /dev/null +++ b/samples/common/include/CVUtils/CvWindowOutput.hpp @@ -0,0 +1,53 @@ +// +// Copyright © 2020 Arm Ltd and Contributors. All rights reserved. +// SPDX-License-Identifier: MIT +// + +#pragma once + +#include "IFrameOutput.hpp" +#include + +namespace common +{ + +class CvWindowOutput : public IFrameOutput { +public: + + CvWindowOutput() = default; + + ~CvWindowOutput() override = default; + + /** + * @brief Creates a named window. + * + * Uses opencv to create a window with given name. + * + * @param windowName opencv window name. + * + */ + void Init(const std::string& windowName); + + /** + * Writes frame to the window. + * + * @param frame data to write. + */ + void WriteFrame(std::shared_ptr& frame) override; + + /** + * Releases all windows. + */ + void Close() override; + + /** + * Always true. + * @return true. + */ + bool IsReady() const override; + +private: + std::string m_windowName; + +}; +}// namespace common \ No newline at end of file diff --git a/samples/common/include/CVUtils/IFrameOutput.hpp b/samples/common/include/CVUtils/IFrameOutput.hpp new file mode 100644 index 0000000000..6f7ca0b574 --- /dev/null +++ b/samples/common/include/CVUtils/IFrameOutput.hpp @@ -0,0 +1,48 @@ +// +// Copyright © 2020 Arm Ltd and Contributors. All rights reserved. +// SPDX-License-Identifier: MIT +// + +#pragma once + +#include +#include + +namespace common +{ +/** + * @brief Frames output interface + * + * @tparam FrameDataT frame container data type + */ + template class IFrameOutput + { + + public: + /** + * @brief Writes frame to the selected output + * + * @param frame container + */ + virtual void WriteFrame(std::shared_ptr & frame) = 0; + + /** + * @brief Closes the frame output + */ + virtual void Close() = 0; + + /** + * @brief Checks if the frame sink is ready to write. + * + * @return True if frame sink is ready, False otherwise + */ + virtual bool IsReady() const = 0; + + /** + * @brief Default destructor + */ + virtual ~IFrameOutput() = default; + + }; + +}// namespace common diff --git a/samples/common/include/CVUtils/IFrameReader.hpp b/samples/common/include/CVUtils/IFrameReader.hpp new file mode 100644 index 0000000000..e171b3bb94 --- /dev/null +++ b/samples/common/include/CVUtils/IFrameReader.hpp @@ -0,0 +1,45 @@ +// +// Copyright © 2020 Arm Ltd and Contributors. All rights reserved. +// SPDX-License-Identifier: MIT +// + +#pragma once + +#include +#include + +namespace common +{ +/** + * @brief Frame source reader interface + * + * @tparam FrameDataT frame container data type + */ +template class IFrameReader +{ + +public: + /** + * @brief Reads the next frame from the source + * + * @return pointer to the frame container + */ + virtual std::shared_ptr ReadFrame() = 0; + + /** + * @brief Checks if the frame source has more frames to read. + * + * @param[in] frame the pointer to the last frame captured with the ReadFrame method could be used in + * implementation specific logic to check frames source state. + * @return True if frame source was exhausted, False otherwise + */ + virtual bool IsExhausted(const std::shared_ptr & frame) const = 0; + + /** + * @brief Default destructor + */ + virtual ~IFrameReader() = default; + +}; + +}// namespace common \ No newline at end of file diff --git a/samples/common/include/Utils/CmdArgsParser.hpp b/samples/common/include/Utils/CmdArgsParser.hpp new file mode 100644 index 0000000000..710a33df93 --- /dev/null +++ b/samples/common/include/Utils/CmdArgsParser.hpp @@ -0,0 +1,25 @@ +// +// Copyright © 2020 Arm Ltd and Contributors. All rights reserved. +// SPDX-License-Identifier: MIT +// +#pragma once +#include +#include + +/* + * Checks that a particular option was specified by the user + */ +bool CheckOptionSpecified(const std::map& options, const std::string& option); + + +/* + * Retrieves the user provided option + */ +std::string GetSpecifiedOption(const std::map& options, const std::string& option); + + +/* + * Parses all the command line options provided by the user and stores in a map. + */ +int ParseOptions(std::map& options, std::map& acceptedOptions, + char *argv[], int argc); \ No newline at end of file diff --git a/samples/common/include/Utils/Types.hpp b/samples/common/include/Utils/Types.hpp new file mode 100644 index 0000000000..4d1f708844 --- /dev/null +++ b/samples/common/include/Utils/Types.hpp @@ -0,0 +1,54 @@ +// +// Copyright © 2020 Arm Ltd and Contributors. All rights reserved. +// SPDX-License-Identifier: MIT +// + +#pragma once + +#include +#include +#include +#include + +#include + +namespace common +{ + +struct Size +{ + + uint32_t m_Width; + uint32_t m_Height; + + Size() : Size(0, 0) {} + + Size(uint32_t width, uint32_t height) : + m_Width{width}, m_Height{height} {} + + Size(const Size& other) + : Size(other.m_Width, other.m_Height) {} + + ~Size() = default; + + Size &operator=(const Size& other) = default; +}; + +struct BBoxColor +{ + std::tuple colorCode; +}; + +struct PipelineOptions +{ + std::string m_ModelName; + std::string m_ModelFilePath; + std::vector m_backends; +}; + +template +using InferenceResult = std::vector; + +template +using InferenceResults = std::vector>; +} // namespace common \ No newline at end of file diff --git a/samples/common/src/CVUtils/CvVideoFileWriter.cpp b/samples/common/src/CVUtils/CvVideoFileWriter.cpp new file mode 100644 index 0000000000..b76630049a --- /dev/null +++ b/samples/common/src/CVUtils/CvVideoFileWriter.cpp @@ -0,0 +1,38 @@ +// +// Copyright © 2020 Arm Ltd and Contributors. All rights reserved. +// SPDX-License-Identifier: MIT +// + +#include "CvVideoFileWriter.hpp" + +namespace common +{ + +void CvVideoFileWriter::Init(const std::string& outputVideo, int encoding, double fps, int width, int height) +{ + m_ready = m_cvWriter.open(outputVideo, cv::CAP_FFMPEG, + encoding, + fps, + cv::Size(width, height), true); +} + + +void CvVideoFileWriter::WriteFrame(std::shared_ptr& frame) +{ + if(m_cvWriter.isOpened()) + { + cv::cvtColor(*frame, *frame, cv::COLOR_RGB2BGR); + m_cvWriter.write(*frame); + } +} + +bool CvVideoFileWriter::IsReady() const +{ + return m_ready; +} + +void CvVideoFileWriter::Close() +{ + m_cvWriter.release(); +} +}// namespace common diff --git a/samples/common/src/CVUtils/CvVideoFrameReader.cpp b/samples/common/src/CVUtils/CvVideoFrameReader.cpp new file mode 100644 index 0000000000..2bd92d2d81 --- /dev/null +++ b/samples/common/src/CVUtils/CvVideoFrameReader.cpp @@ -0,0 +1,98 @@ +// +// Copyright © 2020 Arm Ltd and Contributors. All rights reserved. +// SPDX-License-Identifier: MIT +// + + +#include "CvVideoFrameReader.hpp" + +namespace common +{ + +std::shared_ptr CvVideoFrameReader::ReadFrame() +{ + // opencv copies data anyway + cv::Mat captureFrame; + m_capture.read(captureFrame); + return std::make_shared(std::move(captureFrame)); +} + +bool CvVideoFrameReader::IsExhausted(const std::shared_ptr& frame) const +{ + assert(frame!=nullptr); + return frame->empty(); +} + +void CvVideoFrameReader::CheckIsOpen(const std::string& source) +{ + if (!m_capture.isOpened()) + { + throw std::runtime_error("Failed to open video capture for the source = " + source); + } +} + +void CvVideoFrameReader::Init(const std::string& source) +{ + m_capture.open(source); + CheckIsOpen(source); +} + +int CvVideoFrameReader::GetSourceWidth() const +{ + return static_cast(lround(m_capture.get(cv::CAP_PROP_FRAME_WIDTH))); +} + +int CvVideoFrameReader::GetSourceHeight() const +{ + return static_cast(lround(m_capture.get(cv::CAP_PROP_FRAME_HEIGHT))); +} + +double CvVideoFrameReader::GetSourceFps() const +{ + return m_capture.get(cv::CAP_PROP_FPS); +} + +bool CvVideoFrameReader::ConvertToRGB() +{ + m_capture.set(cv::CAP_PROP_CONVERT_RGB, 1.0); + return static_cast(m_capture.get(cv::CAP_PROP_CONVERT_RGB)); +} + +std::string CvVideoFrameReader::GetSourceEncoding() const +{ + char fourccStr[5]; + auto fourcc = (int)m_capture.get(cv::CAP_PROP_FOURCC); + sprintf(fourccStr,"%c%c%c%c",fourcc & 0xFF, (fourcc >> 8) & 0xFF, (fourcc >> 16) & 0xFF, (fourcc >> 24) & 0xFF); + return fourccStr; +} + +int CvVideoFrameReader::GetSourceEncodingInt() const +{ + return (int)m_capture.get(cv::CAP_PROP_FOURCC); +} + +int CvVideoFrameReader::GetFrameCount() const +{ + return static_cast(lround(m_capture.get(cv::CAP_PROP_FRAME_COUNT))); +}; + +std::shared_ptr CvVideoFrameReaderRgbWrapper::ReadFrame() +{ + auto framePtr = m_reader->ReadFrame(); + if (!IsExhausted(framePtr)) + { + cv::cvtColor(*framePtr, *framePtr, cv::COLOR_BGR2RGB); + } + return framePtr; +} + +bool CvVideoFrameReaderRgbWrapper::IsExhausted(const std::shared_ptr& frame) const +{ + return m_reader->IsExhausted(frame); +} + +CvVideoFrameReaderRgbWrapper::CvVideoFrameReaderRgbWrapper(std::unique_ptr reader): + m_reader(std::move(reader)) +{} + +}// namespace common \ No newline at end of file diff --git a/samples/common/src/CVUtils/CvWindowOutput.cpp b/samples/common/src/CVUtils/CvWindowOutput.cpp new file mode 100644 index 0000000000..190a7602e2 --- /dev/null +++ b/samples/common/src/CVUtils/CvWindowOutput.cpp @@ -0,0 +1,33 @@ +// +// Copyright © 2020 Arm Ltd and Contributors. All rights reserved. +// SPDX-License-Identifier: MIT +// + +#include "CvWindowOutput.hpp" + +namespace common +{ + +void CvWindowOutput::Init(const std::string& windowName) +{ + m_windowName = windowName; + cv::namedWindow(m_windowName, cv::WINDOW_AUTOSIZE); +} + +void CvWindowOutput::WriteFrame(std::shared_ptr& frame) +{ + cv::cvtColor(*frame, *frame, cv::COLOR_RGB2BGR); + cv::imshow( m_windowName, *frame); + cv::waitKey(30); +} + +void CvWindowOutput::Close() +{ + cv::destroyWindow(m_windowName); +} + +bool CvWindowOutput::IsReady() const +{ + return true; +} +}// namespace common \ No newline at end of file diff --git a/samples/common/src/Utils/CmdArgsParser.cpp b/samples/common/src/Utils/CmdArgsParser.cpp new file mode 100644 index 0000000000..1f09826a8b --- /dev/null +++ b/samples/common/src/Utils/CmdArgsParser.cpp @@ -0,0 +1,70 @@ +// +// Copyright © 2020 Arm Ltd and Contributors. All rights reserved. +// SPDX-License-Identifier: MIT +// + +#include "CmdArgsParser.hpp" +#include +/* + * Checks that a particular option was specified by the user + */ +bool CheckOptionSpecified(const std::map& options, const std::string& option) +{ + auto it = options.find(option); + return it!=options.end(); +} + +/* + * Retrieves the user provided option + */ +std::string GetSpecifiedOption(const std::map& options, const std::string& option) +{ + if (CheckOptionSpecified(options, option)){ + return options.at(option); + } + else + { + throw std::invalid_argument("Required option: " + option + " not defined."); + } +} + +/* + * Parses all the command line options provided by the user and stores in a map. + */ +int ParseOptions(std::map& options, std::map& acceptedOptions, + char *argv[], int argc) +{ + for (int i = 1; i < argc; ++i) + { + std::string currentOption = std::string(argv[i]); + auto it = acceptedOptions.find(currentOption); + if (it != acceptedOptions.end()) + { + if (i + 1 < argc && std::string(argv[i + 1]).rfind("--", 0) != 0) + { + std::string value = argv[++i]; + options.insert({it->first, value}); + } + else if (std::string(argv[i]) == "HELP") + { + std::cout << "Available options" << std::endl; + for (auto & acceptedOption : acceptedOptions) + { + std::cout << acceptedOption.first << " : " << acceptedOption.second << std::endl; + } + return 2; + } + else + { + std::cerr << std::string(argv[i]) << " option requires one argument." << std::endl; + return 1; + } + } + else + { + std::cerr << "Unrecognised option: " << std::string(argv[i]) << std::endl; + return 1; + } + } + return 0; +} -- cgit v1.2.1