aboutsummaryrefslogtreecommitdiff
path: root/OutputShapeUtils.cpp
blob: ecec0b9210c83bca0060e119e62a3591d92c696f (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
//
// Copyright © 2017 Arm Ltd. All rights reserved.
// SPDX-License-Identifier: MIT
//

#include "OutputShapeUtils.hpp"

#include <DataLayoutIndexed.hpp>

#include <algorithm>
#include <numeric>
#include <vector>

namespace
{

using namespace armnn;

TensorShape CalculateMaxShape(const TensorShape& inShape0, const TensorShape& inShape1)
{
    // NOTE: The inferred output size will be the maximum size along each dimension
    // of inShape0 and inShape1, starting with the trailing dimensions, and working its way forward.
    //
    // Example: inShape0={4, 1, 2}, inShape1={5, 4, 3, 1} => outputShape={5, 4, 3, 2}

    const unsigned int numInput0Dims = inShape0.GetNumDimensions();
    const unsigned int numInput1Dims = inShape1.GetNumDimensions();

    const unsigned int maxNumDims = std::max(numInput0Dims, numInput1Dims);

    TensorShape outputShape = TensorShape(maxNumDims);
    for (unsigned int reverseIdx = 1u; reverseIdx <= maxNumDims; ++reverseIdx)
    {
        const int input0Idx = numInput0Dims - reverseIdx;
        const int input1Idx = numInput1Dims - reverseIdx;

        const unsigned int input0DimSize = input0Idx >= 0 ? inShape0[input0Idx] : 0u;
        const unsigned int input1DimSize = input1Idx >= 0 ? inShape1[input1Idx] : 0u;

        const unsigned int outputIdx = maxNumDims - reverseIdx;
        outputShape[outputIdx] = std::max(input0DimSize, input1DimSize);
    }

    return outputShape;
}

template<typename ConvolutionDescriptor>
TensorShape InferConvolution2dOutputShapeImpl(const TensorShape& inputShape,
                                              const TensorShape& kernelShape,
                                              const ConvolutionDescriptor& descriptor,
                                              bool isDepthwiseConvolution)
{
    if (inputShape.GetNumDimensions() != 4)
    {
        throw InvalidArgumentException("Input shape must be 4D");
    }

    armnnUtils::DataLayoutIndexed dataLayoutIndex(descriptor.m_DataLayout);

    const unsigned int cIndex = dataLayoutIndex.GetChannelsIndex();
    const unsigned int wIndex = dataLayoutIndex.GetWidthIndex();
    const unsigned int hIndex = dataLayoutIndex.GetHeightIndex();

    const unsigned int wInput = inputShape[wIndex];
    const unsigned int hInput = inputShape[hIndex];

    const unsigned int wKernel  = isDepthwiseConvolution ? kernelShape[2] : kernelShape[wIndex];
    const unsigned int wDilated = wKernel + (descriptor.m_DilationX - 1) * (wKernel - 1);

    const unsigned int wRead   = (wInput + descriptor.m_PadLeft + descriptor.m_PadRight) - wDilated;
    const unsigned int wOutput = 1 + (wRead / descriptor.m_StrideX);

    const unsigned int hKernel  = isDepthwiseConvolution ? kernelShape[3] : kernelShape[hIndex];
    const unsigned int hDilated = hKernel + (descriptor.m_DilationY - 1) * (hKernel - 1);

    const unsigned int hRead   = (hInput + descriptor.m_PadTop + descriptor.m_PadBottom) - hDilated;
    const unsigned int hOutput = 1 + (hRead / descriptor.m_StrideY);

    TensorShape outputShape(4);
    outputShape[0]      = inputShape[0];
    outputShape[cIndex] = kernelShape[0];
    outputShape[wIndex] = wOutput;
    outputShape[hIndex] = hOutput;

    if (isDepthwiseConvolution)
    {
        outputShape[cIndex] *= inputShape[cIndex];
    }

    return outputShape;
}

} // anonymous namespace

namespace armnn_driver
{

using namespace armnn;

TensorShape InferConvolution2dOutputShape(const TensorShape& inputShape,
                                          const TensorShape& kernelShape,
                                          const Convolution2dDescriptor& descriptor)
{
    return InferConvolution2dOutputShapeImpl(inputShape, kernelShape, descriptor, false);
}

TensorShape InferDepthwiseConvolution2dOutputShape(const TensorShape& inputShape,
                                                   const TensorShape& kernelShape,
                                                   const DepthwiseConvolution2dDescriptor& descriptor)
{
    return InferConvolution2dOutputShapeImpl(inputShape, kernelShape, descriptor, true);
}

TensorShape InferMaximumOutputShape(const armnn::TensorShape& input0Shape,
                                    const armnn::TensorShape& input1Shape)
{
    return CalculateMaxShape(input0Shape, input1Shape);
}

TensorShape InferMinimumOutputShape(const armnn::TensorShape& input0Shape,
                                    const armnn::TensorShape& input1Shape)
{
    return CalculateMaxShape(input0Shape, input1Shape);
}

TensorShape InferPadOutputShape(const TensorShape& inputShape,
                                const std::vector<std::pair<unsigned int, unsigned int>>& padList)
{
    const unsigned int numDims = inputShape.GetNumDimensions();

    std::vector<unsigned int> outputDims;
    TensorShape outputShape = TensorShape(numDims);
    for (unsigned int dim = 0; dim < numDims; ++dim)
    {
        unsigned int dimSize = inputShape[dim];
        const std::pair<unsigned int, unsigned int>& dimPadding = padList[dim];
        dimSize += dimPadding.first;
        dimSize += dimPadding.second;
        outputShape[dim] = dimSize;
    }
    return outputShape;
}

TensorShape InferPreluOutputShape(const TensorShape& inputShape, const TensorShape& alphaShape)
{
    return CalculateMaxShape(inputShape, alphaShape);
}

TensorShape InferResizeOutputShape(const TensorShape& inputShape, const ResizeDescriptor& descriptor)
{
    if (inputShape.GetNumDimensions() != 4)
    {
        throw InvalidArgumentException("Input shape for Resize must be 4D");
    }

    armnnUtils::DataLayoutIndexed dataLayoutIndexed(descriptor.m_DataLayout);

    const unsigned int cIndex = dataLayoutIndexed.GetChannelsIndex();
    const unsigned int wIndex = dataLayoutIndexed.GetWidthIndex();
    const unsigned int hIndex = dataLayoutIndexed.GetHeightIndex();

    TensorShape outputShape(4);
    outputShape[0]      = inputShape[0];
    outputShape[cIndex] = inputShape[cIndex];
    outputShape[wIndex] = descriptor.m_TargetWidth;
    outputShape[hIndex] = descriptor.m_TargetHeight;

    return outputShape;
}

TensorShape InferSpaceToDepthOutputShape(const TensorShape& inputShape, const SpaceToDepthDescriptor& descriptor)
{
    TensorShape outputShape(inputShape);

    armnnUtils::DataLayoutIndexed dataLayoutIndexed(descriptor.m_DataLayout);

    const unsigned int cIndex = dataLayoutIndexed.GetChannelsIndex();
    const unsigned int wIndex = dataLayoutIndexed.GetWidthIndex();
    const unsigned int hIndex = dataLayoutIndexed.GetHeightIndex();

    if (descriptor.m_BlockSize == 0)
    {
        throw InvalidArgumentException("Block size must be greater than zero");
    }

    outputShape[cIndex] = inputShape[cIndex] * descriptor.m_BlockSize * descriptor.m_BlockSize;

    outputShape[hIndex] = inputShape[hIndex] / descriptor.m_BlockSize;
    outputShape[wIndex] = inputShape[wIndex] / descriptor.m_BlockSize;

    return outputShape;
}

TensorShape InferSubOutputShape(const TensorShape& input0Shape, const TensorShape& input1Shape)
{
    return CalculateMaxShape(input0Shape, input1Shape);
}

} // namespace armnn_driver