blob: e3812a367412d407531b6b18045db5206fc0e0e6 [file] [log] [blame]
Aron Virginas-Tarf03fcf02019-07-09 17:44:24 +01001//
2// Copyright © 2017 Arm Ltd. All rights reserved.
3// SPDX-License-Identifier: MIT
4//
5
6#include "OutputShapeUtils.hpp"
7
Aron Virginas-Tar2b173122019-07-15 14:29:09 +01008#include <DataLayoutIndexed.hpp>
9
Aron Virginas-Tarf03fcf02019-07-09 17:44:24 +010010#include <algorithm>
Sadik Armagan310d8ff2019-07-11 10:53:38 +010011#include <vector>
Aron Virginas-Tarf03fcf02019-07-09 17:44:24 +010012
Sadik Armagan5e9521c2019-07-12 13:55:57 +010013namespace
14{
15
16using namespace armnn;
17
18TensorShape CalculateMaxShape(const TensorShape& inShape0, const TensorShape& inShape1)
19{
20 // NOTE: The inferred output size will be the maximum size along each dimension
21 // of inShape0 and inShape1, starting with the trailing dimensions, and working its way forward.
22 //
23 // Example: inShape0={4, 1, 2}, inShape1={5, 4, 3, 1} => outputShape={5, 4, 3, 2}
24
25 const unsigned int numInput0Dims = inShape0.GetNumDimensions();
26 const unsigned int numInput1Dims = inShape1.GetNumDimensions();
27
28 const unsigned int maxNumDims = std::max(numInput0Dims, numInput1Dims);
29
30 TensorShape outputShape = TensorShape(maxNumDims);
31 for (unsigned int reverseIdx = 1u; reverseIdx <= maxNumDims; ++reverseIdx)
32 {
33 const int input0Idx = numInput0Dims - reverseIdx;
34 const int input1Idx = numInput1Dims - reverseIdx;
35
36 const unsigned int input0DimSize = input0Idx >= 0 ? inShape0[input0Idx] : 0u;
37 const unsigned int input1DimSize = input1Idx >= 0 ? inShape1[input1Idx] : 0u;
38
39 const unsigned int outputIdx = maxNumDims - reverseIdx;
40 outputShape[outputIdx] = std::max(input0DimSize, input1DimSize);
41 }
42
43 return outputShape;
44}
45
Aron Virginas-Tar9fd37392019-07-15 18:04:32 +010046template<typename ConvolutionDescriptor>
47TensorShape InferConvolution2dOutputShapeImpl(const TensorShape& inputShape,
48 const TensorShape& kernelShape,
49 const ConvolutionDescriptor& descriptor,
50 bool isDepthwiseConvolution)
51{
52 if (inputShape.GetNumDimensions() != 4)
53 {
54 throw InvalidArgumentException("Input shape must be 4D");
55 }
Sadik Armagan5e9521c2019-07-12 13:55:57 +010056
Aron Virginas-Tar9fd37392019-07-15 18:04:32 +010057 armnnUtils::DataLayoutIndexed dataLayoutIndex(descriptor.m_DataLayout);
58
59 const unsigned int cIndex = dataLayoutIndex.GetChannelsIndex();
60 const unsigned int wIndex = dataLayoutIndex.GetWidthIndex();
61 const unsigned int hIndex = dataLayoutIndex.GetHeightIndex();
62
63 const unsigned int wInput = inputShape[wIndex];
64 const unsigned int hInput = inputShape[hIndex];
65
66 const unsigned int wKernel = isDepthwiseConvolution ? kernelShape[2] : kernelShape[wIndex];
67 const unsigned int wDilated = wKernel + (descriptor.m_DilationX - 1) * (wKernel - 1);
68
69 const unsigned int wRead = (wInput + descriptor.m_PadLeft + descriptor.m_PadRight) - wDilated;
70 const unsigned int wOutput = 1 + (wRead / descriptor.m_StrideX);
71
72 const unsigned int hKernel = isDepthwiseConvolution ? kernelShape[3] : kernelShape[hIndex];
73 const unsigned int hDilated = hKernel + (descriptor.m_DilationY - 1) * (hKernel - 1);
74
75 const unsigned int hRead = (hInput + descriptor.m_PadTop + descriptor.m_PadBottom) - hDilated;
76 const unsigned int hOutput = 1 + (hRead / descriptor.m_StrideY);
77
78 TensorShape outputShape(4);
79 outputShape[0] = inputShape[0];
80 outputShape[cIndex] = kernelShape[0];
81 outputShape[wIndex] = wOutput;
82 outputShape[hIndex] = hOutput;
83
84 if (isDepthwiseConvolution)
85 {
86 outputShape[cIndex] *= inputShape[cIndex];
87 }
88
89 return outputShape;
90}
91
92} // anonymous namespace
Sadik Armagan5e9521c2019-07-12 13:55:57 +010093
Aron Virginas-Tarf03fcf02019-07-09 17:44:24 +010094namespace armnn_driver
95{
96
97using namespace armnn;
98
Aron Virginas-Tar366e0a62019-07-10 13:01:41 +010099bool IsDynamicOutput(const TensorInfo& outputInfo)
100{
101 return outputInfo.GetNumElements() == 0u;
102}
103
Aron Virginas-Tar2b173122019-07-15 14:29:09 +0100104TensorShape InferConvolution2dOutputShape(const TensorShape& inputShape,
105 const TensorShape& kernelShape,
106 const Convolution2dDescriptor& descriptor)
107{
Aron Virginas-Tar9fd37392019-07-15 18:04:32 +0100108 return InferConvolution2dOutputShapeImpl(inputShape, kernelShape, descriptor, false);
109}
Aron Virginas-Tar2b173122019-07-15 14:29:09 +0100110
Aron Virginas-Tar9fd37392019-07-15 18:04:32 +0100111TensorShape InferDepthwiseConvolution2dOutputShape(const TensorShape& inputShape,
112 const TensorShape& kernelShape,
113 const DepthwiseConvolution2dDescriptor& descriptor)
114{
115 return InferConvolution2dOutputShapeImpl(inputShape, kernelShape, descriptor, true);
Aron Virginas-Tar2b173122019-07-15 14:29:09 +0100116}
117
Narumol Prangnawarat95b1ef62019-07-15 12:02:20 +0100118TensorShape InferMaximumOutputShape(const armnn::TensorShape& input0Shape,
119 const armnn::TensorShape& input1Shape)
120{
121 return CalculateMaxShape(input0Shape, input1Shape);
122}
123
Ellen Norris-Thompson1cb29aa2019-07-11 17:27:37 +0100124TensorShape InferMinimumOutputShape(const armnn::TensorShape& input0Shape,
125 const armnn::TensorShape& input1Shape)
126{
127 return CalculateMaxShape(input0Shape, input1Shape);
128}
129
Sadik Armagan310d8ff2019-07-11 10:53:38 +0100130TensorShape InferPadOutputShape(const TensorShape& inputShape,
131 const std::vector<std::pair<unsigned int, unsigned int>>& padList)
132{
133 const unsigned int numDims = inputShape.GetNumDimensions();
134
135 std::vector<unsigned int> outputDims;
136 TensorShape outputShape = TensorShape(numDims);
137 for (unsigned int dim = 0; dim < numDims; ++dim)
138 {
139 unsigned int dimSize = inputShape[dim];
140 const std::pair<unsigned int, unsigned int>& dimPadding = padList[dim];
141 dimSize += dimPadding.first;
142 dimSize += dimPadding.second;
143 outputShape[dim] = dimSize;
144 }
145 return outputShape;
146}
147
Aron Virginas-Tarf03fcf02019-07-09 17:44:24 +0100148TensorShape InferPreluOutputShape(const TensorShape& inputShape, const TensorShape& alphaShape)
149{
Sadik Armagan5e9521c2019-07-12 13:55:57 +0100150 return CalculateMaxShape(inputShape, alphaShape);
151}
Aron Virginas-Tarf03fcf02019-07-09 17:44:24 +0100152
Aron Virginas-Tarbe5d3562019-07-16 11:32:29 +0100153TensorShape InferResizeOutputShape(const TensorShape& inputShape, const ResizeDescriptor& descriptor)
154{
155 if (inputShape.GetNumDimensions() != 4)
156 {
157 throw InvalidArgumentException("Input shape for Resize must be 4D");
158 }
159
160 armnnUtils::DataLayoutIndexed dataLayoutIndexed(descriptor.m_DataLayout);
161
162 const unsigned int cIndex = dataLayoutIndexed.GetChannelsIndex();
163 const unsigned int wIndex = dataLayoutIndexed.GetWidthIndex();
164 const unsigned int hIndex = dataLayoutIndexed.GetHeightIndex();
165
166 TensorShape outputShape(4);
167 outputShape[0] = inputShape[0];
168 outputShape[cIndex] = inputShape[cIndex];
169 outputShape[wIndex] = descriptor.m_TargetWidth;
170 outputShape[hIndex] = descriptor.m_TargetHeight;
171
172 return outputShape;
173}
174
Sadik Armagan5e9521c2019-07-12 13:55:57 +0100175TensorShape InferSubOutputShape(const TensorShape& input0Shape, const TensorShape& input1Shape)
176{
177 return CalculateMaxShape(input0Shape, input1Shape);
Aron Virginas-Tarf03fcf02019-07-09 17:44:24 +0100178}
179
180} // namespace armnn_driver