blob: d77f5d74c3e3c61a7f8a8ff68e65d342d8958805 [file] [log] [blame]
Nina Drozdd41b2592018-11-19 13:03:36 +00001//
2// Copyright © 2017 Arm Ltd. All rights reserved.
3// SPDX-License-Identifier: MIT
4//
5
Matteo Martincighe011d202019-11-28 11:35:47 +00006#include <armnnUtils/TensorUtils.hpp>
Matteo Martincigh9a5f9f22019-10-31 11:02:47 +00007
Matteo Martincighe5b8eb92019-11-28 15:45:42 +00008#include <armnn/backends/ITensorHandle.hpp>
Narumol Prangnawaratac2770a2020-04-01 16:51:23 +01009#include <armnn/utility/Assert.hpp>
Matthew Sloyan0663d662020-09-14 11:47:26 +010010#include <armnn/utility/NumericCast.hpp>
Nina Drozdd41b2592018-11-19 13:03:36 +000011
Colm Donelan5b5c2222020-09-09 12:48:16 +010012#include <fmt/format.h>
Narumol Prangnawarat02807852019-09-11 16:43:09 +010013
Matteo Martincigh9a5f9f22019-10-31 11:02:47 +000014using namespace armnn;
15
Nina Drozdd41b2592018-11-19 13:03:36 +000016namespace armnnUtils
17{
18
Matteo Martincigh9a5f9f22019-10-31 11:02:47 +000019TensorShape GetTensorShape(unsigned int numberOfBatches,
Nina Drozdd41b2592018-11-19 13:03:36 +000020 unsigned int numberOfChannels,
21 unsigned int height,
22 unsigned int width,
Matteo Martincigh9a5f9f22019-10-31 11:02:47 +000023 const DataLayout dataLayout)
Nina Drozdd41b2592018-11-19 13:03:36 +000024{
25 switch (dataLayout)
26 {
Matteo Martincigh9a5f9f22019-10-31 11:02:47 +000027 case DataLayout::NCHW:
28 return TensorShape({numberOfBatches, numberOfChannels, height, width});
29 case DataLayout::NHWC:
30 return TensorShape({numberOfBatches, height, width, numberOfChannels});
Nina Drozdd41b2592018-11-19 13:03:36 +000031 default:
Matteo Martincigh9a5f9f22019-10-31 11:02:47 +000032 throw InvalidArgumentException("Unknown data layout ["
Nina Drozdd41b2592018-11-19 13:03:36 +000033 + std::to_string(static_cast<int>(dataLayout)) +
34 "]", CHECK_LOCATION());
35 }
36}
37
Matteo Martincigh9a5f9f22019-10-31 11:02:47 +000038TensorInfo GetTensorInfo(unsigned int numberOfBatches,
Nattapat Chaimanowong649dd952019-01-22 16:10:44 +000039 unsigned int numberOfChannels,
40 unsigned int height,
41 unsigned int width,
Matteo Martincigh9a5f9f22019-10-31 11:02:47 +000042 const DataLayout dataLayout,
43 const DataType dataType)
Nattapat Chaimanowong649dd952019-01-22 16:10:44 +000044{
45 switch (dataLayout)
46 {
Matteo Martincigh9a5f9f22019-10-31 11:02:47 +000047 case DataLayout::NCHW:
48 return TensorInfo({numberOfBatches, numberOfChannels, height, width}, dataType);
49 case DataLayout::NHWC:
50 return TensorInfo({numberOfBatches, height, width, numberOfChannels}, dataType);
Nattapat Chaimanowong649dd952019-01-22 16:10:44 +000051 default:
Matteo Martincigh9a5f9f22019-10-31 11:02:47 +000052 throw InvalidArgumentException("Unknown data layout ["
Nattapat Chaimanowong649dd952019-01-22 16:10:44 +000053 + std::to_string(static_cast<int>(dataLayout)) +
54 "]", CHECK_LOCATION());
55 }
Nina Drozdd41b2592018-11-19 13:03:36 +000056}
57
Tamás Nyíri7b885b32021-10-26 14:47:57 +010058TensorInfo GetTensorInfo(unsigned int numberOfBatches,
59 unsigned int numberOfChannels,
60 unsigned int depth,
61 unsigned int height,
62 unsigned int width,
63 const DataLayout dataLayout,
64 const DataType dataType)
65{
66 switch (dataLayout)
67 {
68 case DataLayout::NDHWC:
69 return TensorInfo({numberOfBatches, depth, height, width, numberOfChannels}, dataType);
70 case DataLayout::NCDHW:
71 return TensorInfo({numberOfBatches, numberOfChannels, depth, height, width}, dataType);
72 default:
73 throw InvalidArgumentException("Unknown data layout ["
74 + std::to_string(static_cast<int>(dataLayout)) +
75 "]", CHECK_LOCATION());
76 }
77}
78
Matteo Martincigh9a5f9f22019-10-31 11:02:47 +000079std::pair<float, float> FindMinMax(ITensorHandle* tensorHandle)
Jim Flynnf92dfce2019-05-02 11:33:25 +010080{
81 auto tensor_data = static_cast<const float *>(tensorHandle->Map(true));
82 auto tensor_size = tensorHandle->GetShape().GetNumElements();
83
84 // Set min/max initially to first value in tensor
85 float min = tensor_data[0];
86 float max = tensor_data[0];
87
88 // Loop over rest of tensor and update min/max if necessary
89 for (unsigned int val = 1; val < tensor_size; val++)
90 {
91 if (tensor_data[val] < min)
92 {
93 min = tensor_data[val];
94 }
95 else if (tensor_data[val] > max)
96 {
97 max = tensor_data[val];
98 }
99 }
100
101 tensorHandle->Unmap();
102
103 return std::make_pair(min, max);
104}
105
Matteo Martincigh9a5f9f22019-10-31 11:02:47 +0000106TensorShape ExpandDims(const TensorShape& tensorShape, int axis)
Narumol Prangnawarat02807852019-09-11 16:43:09 +0100107{
108 unsigned int outputDim = tensorShape.GetNumDimensions() + 1;
109
Matthew Sloyan0663d662020-09-14 11:47:26 +0100110 if (axis < -armnn::numeric_cast<int>(outputDim) || axis > armnn::numeric_cast<int>(tensorShape.GetNumDimensions()))
Narumol Prangnawarat02807852019-09-11 16:43:09 +0100111 {
Colm Donelan5b5c2222020-09-09 12:48:16 +0100112 throw InvalidArgumentException(fmt::format("Invalid expansion axis {} for {}D input tensor. {}",
113 axis,
114 tensorShape.GetNumDimensions(),
115 CHECK_LOCATION().AsString()));
Narumol Prangnawarat02807852019-09-11 16:43:09 +0100116 }
117
118 if (axis < 0)
119 {
Matthew Sloyan0663d662020-09-14 11:47:26 +0100120 axis = armnn::numeric_cast<int>(outputDim) + axis;
Narumol Prangnawarat02807852019-09-11 16:43:09 +0100121 }
122
123 std::vector<unsigned int> outputShape;
Colm Donelan5b5c2222020-09-09 12:48:16 +0100124 outputShape.reserve(tensorShape.GetNumDimensions());
Narumol Prangnawarat02807852019-09-11 16:43:09 +0100125 for (unsigned int i = 0; i < tensorShape.GetNumDimensions(); ++i)
126 {
127 outputShape.push_back(tensorShape[i]);
128 }
129 outputShape.insert(outputShape.begin() + axis, 1);
130
Matteo Martincigh9a5f9f22019-10-31 11:02:47 +0000131 return TensorShape(outputDim, outputShape.data());
Narumol Prangnawarat02807852019-09-11 16:43:09 +0100132}
133
Mike Kelly21fe06f2022-05-16 23:10:42 +0100134std::vector<unsigned int> SqueezeDims(const TensorShape& tensorShape)
135{
136 unsigned int outputDimSize = 0;
137 std::vector<unsigned int> squeezedDims;
138
139 for (unsigned int i = 0; i < tensorShape.GetNumDimensions(); ++i)
140 {
141 if (tensorShape[i] != 1)
142 {
143 squeezedDims.push_back(tensorShape[i]);
144 ++outputDimSize;
145 }
146 }
147 return squeezedDims;
148}
149
Matteo Martincigh9a5f9f22019-10-31 11:02:47 +0000150unsigned int GetNumElementsBetween(const TensorShape& shape,
Narumol Prangnawarat4dc64a62019-09-16 17:00:22 +0100151 const unsigned int firstAxisInclusive,
152 const unsigned int lastAxisExclusive)
153{
Narumol Prangnawaratac2770a2020-04-01 16:51:23 +0100154 ARMNN_ASSERT(firstAxisInclusive <= lastAxisExclusive);
155 ARMNN_ASSERT(lastAxisExclusive <= shape.GetNumDimensions());
Narumol Prangnawarat4dc64a62019-09-16 17:00:22 +0100156 unsigned int count = 1;
157 for (unsigned int i = firstAxisInclusive; i < lastAxisExclusive; i++)
158 {
159 count *= shape[i];
160 }
161 return count;
162}
163
164unsigned int GetUnsignedAxis(const unsigned int inputDimension, const int axis)
165{
Matthew Sloyan0663d662020-09-14 11:47:26 +0100166 ARMNN_ASSERT_MSG(axis < armnn::numeric_cast<int>(inputDimension),
Narumol Prangnawarat4dc64a62019-09-16 17:00:22 +0100167 "Required axis index greater than number of dimensions.");
Matthew Sloyan0663d662020-09-14 11:47:26 +0100168 ARMNN_ASSERT_MSG(axis >= -armnn::numeric_cast<int>(inputDimension),
Narumol Prangnawarat4dc64a62019-09-16 17:00:22 +0100169 "Required axis index lower than negative of the number of dimensions");
170
171 unsigned int uAxis = axis < 0 ?
Matthew Sloyan0663d662020-09-14 11:47:26 +0100172 inputDimension - armnn::numeric_cast<unsigned int>(abs(axis))
173 : armnn::numeric_cast<unsigned int>(axis);
Narumol Prangnawarat4dc64a62019-09-16 17:00:22 +0100174 return uAxis;
175}
176
Aron Virginas-Tarb67f9572019-11-04 15:00:19 +0000177unsigned int GetNumElementsAfter(const armnn::TensorShape& shape, unsigned int axis)
178{
179 unsigned int numDim = shape.GetNumDimensions();
Narumol Prangnawaratac2770a2020-04-01 16:51:23 +0100180 ARMNN_ASSERT(axis <= numDim - 1);
Aron Virginas-Tarb67f9572019-11-04 15:00:19 +0000181 unsigned int count = 1;
Jan Eilers53ef7952021-06-02 12:01:25 +0100182 for (unsigned int i = axis+1; i < numDim; i++)
Aron Virginas-Tarb67f9572019-11-04 15:00:19 +0000183 {
184 count *= shape[i];
185 }
186 return count;
187}
188
189std::pair<unsigned int, std::vector<float>> GetPerAxisParams(const armnn::TensorInfo& info)
190{
191 const std::vector<float>& scales = info.GetQuantizationScales();
192 armnn::Optional<unsigned int> quantizationDim = info.GetQuantizationDim();
Aron Virginas-Tar5edc8812019-11-05 18:00:21 +0000193 if (!info.HasPerAxisQuantization())
Aron Virginas-Tarb67f9572019-11-04 15:00:19 +0000194 {
195 throw armnn::InvalidArgumentException(
196 std::string("Per-axis quantization params not set for tensor of type ") +
197 armnn::GetDataTypeName(info.GetDataType()), CHECK_LOCATION());
198 }
Jan Eilers53ef7952021-06-02 12:01:25 +0100199 unsigned int axisFactor = GetNumElementsAfter(info.GetShape(), quantizationDim.value()) ;
Aron Virginas-Tarb67f9572019-11-04 15:00:19 +0000200
201 return { axisFactor, scales };
202}
203
Matteo Martincigh9a5f9f22019-10-31 11:02:47 +0000204} // namespace armnnUtils