blob: 38a0053a56443dc1536f36fd8695aa744bd5f917 [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
Aron Virginas-Tar5caf9072018-11-14 18:35:18 +00006#include "WorkloadTestUtils.hpp"
7
David Beckac42efd2018-09-26 17:41:13 +01008#include <armnn/Exceptions.hpp>
9#include <armnn/LayerSupport.hpp>
Aron Virginas-Tar5caf9072018-11-14 18:35:18 +000010#include <armnn/Types.hpp>
telsoa014fcda012018-03-09 14:13:49 +000011
Aron Virginas-Tarc9cc8042018-11-01 16:15:57 +000012#include <backendsCommon/CpuTensorHandle.hpp>
Aron Virginas-Tar5caf9072018-11-14 18:35:18 +000013#include <backendsCommon/IBackendInternal.hpp>
Aron Virginas-Tarc9cc8042018-11-01 16:15:57 +000014#include <backendsCommon/WorkloadFactory.hpp>
telsoa014fcda012018-03-09 14:13:49 +000015
Aron Virginas-Tar5caf9072018-11-14 18:35:18 +000016LayerTestResult<float,4> SimpleNormalizationTestImpl(
17 armnn::IWorkloadFactory& workloadFactory,
18 const armnn::IBackendInternal::IMemoryManagerSharedPtr& memoryManager,
19 armnn::NormalizationAlgorithmChannel normChannel,
20 armnn::NormalizationAlgorithmMethod normMethod)
telsoa014fcda012018-03-09 14:13:49 +000021{
22 const unsigned int inputHeight = 2;
23 const unsigned int inputWidth = 2;
24 const unsigned int inputChannels = 1;
25 const unsigned int inputNum = 2;
26
27 unsigned int outputHeight = inputHeight;
28 unsigned int outputWidth = inputWidth;
29 unsigned int outputChannels = inputChannels;
30 unsigned int outputNum = inputNum;
31
32 unsigned int inputShape[] = { inputNum, inputChannels, inputHeight, inputWidth };
33 unsigned int outputShape[] = { outputNum, outputChannels, outputHeight, outputWidth };
34
35 auto inputTensorInfo = armnn::TensorInfo(4, inputShape, armnn::DataType::Float32);
36 auto outputTensorInfo = armnn::TensorInfo(4, outputShape, armnn::DataType::Float32);
37
38 LayerTestResult<float,4> ret(outputTensorInfo);
39
40 auto input = MakeTensor<float, 4>(inputTensorInfo, std::vector<float>({
41 // Batch #0
42 1.0f, 2.0f,
43 3.0f, 4.0f,
44 // Batch #1
45 5.0f, 6.0f,
46 7.0f, 8.0f
47 }));
48
49 float alpha = 1.f;
50 float beta = 1.f;
51 float kappa = 1.f;
52 uint32_t normSize = 3;
53
54 std::unique_ptr<armnn::ITensorHandle> inputHandle = workloadFactory.CreateTensorHandle(inputTensorInfo);
55 std::unique_ptr<armnn::ITensorHandle> outputHandle = workloadFactory.CreateTensorHandle(outputTensorInfo);
56
57 armnn::NormalizationQueueDescriptor data;
58 armnn::WorkloadInfo info;
59 AddInputToWorkload(data, info, inputTensorInfo, inputHandle.get());
60 AddOutputToWorkload(data, info, outputTensorInfo, outputHandle.get());
61 data.m_Parameters.m_NormChannelType = normChannel;
62 data.m_Parameters.m_NormMethodType = normMethod;
63 data.m_Parameters.m_NormSize = normSize;
64 data.m_Parameters.m_Alpha = alpha;
65 data.m_Parameters.m_Beta = beta;
66 data.m_Parameters.m_K = kappa;
narpra0155a97bc2018-10-02 14:35:53 +010067 data.m_Parameters.m_DataLayout = armnn::DataLayout::NCHW;
telsoa014fcda012018-03-09 14:13:49 +000068
69 armnn::PassthroughCpuTensorHandle refHandle(outputTensorInfo, &ret.outputExpected[0][0][0][0]);
70 armnn::NormalizationQueueDescriptor refData = data;
71 armnn::WorkloadInfo refInfo = info;
72 SetWorkloadOutput(refData, refInfo, 0, outputTensorInfo, &refHandle);
73
74 std::unique_ptr<armnn::IWorkload> workload = workloadFactory.CreateNormalization(data, info);
75
76 inputHandle->Allocate();
77 outputHandle->Allocate();
78
79 CopyDataToITensorHandle(inputHandle.get(), &input[0][0][0][0]);
80
Aron Virginas-Tar5caf9072018-11-14 18:35:18 +000081 ExecuteWorkload(*workload, memoryManager);
telsoa014fcda012018-03-09 14:13:49 +000082
83 CopyDataFromITensorHandle(&ret.output[0][0][0][0], outputHandle.get());
84
85 switch (normMethod)
86 {
87 case armnn::NormalizationAlgorithmMethod::LocalBrightness:
88 {
89 switch (normChannel)
90 {
91 case armnn::NormalizationAlgorithmChannel::Within:
92 {
93 // When normalising within channels, the 3x3 kernel covers the entire 2x2 input at every index.
94 // Therefore, all output values should equal the inputs, but divided by:
95 // pow((kappa + (accumulatedScale * alpha)), beta)
telsoa01c577f2c2018-08-31 09:22:23 +010096 // ...where accumulatedScale is the sum of every element squared.
telsoa014fcda012018-03-09 14:13:49 +000097 float divisor[inputNum];
98 for(int i = 0; i < boost::numeric_cast<int>(inputNum); i++)
99 {
100 float accumulatedScale = input[i][0][0][0]*input[i][0][0][0] +
101 input[i][0][0][1]*input[i][0][0][1] +
102 input[i][0][1][0]*input[i][0][1][0] +
103 input[i][0][1][1]*input[i][0][1][1];
104 divisor[i] = powf((kappa + accumulatedScale * alpha), beta);
105 }
106 ret.outputExpected = MakeTensor<float, 4>(outputTensorInfo,
107 std::vector<float>({input[0][0][0][0]/divisor[0],
108 input[0][0][0][1]/divisor[0],
109 input[0][0][1][0]/divisor[0],
110 input[0][0][1][1]/divisor[0],
111 input[1][0][0][0]/divisor[1],
112 input[1][0][0][1]/divisor[1],
113 input[1][0][1][0]/divisor[1],
114 input[1][0][1][1]/divisor[1]}));
115 break;
116 }
117 case armnn::NormalizationAlgorithmChannel::Across:
118 {
119 // When normalising across channels, all output values should equal the inputs, but multiplied by:
120 // pow((kappa + (accumulatedScale * alpha)), -beta)
121 // ...where accumulatedScale is the sum of the inputs for adjacent channels for this element squared
122 // ...where adjacent channels means within half the normSize for the channel
123 // The test data has only one channel, so this is simplified below.
124 std::vector<float> outputVector;
125 for (int n = 0; n < boost::numeric_cast<int>(inputNum); ++n)
126 {
127 for (int h = 0; h < boost::numeric_cast<int>(inputHeight); ++h)
128 {
129 for (int w = 0; w < boost::numeric_cast<int>(inputWidth); ++w)
130 {
131 float accumulatedScale = input[n][0][h][w]*input[n][0][h][w];
132 float scale = powf((kappa + accumulatedScale * alpha), -beta);
133 outputVector.push_back(input[n][0][h][w] * scale);
134 }
135 }
136 }
137 ret.outputExpected = MakeTensor<float, 4>(outputTensorInfo, outputVector);
138 break;
139 }
140 default:
141 {
142 throw armnn::UnimplementedException("Unsupported normalisation channel type, "
143 "only Across and Within are supported");
144 }
145 }
146 break;
147 }
telsoa01c577f2c2018-08-31 09:22:23 +0100148 case armnn::NormalizationAlgorithmMethod::LocalContrast: // NOTE: intentional fallthrough.
telsoa014fcda012018-03-09 14:13:49 +0000149 default:
150 {
151 throw armnn::UnimplementedException("Unsupported normalisation method type, "
152 "only LocalBrightness is supported");
153 }
154 }
155
156 return ret;
157}
158
Aron Virginas-Tar5caf9072018-11-14 18:35:18 +0000159LayerTestResult<float,4> SimpleNormalizationNhwcTestImpl(
160 armnn::IWorkloadFactory& workloadFactory,
161 const armnn::IBackendInternal::IMemoryManagerSharedPtr& memoryManager,
162 armnn::NormalizationAlgorithmChannel normChannel,
163 armnn::NormalizationAlgorithmMethod normMethod)
narpra0155a97bc2018-10-02 14:35:53 +0100164{
165 const unsigned int inputHeight = 2;
166 const unsigned int inputWidth = 2;
167 const unsigned int inputChannels = 1;
168 const unsigned int inputNum = 2;
169
170 unsigned int outputHeight = inputHeight;
171 unsigned int outputWidth = inputWidth;
172 unsigned int outputChannels = inputChannels;
173 unsigned int outputNum = inputNum;
174
175 unsigned int inputShape[] = { inputNum, inputHeight, inputWidth, inputChannels };
176 unsigned int outputShape[] = { outputNum, outputHeight, outputWidth, outputChannels };
177
178 auto inputTensorInfo = armnn::TensorInfo(4, inputShape, armnn::DataType::Float32);
179 auto outputTensorInfo = armnn::TensorInfo(4, outputShape, armnn::DataType::Float32);
180
181 LayerTestResult<float,4> ret(outputTensorInfo);
182
183 auto input = MakeTensor<float, 4>(inputTensorInfo, std::vector<float>({
184 // Batch #0
185 1.0f, 2.0f,
186 3.0f, 4.0f,
187 // Batch #1
188 5.0f, 6.0f,
189 7.0f, 8.0f
190 }));
191
192 float alpha = 1.f;
193 float beta = 1.f;
194 float kappa = 1.f;
195 uint32_t normSize = 3;
196
197 std::unique_ptr<armnn::ITensorHandle> inputHandle = workloadFactory.CreateTensorHandle(inputTensorInfo);
198 std::unique_ptr<armnn::ITensorHandle> outputHandle = workloadFactory.CreateTensorHandle(outputTensorInfo);
199
200 armnn::NormalizationQueueDescriptor data;
201 armnn::WorkloadInfo info;
202 AddInputToWorkload(data, info, inputTensorInfo, inputHandle.get());
203 AddOutputToWorkload(data, info, outputTensorInfo, outputHandle.get());
204 data.m_Parameters.m_NormChannelType = normChannel;
205 data.m_Parameters.m_NormMethodType = normMethod;
206 data.m_Parameters.m_NormSize = normSize;
207 data.m_Parameters.m_Alpha = alpha;
208 data.m_Parameters.m_Beta = beta;
209 data.m_Parameters.m_K = kappa;
210 data.m_Parameters.m_DataLayout = armnn::DataLayout::NHWC;
211
212 armnn::PassthroughCpuTensorHandle refHandle(outputTensorInfo, &ret.outputExpected[0][0][0][0]);
213 armnn::NormalizationQueueDescriptor refData = data;
214 armnn::WorkloadInfo refInfo = info;
215 SetWorkloadOutput(refData, refInfo, 0, outputTensorInfo, &refHandle);
216
217 std::unique_ptr<armnn::IWorkload> workload = workloadFactory.CreateNormalization(data, info);
218
219 inputHandle->Allocate();
220 outputHandle->Allocate();
221
222 CopyDataToITensorHandle(inputHandle.get(), &input[0][0][0][0]);
223
Aron Virginas-Tar5caf9072018-11-14 18:35:18 +0000224 ExecuteWorkload(*workload, memoryManager);
narpra0155a97bc2018-10-02 14:35:53 +0100225
226 CopyDataFromITensorHandle(&ret.output[0][0][0][0], outputHandle.get());
227
228 switch (normMethod)
229 {
230 case armnn::NormalizationAlgorithmMethod::LocalBrightness:
231 {
232 switch (normChannel)
233 {
234 case armnn::NormalizationAlgorithmChannel::Across:
235 {
236 std::vector<float> expectedOutput{ 0.5f, 0.400000006f, 0.300000012f, 0.235294119f,
237 0.192307696f, 0.16216217f, 0.140000001f, 0.123076923f };
238 ret.outputExpected = MakeTensor<float, 4>(outputTensorInfo, expectedOutput);
239 break;
240 }
241 default:
242 {
243 throw armnn::UnimplementedException("Unsupported normalisation channel type, "
244 "Only Cross-map is supported for NHWC layout");
245 }
246 }
247 break;
248 }
249 case armnn::NormalizationAlgorithmMethod::LocalContrast: // NOTE: intentional fallthrough.
250 default:
251 {
252 throw armnn::UnimplementedException("Unsupported normalisation method type, "
253 "only LocalBrightness is supported");
254 }
255 }
256
257 return ret;
258}
259
Aron Virginas-Tar5caf9072018-11-14 18:35:18 +0000260LayerTestResult<float,4> CompareNormalizationTestImpl(
261 armnn::IWorkloadFactory& workloadFactory,
262 const armnn::IBackendInternal::IMemoryManagerSharedPtr& memoryManager,
263 armnn::IWorkloadFactory& refWorkloadFactory,
264 armnn::NormalizationAlgorithmChannel normChannel,
265 armnn::NormalizationAlgorithmMethod normMethod)
telsoa014fcda012018-03-09 14:13:49 +0000266{
267 constexpr unsigned int inputNum = 5;
268 constexpr unsigned int inputChannels = 3;
269 constexpr unsigned int inputHeight = 32;
270 constexpr unsigned int inputWidth = 24;
271
272 constexpr unsigned int outputNum = inputNum;
273 constexpr unsigned int outputChannels = inputChannels;
274 constexpr unsigned int outputHeight = inputHeight;
275 constexpr unsigned int outputWidth = inputWidth;
276
277 armnn::TensorInfo inputTensorInfo;
278 armnn::TensorInfo outputTensorInfo;
279
280 unsigned int inputShape[] = {inputNum, inputChannels, inputHeight, inputWidth};
281 unsigned int outputShape[] = {outputNum, outputChannels, outputHeight, outputWidth};
282
283 inputTensorInfo = armnn::TensorInfo(4, inputShape, armnn::DataType::Float32);
284 outputTensorInfo = armnn::TensorInfo(4, outputShape, armnn::DataType::Float32);
285
286 LayerTestResult<float,4> ret(outputTensorInfo);
287
288 auto input = MakeRandomTensor<float, 4>(inputTensorInfo, 111234);
289
290 constexpr float alpha = 1.f;
291 constexpr float beta = 1.f;
292 constexpr float kappa = 1.f;
293 constexpr uint32_t normSize = 5;
294
295 std::unique_ptr<armnn::ITensorHandle> inputHandle = workloadFactory.CreateTensorHandle(inputTensorInfo);
296 std::unique_ptr<armnn::ITensorHandle> outputHandle = workloadFactory.CreateTensorHandle(outputTensorInfo);
297
298 armnn::NormalizationQueueDescriptor data;
299 armnn::WorkloadInfo info;
300 AddInputToWorkload(data, info, inputTensorInfo, inputHandle.get());
301 AddOutputToWorkload(data, info, outputTensorInfo, outputHandle.get());
302 data.m_Parameters.m_NormChannelType = normChannel;
303 data.m_Parameters.m_NormMethodType = normMethod;
304 data.m_Parameters.m_NormSize = normSize;
305 data.m_Parameters.m_Alpha = alpha;
306 data.m_Parameters.m_Beta = beta;
307 data.m_Parameters.m_K = kappa;
308
309 std::unique_ptr<armnn::ITensorHandle> outputHandleRef = refWorkloadFactory.CreateTensorHandle(outputTensorInfo);
310 std::unique_ptr<armnn::ITensorHandle> inputHandleRef = refWorkloadFactory.CreateTensorHandle(inputTensorInfo);
311
312 armnn::NormalizationQueueDescriptor refData = data;
313 armnn::WorkloadInfo refInfo = info;
314 SetWorkloadInput(refData, refInfo, 0, inputTensorInfo, inputHandleRef.get());
315 SetWorkloadOutput(refData, refInfo, 0, outputTensorInfo, outputHandleRef.get());
316
317 // Don't execute if Normalization is not supported for the method and channel types, as an exception will be raised.
David Beck79141b92018-10-23 16:09:36 +0100318 armnn::BackendId backend = workloadFactory.GetBackendId();
telsoa014fcda012018-03-09 14:13:49 +0000319 const size_t reasonIfUnsupportedMaxLen = 255;
320 char reasonIfUnsupported[reasonIfUnsupportedMaxLen+1];
David Beck79141b92018-10-23 16:09:36 +0100321 ret.supported = armnn::IsNormalizationSupported(backend, inputTensorInfo, outputTensorInfo, data.m_Parameters,
telsoa014fcda012018-03-09 14:13:49 +0000322 reasonIfUnsupported, reasonIfUnsupportedMaxLen);
323 if (!ret.supported)
324 {
325 return ret;
326 }
327
328 std::unique_ptr<armnn::IWorkload> workload = workloadFactory.CreateNormalization(data, info);
329 std::unique_ptr<armnn::IWorkload> workloadRef = refWorkloadFactory.CreateNormalization(refData, refInfo);
330
331 outputHandleRef->Allocate();
332 inputHandleRef->Allocate();
333
334 inputHandle->Allocate();
335 outputHandle->Allocate();
336
337 CopyDataToITensorHandle(inputHandle.get(), &input[0][0][0][0]);
338 CopyDataToITensorHandle(inputHandleRef.get(), &input[0][0][0][0]);
339
Aron Virginas-Tar5caf9072018-11-14 18:35:18 +0000340 ExecuteWorkload(*workload, memoryManager);
Aron Virginas-Tar60578952018-10-31 11:04:01 +0000341
telsoa014fcda012018-03-09 14:13:49 +0000342 workloadRef->Execute();
343
344 CopyDataFromITensorHandle(&ret.output[0][0][0][0], outputHandle.get());
345 CopyDataFromITensorHandle(&ret.outputExpected[0][0][0][0], outputHandleRef.get());
346
347 return ret;
348}
349