| /* | |
| * Copyright (c) 2017-2020 Arm Limited. | |
| * | |
| * SPDX-License-Identifier: MIT | |
| * | |
| * Permission is hereby granted, free of charge, to any person obtaining a copy | |
| * of this software and associated documentation files (the "Software"), to | |
| * deal in the Software without restriction, including without limitation the | |
| * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or | |
| * sell copies of the Software, and to permit persons to whom the Software is | |
| * furnished to do so, subject to the following conditions: | |
| * | |
| * The above copyright notice and this permission notice shall be included in all | |
| * copies or substantial portions of the Software. | |
| * | |
| * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
| * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
| * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |
| * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
| * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
| * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | |
| * SOFTWARE. | |
| */ | |
| #ifndef ARM_COMPUTE_TEST_SIMPLE_TENSOR_H | |
| #define ARM_COMPUTE_TEST_SIMPLE_TENSOR_H | |
| #include "arm_compute/core/TensorShape.h" | |
| #include "arm_compute/core/Types.h" | |
| #include "arm_compute/core/Utils.h" | |
| #include "tests/IAccessor.h" | |
| #include "tests/Utils.h" | |
| #include <algorithm> | |
| #include <array> | |
| #include <cstddef> | |
| #include <cstdint> | |
| #include <functional> | |
| #include <memory> | |
| #include <stdexcept> | |
| #include <utility> | |
| namespace arm_compute | |
| { | |
| namespace test | |
| { | |
| class RawTensor; | |
| /** Simple tensor object that stores elements in a consecutive chunk of memory. | |
| * | |
| * It can be created by either loading an image from a file which also | |
| * initialises the content of the tensor or by explcitly specifying the size. | |
| * The latter leaves the content uninitialised. | |
| * | |
| * Furthermore, the class provides methods to convert the tensor's values into | |
| * different image format. | |
| */ | |
| template <typename T> | |
| class SimpleTensor : public IAccessor | |
| { | |
| public: | |
| /** Create an uninitialised tensor. */ | |
| SimpleTensor() = default; | |
| /** Create an uninitialised tensor of the given @p shape and @p format. | |
| * | |
| * @param[in] shape Shape of the new raw tensor. | |
| * @param[in] format Format of the new raw tensor. | |
| */ | |
| SimpleTensor(TensorShape shape, Format format); | |
| /** Create an uninitialised tensor of the given @p shape and @p data type. | |
| * | |
| * @param[in] shape Shape of the new raw tensor. | |
| * @param[in] data_type Data type of the new raw tensor. | |
| * @param[in] num_channels (Optional) Number of channels (default = 1). | |
| * @param[in] quantization_info (Optional) Quantization info for asymmetric quantization (default = empty). | |
| * @param[in] data_layout (Optional) Data layout of the tensor (default = NCHW). | |
| */ | |
| SimpleTensor(TensorShape shape, DataType data_type, | |
| int num_channels = 1, | |
| QuantizationInfo quantization_info = QuantizationInfo(), | |
| DataLayout data_layout = DataLayout::NCHW); | |
| /** Create a deep copy of the given @p tensor. | |
| * | |
| * @param[in] tensor To be copied tensor. | |
| */ | |
| SimpleTensor(const SimpleTensor &tensor); | |
| /** Create a deep copy of the given @p tensor. | |
| * | |
| * @param[in] tensor To be copied tensor. | |
| * | |
| * @return a copy of the given tensor. | |
| */ | |
| SimpleTensor &operator=(SimpleTensor tensor); | |
| /** Allow instances of this class to be move constructed */ | |
| SimpleTensor(SimpleTensor &&) = default; | |
| /** Default destructor. */ | |
| ~SimpleTensor() = default; | |
| /** Tensor value type */ | |
| using value_type = T; | |
| /** Tensor buffer pointer type */ | |
| using Buffer = std::unique_ptr<value_type[]>; | |
| friend class RawTensor; | |
| /** Return value at @p offset in the buffer. | |
| * | |
| * @param[in] offset Offset within the buffer. | |
| * | |
| * @return value in the buffer. | |
| */ | |
| T &operator[](size_t offset); | |
| /** Return constant value at @p offset in the buffer. | |
| * | |
| * @param[in] offset Offset within the buffer. | |
| * | |
| * @return constant value in the buffer. | |
| */ | |
| const T &operator[](size_t offset) const; | |
| /** Shape of the tensor. | |
| * | |
| * @return the shape of the tensor. | |
| */ | |
| TensorShape shape() const override; | |
| /** Size of each element in the tensor in bytes. | |
| * | |
| * @return the size of each element in the tensor in bytes. | |
| */ | |
| size_t element_size() const override; | |
| /** Total size of the tensor in bytes. | |
| * | |
| * @return the total size of the tensor in bytes. | |
| */ | |
| size_t size() const override; | |
| /** Image format of the tensor. | |
| * | |
| * @return the format of the tensor. | |
| */ | |
| Format format() const override; | |
| /** Data layout of the tensor. | |
| * | |
| * @return the data layout of the tensor. | |
| */ | |
| DataLayout data_layout() const override; | |
| /** Data type of the tensor. | |
| * | |
| * @return the data type of the tensor. | |
| */ | |
| DataType data_type() const override; | |
| /** Number of channels of the tensor. | |
| * | |
| * @return the number of channels of the tensor. | |
| */ | |
| int num_channels() const override; | |
| /** Number of elements of the tensor. | |
| * | |
| * @return the number of elements of the tensor. | |
| */ | |
| int num_elements() const override; | |
| /** Available padding around the tensor. | |
| * | |
| * @return the available padding around the tensor. | |
| */ | |
| PaddingSize padding() const override; | |
| /** Quantization info in case of asymmetric quantized type | |
| * | |
| * @return | |
| */ | |
| QuantizationInfo quantization_info() const override; | |
| /** Constant pointer to the underlying buffer. | |
| * | |
| * @return a constant pointer to the data. | |
| */ | |
| const T *data() const; | |
| /** Pointer to the underlying buffer. | |
| * | |
| * @return a pointer to the data. | |
| */ | |
| T *data(); | |
| /** Read only access to the specified element. | |
| * | |
| * @param[in] coord Coordinates of the desired element. | |
| * | |
| * @return A pointer to the desired element. | |
| */ | |
| const void *operator()(const Coordinates &coord) const override; | |
| /** Access to the specified element. | |
| * | |
| * @param[in] coord Coordinates of the desired element. | |
| * | |
| * @return A pointer to the desired element. | |
| */ | |
| void *operator()(const Coordinates &coord) override; | |
| /** Swaps the content of the provided tensors. | |
| * | |
| * @param[in, out] tensor1 Tensor to be swapped. | |
| * @param[in, out] tensor2 Tensor to be swapped. | |
| */ | |
| template <typename U> | |
| friend void swap(SimpleTensor<U> &tensor1, SimpleTensor<U> &tensor2); | |
| protected: | |
| Buffer _buffer{ nullptr }; | |
| TensorShape _shape{}; | |
| Format _format{ Format::UNKNOWN }; | |
| DataType _data_type{ DataType::UNKNOWN }; | |
| int _num_channels{ 0 }; | |
| QuantizationInfo _quantization_info{}; | |
| DataLayout _data_layout{ DataLayout::UNKNOWN }; | |
| }; | |
| template <typename T1, typename T2> | |
| SimpleTensor<T1> copy_tensor(const SimpleTensor<T2> &tensor) | |
| { | |
| SimpleTensor<T1> st(tensor.shape(), tensor.data_type(), | |
| tensor.num_channels(), | |
| tensor.quantization_info(), | |
| tensor.data_layout()); | |
| for(size_t n = 0; n < size_t(st.num_elements()); n++) | |
| { | |
| st.data()[n] = static_cast<T1>(tensor.data()[n]); | |
| } | |
| return st; | |
| } | |
| template <typename T1, typename T2, typename std::enable_if<std::is_same<T1, T2>::value, int>::type = 0> | |
| SimpleTensor<T1> copy_tensor(const SimpleTensor<half> &tensor) | |
| { | |
| SimpleTensor<T1> st(tensor.shape(), tensor.data_type(), | |
| tensor.num_channels(), | |
| tensor.quantization_info(), | |
| tensor.data_layout()); | |
| memcpy((void *)st.data(), (const void *)tensor.data(), size_t(st.num_elements() * sizeof(T1))); | |
| return st; | |
| } | |
| template < typename T1, typename T2, typename std::enable_if < (std::is_same<T1, half>::value || std::is_same<T2, half>::value), int >::type = 0 > | |
| SimpleTensor<T1> copy_tensor(const SimpleTensor<half> &tensor) | |
| { | |
| SimpleTensor<T1> st(tensor.shape(), tensor.data_type(), | |
| tensor.num_channels(), | |
| tensor.quantization_info(), | |
| tensor.data_layout()); | |
| for(size_t n = 0; n < size_t(st.num_elements()); n++) | |
| { | |
| st.data()[n] = half_float::detail::half_cast<T1, T2>(tensor.data()[n]); | |
| } | |
| return st; | |
| } | |
| template <typename T> | |
| SimpleTensor<T>::SimpleTensor(TensorShape shape, Format format) | |
| : _buffer(nullptr), | |
| _shape(shape), | |
| _format(format), | |
| _quantization_info(), | |
| _data_layout(DataLayout::NCHW) | |
| { | |
| _num_channels = num_channels(); | |
| _buffer = std::make_unique<T[]>(num_elements() * _num_channels); | |
| } | |
| template <typename T> | |
| SimpleTensor<T>::SimpleTensor(TensorShape shape, DataType data_type, int num_channels, QuantizationInfo quantization_info, DataLayout data_layout) | |
| : _buffer(nullptr), | |
| _shape(shape), | |
| _data_type(data_type), | |
| _num_channels(num_channels), | |
| _quantization_info(quantization_info), | |
| _data_layout(data_layout) | |
| { | |
| _buffer = std::make_unique<T[]>(this->_shape.total_size() * _num_channels); | |
| } | |
| template <typename T> | |
| SimpleTensor<T>::SimpleTensor(const SimpleTensor &tensor) | |
| : _buffer(nullptr), | |
| _shape(tensor.shape()), | |
| _format(tensor.format()), | |
| _data_type(tensor.data_type()), | |
| _num_channels(tensor.num_channels()), | |
| _quantization_info(tensor.quantization_info()), | |
| _data_layout(tensor.data_layout()) | |
| { | |
| _buffer = std::make_unique<T[]>(tensor.num_elements() * _num_channels); | |
| std::copy_n(tensor.data(), this->_shape.total_size() * _num_channels, _buffer.get()); | |
| } | |
| template <typename T> | |
| SimpleTensor<T> &SimpleTensor<T>::operator=(SimpleTensor tensor) | |
| { | |
| swap(*this, tensor); | |
| return *this; | |
| } | |
| template <typename T> | |
| T &SimpleTensor<T>::operator[](size_t offset) | |
| { | |
| return _buffer[offset]; | |
| } | |
| template <typename T> | |
| const T &SimpleTensor<T>::operator[](size_t offset) const | |
| { | |
| return _buffer[offset]; | |
| } | |
| template <typename T> | |
| TensorShape SimpleTensor<T>::shape() const | |
| { | |
| return _shape; | |
| } | |
| template <typename T> | |
| size_t SimpleTensor<T>::element_size() const | |
| { | |
| return num_channels() * element_size_from_data_type(data_type()); | |
| } | |
| template <typename T> | |
| QuantizationInfo SimpleTensor<T>::quantization_info() const | |
| { | |
| return _quantization_info; | |
| } | |
| template <typename T> | |
| size_t SimpleTensor<T>::size() const | |
| { | |
| const size_t size = std::accumulate(_shape.cbegin(), _shape.cend(), 1, std::multiplies<size_t>()); | |
| return size * element_size(); | |
| } | |
| template <typename T> | |
| Format SimpleTensor<T>::format() const | |
| { | |
| return _format; | |
| } | |
| template <typename T> | |
| DataLayout SimpleTensor<T>::data_layout() const | |
| { | |
| return _data_layout; | |
| } | |
| template <typename T> | |
| DataType SimpleTensor<T>::data_type() const | |
| { | |
| if(_format != Format::UNKNOWN) | |
| { | |
| return data_type_from_format(_format); | |
| } | |
| else | |
| { | |
| return _data_type; | |
| } | |
| } | |
| template <typename T> | |
| int SimpleTensor<T>::num_channels() const | |
| { | |
| switch(_format) | |
| { | |
| case Format::U8: | |
| case Format::U16: | |
| case Format::S16: | |
| case Format::U32: | |
| case Format::S32: | |
| case Format::F16: | |
| case Format::F32: | |
| return 1; | |
| // Because the U and V channels are subsampled | |
| // these formats appear like having only 2 channels: | |
| case Format::YUYV422: | |
| case Format::UYVY422: | |
| return 2; | |
| case Format::UV88: | |
| return 2; | |
| case Format::RGB888: | |
| return 3; | |
| case Format::RGBA8888: | |
| return 4; | |
| case Format::UNKNOWN: | |
| return _num_channels; | |
| //Doesn't make sense for planar formats: | |
| case Format::NV12: | |
| case Format::NV21: | |
| case Format::IYUV: | |
| case Format::YUV444: | |
| default: | |
| return 0; | |
| } | |
| } | |
| template <typename T> | |
| int SimpleTensor<T>::num_elements() const | |
| { | |
| return _shape.total_size(); | |
| } | |
| template <typename T> | |
| PaddingSize SimpleTensor<T>::padding() const | |
| { | |
| return PaddingSize(0); | |
| } | |
| template <typename T> | |
| const T *SimpleTensor<T>::data() const | |
| { | |
| return _buffer.get(); | |
| } | |
| template <typename T> | |
| T *SimpleTensor<T>::data() | |
| { | |
| return _buffer.get(); | |
| } | |
| template <typename T> | |
| const void *SimpleTensor<T>::operator()(const Coordinates &coord) const | |
| { | |
| return _buffer.get() + coord2index(_shape, coord) * _num_channels; | |
| } | |
| template <typename T> | |
| void *SimpleTensor<T>::operator()(const Coordinates &coord) | |
| { | |
| return _buffer.get() + coord2index(_shape, coord) * _num_channels; | |
| } | |
| template <typename U> | |
| void swap(SimpleTensor<U> &tensor1, SimpleTensor<U> &tensor2) | |
| { | |
| // Use unqualified call to swap to enable ADL. But make std::swap available | |
| // as backup. | |
| using std::swap; | |
| swap(tensor1._shape, tensor2._shape); | |
| swap(tensor1._format, tensor2._format); | |
| swap(tensor1._data_type, tensor2._data_type); | |
| swap(tensor1._num_channels, tensor2._num_channels); | |
| swap(tensor1._quantization_info, tensor2._quantization_info); | |
| swap(tensor1._buffer, tensor2._buffer); | |
| } | |
| } // namespace test | |
| } // namespace arm_compute | |
| #endif /* ARM_COMPUTE_TEST_SIMPLE_TENSOR_H */ |