diff options
Diffstat (limited to 'samples/common/include')
-rw-r--r-- | samples/common/include/ArmnnUtils/ArmnnNetworkExecutor.hpp | 214 | ||||
-rw-r--r-- | samples/common/include/CVUtils/CvVideoFileWriter.hpp | 61 | ||||
-rw-r--r-- | samples/common/include/CVUtils/CvVideoFrameReader.hpp | 108 | ||||
-rw-r--r-- | samples/common/include/CVUtils/CvWindowOutput.hpp | 53 | ||||
-rw-r--r-- | samples/common/include/CVUtils/IFrameOutput.hpp | 48 | ||||
-rw-r--r-- | samples/common/include/CVUtils/IFrameReader.hpp | 45 | ||||
-rw-r--r-- | samples/common/include/Utils/CmdArgsParser.hpp | 25 | ||||
-rw-r--r-- | samples/common/include/Utils/Types.hpp | 54 |
8 files changed, 608 insertions, 0 deletions
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 <armnn/Logging.hpp> + +#include <string> +#include <vector> + +namespace common +{ +/** +* @brief Used to load in a network through ArmNN and run inference on it against a given backend. +* +*/ +template <class Tout> +class ArmnnNetworkExecutor +{ +private: + armnn::IRuntimePtr m_Runtime; + armnn::NetworkId m_NetId{}; + mutable InferenceResults<Tout> m_OutputBuffer; + armnn::InputTensors m_InputTensors; + armnn::OutputTensors m_OutputTensors; + std::vector<armnnTfLiteParser::BindingPointInfo> m_outputBindingInfo; + + std::vector<std::string> m_outputLayerNamesList; + + armnnTfLiteParser::BindingPointInfo m_inputBindingInfo; + + void PrepareTensors(const void* inputData, const size_t dataBytes); + + template <typename Enumeration> + auto log_as_int(Enumeration value) + -> typename std::underlying_type<Enumeration>::type + { + return static_cast<typename std::underlying_type<Enumeration>::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<armnn::BackendId>& 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<Tout>& outResults); + +}; + +template <class Tout> +ArmnnNetworkExecutor<Tout>::ArmnnNetworkExecutor(std::string& modelPath, + std::vector<armnn::BackendId>& 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<std::string> inputNames = parser->GetSubgraphInputTensorNames(0); + + m_inputBindingInfo = parser->GetNetworkInputBindingInfo(0, inputNames[0]); + + m_outputLayerNamesList = parser->GetSubgraphOutputTensorNames(0); + + std::vector<armnn::BindingPointInfo> outputBindings; + for(const std::string& name : m_outputLayerNamesList) + { + m_outputBindingInfo.push_back(std::move(parser->GetNetworkOutputBindingInfo(0, name))); + } + std::vector<std::string> errorMessages; + // optimize the network. + armnn::IOptimizedNetworkPtr optNet = Optimize(*network, + preferredBackends, + m_Runtime->GetDeviceSpec(), + armnn::OptimizerOptions(), + armnn::Optional<std::vector<std::string>&>(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<Tout> 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 <class Tout> +armnn::DataType ArmnnNetworkExecutor<Tout>::GetInputDataType() const +{ + return m_inputBindingInfo.second.GetDataType(); +} + +template <class Tout> +void ArmnnNetworkExecutor<Tout>::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 <class Tout> +bool ArmnnNetworkExecutor<Tout>::Run(const void* inputData, const size_t dataBytes, InferenceResults<Tout>& 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 <class Tout> +float ArmnnNetworkExecutor<Tout>::GetQuantizationScale() +{ + return this->m_inputBindingInfo.second.GetQuantizationScale(); +} + +template <class Tout> +int ArmnnNetworkExecutor<Tout>::GetQuantizationOffset() +{ + return this->m_inputBindingInfo.second.GetQuantizationOffset(); +} + +template <class Tout> +Size ArmnnNetworkExecutor<Tout>::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 <opencv2/opencv.hpp> + +namespace common +{ + +class CvVideoFileWriter : public IFrameOutput<cv::Mat> { +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<cv::Mat>& 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 <opencv2/opencv.hpp> + +namespace common +{ + +class CvVideoFrameReader : + public IFrameReader<cv::Mat> +{ +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 <cv::Mat> ReadFrame() override; + + bool IsExhausted(const std::shared_ptr <cv::Mat>& 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<cv::Mat> +{ +public: + CvVideoFrameReaderRgbWrapper() = delete; + CvVideoFrameReaderRgbWrapper(const CvVideoFrameReaderRgbWrapper& o) = delete; + CvVideoFrameReaderRgbWrapper(CvVideoFrameReaderRgbWrapper&& o) = delete; + + CvVideoFrameReaderRgbWrapper(std::unique_ptr<common::CvVideoFrameReader> reader); + + std::shared_ptr<cv::Mat> ReadFrame() override; + + bool IsExhausted(const std::shared_ptr<cv::Mat>& frame) const override; + +private: + std::unique_ptr<common::CvVideoFrameReader> 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 <opencv2/opencv.hpp> + +namespace common +{ + +class CvWindowOutput : public IFrameOutput<cv::Mat> { +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<cv::Mat>& 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 <cstddef> +#include <memory> + +namespace common +{ +/** + * @brief Frames output interface + * + * @tparam FrameDataT frame container data type + */ + template<typename FrameDataT> class IFrameOutput + { + + public: + /** + * @brief Writes frame to the selected output + * + * @param frame container + */ + virtual void WriteFrame(std::shared_ptr <FrameDataT>& 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 <cstddef> +#include <memory> + +namespace common +{ +/** + * @brief Frame source reader interface + * + * @tparam FrameDataT frame container data type + */ +template<typename FrameDataT> class IFrameReader +{ + +public: + /** + * @brief Reads the next frame from the source + * + * @return pointer to the frame container + */ + virtual std::shared_ptr <FrameDataT> 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 <FrameDataT>& 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 <string> +#include <map> + +/* + * Checks that a particular option was specified by the user + */ +bool CheckOptionSpecified(const std::map<std::string, std::string>& options, const std::string& option); + + +/* + * Retrieves the user provided option + */ +std::string GetSpecifiedOption(const std::map<std::string, std::string>& options, const std::string& option); + + +/* + * Parses all the command line options provided by the user and stores in a map. + */ +int ParseOptions(std::map<std::string, std::string>& options, std::map<std::string, std::string>& 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 <cstddef> +#include <cstdint> +#include <vector> +#include <tuple> + +#include <armnn/BackendId.hpp> + +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<int, int, int> colorCode; +}; + +struct PipelineOptions +{ + std::string m_ModelName; + std::string m_ModelFilePath; + std::vector<armnn::BackendId> m_backends; +}; + +template<typename T> +using InferenceResult = std::vector<T>; + +template<typename T> +using InferenceResults = std::vector<InferenceResult<T>>; +} // namespace common
\ No newline at end of file |