blob: 2690313655c1cbd4433e1981002200a915c5a33a [file] [log] [blame]
telsoa014fcda012018-03-09 14:13:49 +00001//
2// Copyright © 2017 Arm Ltd. All rights reserved.
David Beckecb56cd2018-09-05 12:52:57 +01003// SPDX-License-Identifier: MIT
telsoa014fcda012018-03-09 14:13:49 +00004//
5
6#include "armnn/Exceptions.hpp"
7#include "armnn/LayerSupport.hpp"
8
9#include "backends/CpuTensorHandle.hpp"
10#include "backends/WorkloadFactory.hpp"
11
12LayerTestResult<float,4> SimpleNormalizationTestImpl(armnn::IWorkloadFactory& workloadFactory,
13 armnn::NormalizationAlgorithmChannel normChannel,
14 armnn::NormalizationAlgorithmMethod normMethod)
15{
16 const unsigned int inputHeight = 2;
17 const unsigned int inputWidth = 2;
18 const unsigned int inputChannels = 1;
19 const unsigned int inputNum = 2;
20
21 unsigned int outputHeight = inputHeight;
22 unsigned int outputWidth = inputWidth;
23 unsigned int outputChannels = inputChannels;
24 unsigned int outputNum = inputNum;
25
26 unsigned int inputShape[] = { inputNum, inputChannels, inputHeight, inputWidth };
27 unsigned int outputShape[] = { outputNum, outputChannels, outputHeight, outputWidth };
28
29 auto inputTensorInfo = armnn::TensorInfo(4, inputShape, armnn::DataType::Float32);
30 auto outputTensorInfo = armnn::TensorInfo(4, outputShape, armnn::DataType::Float32);
31
32 LayerTestResult<float,4> ret(outputTensorInfo);
33
34 auto input = MakeTensor<float, 4>(inputTensorInfo, std::vector<float>({
35 // Batch #0
36 1.0f, 2.0f,
37 3.0f, 4.0f,
38 // Batch #1
39 5.0f, 6.0f,
40 7.0f, 8.0f
41 }));
42
43 float alpha = 1.f;
44 float beta = 1.f;
45 float kappa = 1.f;
46 uint32_t normSize = 3;
47
48 std::unique_ptr<armnn::ITensorHandle> inputHandle = workloadFactory.CreateTensorHandle(inputTensorInfo);
49 std::unique_ptr<armnn::ITensorHandle> outputHandle = workloadFactory.CreateTensorHandle(outputTensorInfo);
50
51 armnn::NormalizationQueueDescriptor data;
52 armnn::WorkloadInfo info;
53 AddInputToWorkload(data, info, inputTensorInfo, inputHandle.get());
54 AddOutputToWorkload(data, info, outputTensorInfo, outputHandle.get());
55 data.m_Parameters.m_NormChannelType = normChannel;
56 data.m_Parameters.m_NormMethodType = normMethod;
57 data.m_Parameters.m_NormSize = normSize;
58 data.m_Parameters.m_Alpha = alpha;
59 data.m_Parameters.m_Beta = beta;
60 data.m_Parameters.m_K = kappa;
61
62 armnn::PassthroughCpuTensorHandle refHandle(outputTensorInfo, &ret.outputExpected[0][0][0][0]);
63 armnn::NormalizationQueueDescriptor refData = data;
64 armnn::WorkloadInfo refInfo = info;
65 SetWorkloadOutput(refData, refInfo, 0, outputTensorInfo, &refHandle);
66
67 std::unique_ptr<armnn::IWorkload> workload = workloadFactory.CreateNormalization(data, info);
68
69 inputHandle->Allocate();
70 outputHandle->Allocate();
71
72 CopyDataToITensorHandle(inputHandle.get(), &input[0][0][0][0]);
73
surmeh013537c2c2018-05-18 16:31:43 +010074 workloadFactory.Finalize();
telsoa014fcda012018-03-09 14:13:49 +000075 workload->Execute();
76
77 CopyDataFromITensorHandle(&ret.output[0][0][0][0], outputHandle.get());
78
79 switch (normMethod)
80 {
81 case armnn::NormalizationAlgorithmMethod::LocalBrightness:
82 {
83 switch (normChannel)
84 {
85 case armnn::NormalizationAlgorithmChannel::Within:
86 {
87 // When normalising within channels, the 3x3 kernel covers the entire 2x2 input at every index.
88 // Therefore, all output values should equal the inputs, but divided by:
89 // pow((kappa + (accumulatedScale * alpha)), beta)
telsoa01c577f2c2018-08-31 09:22:23 +010090 // ...where accumulatedScale is the sum of every element squared.
telsoa014fcda012018-03-09 14:13:49 +000091 float divisor[inputNum];
92 for(int i = 0; i < boost::numeric_cast<int>(inputNum); i++)
93 {
94 float accumulatedScale = input[i][0][0][0]*input[i][0][0][0] +
95 input[i][0][0][1]*input[i][0][0][1] +
96 input[i][0][1][0]*input[i][0][1][0] +
97 input[i][0][1][1]*input[i][0][1][1];
98 divisor[i] = powf((kappa + accumulatedScale * alpha), beta);
99 }
100 ret.outputExpected = MakeTensor<float, 4>(outputTensorInfo,
101 std::vector<float>({input[0][0][0][0]/divisor[0],
102 input[0][0][0][1]/divisor[0],
103 input[0][0][1][0]/divisor[0],
104 input[0][0][1][1]/divisor[0],
105 input[1][0][0][0]/divisor[1],
106 input[1][0][0][1]/divisor[1],
107 input[1][0][1][0]/divisor[1],
108 input[1][0][1][1]/divisor[1]}));
109 break;
110 }
111 case armnn::NormalizationAlgorithmChannel::Across:
112 {
113 // When normalising across channels, all output values should equal the inputs, but multiplied by:
114 // pow((kappa + (accumulatedScale * alpha)), -beta)
115 // ...where accumulatedScale is the sum of the inputs for adjacent channels for this element squared
116 // ...where adjacent channels means within half the normSize for the channel
117 // The test data has only one channel, so this is simplified below.
118 std::vector<float> outputVector;
119 for (int n = 0; n < boost::numeric_cast<int>(inputNum); ++n)
120 {
121 for (int h = 0; h < boost::numeric_cast<int>(inputHeight); ++h)
122 {
123 for (int w = 0; w < boost::numeric_cast<int>(inputWidth); ++w)
124 {
125 float accumulatedScale = input[n][0][h][w]*input[n][0][h][w];
126 float scale = powf((kappa + accumulatedScale * alpha), -beta);
127 outputVector.push_back(input[n][0][h][w] * scale);
128 }
129 }
130 }
131 ret.outputExpected = MakeTensor<float, 4>(outputTensorInfo, outputVector);
132 break;
133 }
134 default:
135 {
136 throw armnn::UnimplementedException("Unsupported normalisation channel type, "
137 "only Across and Within are supported");
138 }
139 }
140 break;
141 }
telsoa01c577f2c2018-08-31 09:22:23 +0100142 case armnn::NormalizationAlgorithmMethod::LocalContrast: // NOTE: intentional fallthrough.
telsoa014fcda012018-03-09 14:13:49 +0000143 default:
144 {
145 throw armnn::UnimplementedException("Unsupported normalisation method type, "
146 "only LocalBrightness is supported");
147 }
148 }
149
150 return ret;
151}
152
153LayerTestResult<float,4> CompareNormalizationTestImpl(armnn::IWorkloadFactory& workloadFactory,
154 armnn::IWorkloadFactory& refWorkloadFactory,
155 armnn::NormalizationAlgorithmChannel normChannel,
156 armnn::NormalizationAlgorithmMethod normMethod)
157{
158 constexpr unsigned int inputNum = 5;
159 constexpr unsigned int inputChannels = 3;
160 constexpr unsigned int inputHeight = 32;
161 constexpr unsigned int inputWidth = 24;
162
163 constexpr unsigned int outputNum = inputNum;
164 constexpr unsigned int outputChannels = inputChannels;
165 constexpr unsigned int outputHeight = inputHeight;
166 constexpr unsigned int outputWidth = inputWidth;
167
168 armnn::TensorInfo inputTensorInfo;
169 armnn::TensorInfo outputTensorInfo;
170
171 unsigned int inputShape[] = {inputNum, inputChannels, inputHeight, inputWidth};
172 unsigned int outputShape[] = {outputNum, outputChannels, outputHeight, outputWidth};
173
174 inputTensorInfo = armnn::TensorInfo(4, inputShape, armnn::DataType::Float32);
175 outputTensorInfo = armnn::TensorInfo(4, outputShape, armnn::DataType::Float32);
176
177 LayerTestResult<float,4> ret(outputTensorInfo);
178
179 auto input = MakeRandomTensor<float, 4>(inputTensorInfo, 111234);
180
181 constexpr float alpha = 1.f;
182 constexpr float beta = 1.f;
183 constexpr float kappa = 1.f;
184 constexpr uint32_t normSize = 5;
185
186 std::unique_ptr<armnn::ITensorHandle> inputHandle = workloadFactory.CreateTensorHandle(inputTensorInfo);
187 std::unique_ptr<armnn::ITensorHandle> outputHandle = workloadFactory.CreateTensorHandle(outputTensorInfo);
188
189 armnn::NormalizationQueueDescriptor data;
190 armnn::WorkloadInfo info;
191 AddInputToWorkload(data, info, inputTensorInfo, inputHandle.get());
192 AddOutputToWorkload(data, info, outputTensorInfo, outputHandle.get());
193 data.m_Parameters.m_NormChannelType = normChannel;
194 data.m_Parameters.m_NormMethodType = normMethod;
195 data.m_Parameters.m_NormSize = normSize;
196 data.m_Parameters.m_Alpha = alpha;
197 data.m_Parameters.m_Beta = beta;
198 data.m_Parameters.m_K = kappa;
199
200 std::unique_ptr<armnn::ITensorHandle> outputHandleRef = refWorkloadFactory.CreateTensorHandle(outputTensorInfo);
201 std::unique_ptr<armnn::ITensorHandle> inputHandleRef = refWorkloadFactory.CreateTensorHandle(inputTensorInfo);
202
203 armnn::NormalizationQueueDescriptor refData = data;
204 armnn::WorkloadInfo refInfo = info;
205 SetWorkloadInput(refData, refInfo, 0, inputTensorInfo, inputHandleRef.get());
206 SetWorkloadOutput(refData, refInfo, 0, outputTensorInfo, outputHandleRef.get());
207
208 // Don't execute if Normalization is not supported for the method and channel types, as an exception will be raised.
209 armnn::Compute compute = workloadFactory.GetCompute();
210 const size_t reasonIfUnsupportedMaxLen = 255;
211 char reasonIfUnsupported[reasonIfUnsupportedMaxLen+1];
212 ret.supported = armnn::IsNormalizationSupported(compute, inputTensorInfo, outputTensorInfo, data.m_Parameters,
213 reasonIfUnsupported, reasonIfUnsupportedMaxLen);
214 if (!ret.supported)
215 {
216 return ret;
217 }
218
219 std::unique_ptr<armnn::IWorkload> workload = workloadFactory.CreateNormalization(data, info);
220 std::unique_ptr<armnn::IWorkload> workloadRef = refWorkloadFactory.CreateNormalization(refData, refInfo);
221
222 outputHandleRef->Allocate();
223 inputHandleRef->Allocate();
224
225 inputHandle->Allocate();
226 outputHandle->Allocate();
227
228 CopyDataToITensorHandle(inputHandle.get(), &input[0][0][0][0]);
229 CopyDataToITensorHandle(inputHandleRef.get(), &input[0][0][0][0]);
230
surmeh013537c2c2018-05-18 16:31:43 +0100231 workloadFactory.Finalize();
telsoa014fcda012018-03-09 14:13:49 +0000232 workload->Execute();
surmeh013537c2c2018-05-18 16:31:43 +0100233 refWorkloadFactory.Finalize();
telsoa014fcda012018-03-09 14:13:49 +0000234 workloadRef->Execute();
235
236 CopyDataFromITensorHandle(&ret.output[0][0][0][0], outputHandle.get());
237 CopyDataFromITensorHandle(&ret.outputExpected[0][0][0][0], outputHandleRef.get());
238
239 return ret;
240}
241