blob: 6c936ee7fa4b53205fddd5aab8952522296657ed [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
Sadik Armagan310d8ff2019-07-11 10:53:38 +0100124TensorShape InferPadOutputShape(const TensorShape& inputShape,
125 const std::vector<std::pair<unsigned int, unsigned int>>& padList)
126{
127 const unsigned int numDims = inputShape.GetNumDimensions();
128
129 std::vector<unsigned int> outputDims;
130 TensorShape outputShape = TensorShape(numDims);
131 for (unsigned int dim = 0; dim < numDims; ++dim)
132 {
133 unsigned int dimSize = inputShape[dim];
134 const std::pair<unsigned int, unsigned int>& dimPadding = padList[dim];
135 dimSize += dimPadding.first;
136 dimSize += dimPadding.second;
137 outputShape[dim] = dimSize;
138 }
139 return outputShape;
140}
141
Aron Virginas-Tarf03fcf02019-07-09 17:44:24 +0100142TensorShape InferPreluOutputShape(const TensorShape& inputShape, const TensorShape& alphaShape)
143{
Sadik Armagan5e9521c2019-07-12 13:55:57 +0100144 return CalculateMaxShape(inputShape, alphaShape);
145}
Aron Virginas-Tarf03fcf02019-07-09 17:44:24 +0100146
Aron Virginas-Tarbe5d3562019-07-16 11:32:29 +0100147TensorShape InferResizeOutputShape(const TensorShape& inputShape, const ResizeDescriptor& descriptor)
148{
149 if (inputShape.GetNumDimensions() != 4)
150 {
151 throw InvalidArgumentException("Input shape for Resize must be 4D");
152 }
153
154 armnnUtils::DataLayoutIndexed dataLayoutIndexed(descriptor.m_DataLayout);
155
156 const unsigned int cIndex = dataLayoutIndexed.GetChannelsIndex();
157 const unsigned int wIndex = dataLayoutIndexed.GetWidthIndex();
158 const unsigned int hIndex = dataLayoutIndexed.GetHeightIndex();
159
160 TensorShape outputShape(4);
161 outputShape[0] = inputShape[0];
162 outputShape[cIndex] = inputShape[cIndex];
163 outputShape[wIndex] = descriptor.m_TargetWidth;
164 outputShape[hIndex] = descriptor.m_TargetHeight;
165
166 return outputShape;
167}
168
Sadik Armagan5e9521c2019-07-12 13:55:57 +0100169TensorShape InferSubOutputShape(const TensorShape& input0Shape, const TensorShape& input1Shape)
170{
171 return CalculateMaxShape(input0Shape, input1Shape);
Aron Virginas-Tarf03fcf02019-07-09 17:44:24 +0100172}
173
174} // namespace armnn_driver