blob: f65960fc86c4e65fa9df7685c0d0dc75c93dccba [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-Tar00d306e2019-08-28 18:08:46 +01006#include "NormalizationTestImpl.hpp"
Aron Virginas-Tar5caf9072018-11-14 18:35:18 +00007
David Beckac42efd2018-09-26 17:41:13 +01008#include <armnn/Exceptions.hpp>
9#include <armnn/LayerSupport.hpp>
telsoa014fcda012018-03-09 14:13:49 +000010
Aron Virginas-Tarc9cc8042018-11-01 16:15:57 +000011#include <backendsCommon/CpuTensorHandle.hpp>
Aron Virginas-Tar00d306e2019-08-28 18:08:46 +010012
13#include <backendsCommon/test/TensorCopyUtils.hpp>
14#include <backendsCommon/test/WorkloadTestUtils.hpp>
15
16#include <test/TensorHelpers.hpp>
17
18namespace
19{
telsoa014fcda012018-03-09 14:13:49 +000020
Aron Virginas-Tar5caf9072018-11-14 18:35:18 +000021LayerTestResult<float,4> SimpleNormalizationTestImpl(
22 armnn::IWorkloadFactory& workloadFactory,
23 const armnn::IBackendInternal::IMemoryManagerSharedPtr& memoryManager,
24 armnn::NormalizationAlgorithmChannel normChannel,
25 armnn::NormalizationAlgorithmMethod normMethod)
telsoa014fcda012018-03-09 14:13:49 +000026{
27 const unsigned int inputHeight = 2;
28 const unsigned int inputWidth = 2;
29 const unsigned int inputChannels = 1;
30 const unsigned int inputNum = 2;
31
32 unsigned int outputHeight = inputHeight;
33 unsigned int outputWidth = inputWidth;
34 unsigned int outputChannels = inputChannels;
35 unsigned int outputNum = inputNum;
36
37 unsigned int inputShape[] = { inputNum, inputChannels, inputHeight, inputWidth };
38 unsigned int outputShape[] = { outputNum, outputChannels, outputHeight, outputWidth };
39
40 auto inputTensorInfo = armnn::TensorInfo(4, inputShape, armnn::DataType::Float32);
41 auto outputTensorInfo = armnn::TensorInfo(4, outputShape, armnn::DataType::Float32);
42
43 LayerTestResult<float,4> ret(outputTensorInfo);
44
45 auto input = MakeTensor<float, 4>(inputTensorInfo, std::vector<float>({
46 // Batch #0
47 1.0f, 2.0f,
48 3.0f, 4.0f,
49 // Batch #1
50 5.0f, 6.0f,
51 7.0f, 8.0f
52 }));
53
54 float alpha = 1.f;
55 float beta = 1.f;
56 float kappa = 1.f;
57 uint32_t normSize = 3;
58
59 std::unique_ptr<armnn::ITensorHandle> inputHandle = workloadFactory.CreateTensorHandle(inputTensorInfo);
60 std::unique_ptr<armnn::ITensorHandle> outputHandle = workloadFactory.CreateTensorHandle(outputTensorInfo);
61
62 armnn::NormalizationQueueDescriptor data;
63 armnn::WorkloadInfo info;
64 AddInputToWorkload(data, info, inputTensorInfo, inputHandle.get());
65 AddOutputToWorkload(data, info, outputTensorInfo, outputHandle.get());
66 data.m_Parameters.m_NormChannelType = normChannel;
67 data.m_Parameters.m_NormMethodType = normMethod;
68 data.m_Parameters.m_NormSize = normSize;
69 data.m_Parameters.m_Alpha = alpha;
70 data.m_Parameters.m_Beta = beta;
71 data.m_Parameters.m_K = kappa;
narpra0155a97bc2018-10-02 14:35:53 +010072 data.m_Parameters.m_DataLayout = armnn::DataLayout::NCHW;
telsoa014fcda012018-03-09 14:13:49 +000073
74 armnn::PassthroughCpuTensorHandle refHandle(outputTensorInfo, &ret.outputExpected[0][0][0][0]);
75 armnn::NormalizationQueueDescriptor refData = data;
76 armnn::WorkloadInfo refInfo = info;
77 SetWorkloadOutput(refData, refInfo, 0, outputTensorInfo, &refHandle);
78
79 std::unique_ptr<armnn::IWorkload> workload = workloadFactory.CreateNormalization(data, info);
80
81 inputHandle->Allocate();
82 outputHandle->Allocate();
83
84 CopyDataToITensorHandle(inputHandle.get(), &input[0][0][0][0]);
85
Aron Virginas-Tar5caf9072018-11-14 18:35:18 +000086 ExecuteWorkload(*workload, memoryManager);
telsoa014fcda012018-03-09 14:13:49 +000087
88 CopyDataFromITensorHandle(&ret.output[0][0][0][0], outputHandle.get());
89
90 switch (normMethod)
91 {
92 case armnn::NormalizationAlgorithmMethod::LocalBrightness:
93 {
94 switch (normChannel)
95 {
96 case armnn::NormalizationAlgorithmChannel::Within:
97 {
98 // When normalising within channels, the 3x3 kernel covers the entire 2x2 input at every index.
99 // Therefore, all output values should equal the inputs, but divided by:
100 // pow((kappa + (accumulatedScale * alpha)), beta)
telsoa01c577f2c2018-08-31 09:22:23 +0100101 // ...where accumulatedScale is the sum of every element squared.
telsoa014fcda012018-03-09 14:13:49 +0000102 float divisor[inputNum];
103 for(int i = 0; i < boost::numeric_cast<int>(inputNum); i++)
104 {
105 float accumulatedScale = input[i][0][0][0]*input[i][0][0][0] +
106 input[i][0][0][1]*input[i][0][0][1] +
107 input[i][0][1][0]*input[i][0][1][0] +
108 input[i][0][1][1]*input[i][0][1][1];
109 divisor[i] = powf((kappa + accumulatedScale * alpha), beta);
110 }
111 ret.outputExpected = MakeTensor<float, 4>(outputTensorInfo,
112 std::vector<float>({input[0][0][0][0]/divisor[0],
113 input[0][0][0][1]/divisor[0],
114 input[0][0][1][0]/divisor[0],
115 input[0][0][1][1]/divisor[0],
116 input[1][0][0][0]/divisor[1],
117 input[1][0][0][1]/divisor[1],
118 input[1][0][1][0]/divisor[1],
119 input[1][0][1][1]/divisor[1]}));
120 break;
121 }
122 case armnn::NormalizationAlgorithmChannel::Across:
123 {
124 // When normalising across channels, all output values should equal the inputs, but multiplied by:
125 // pow((kappa + (accumulatedScale * alpha)), -beta)
126 // ...where accumulatedScale is the sum of the inputs for adjacent channels for this element squared
127 // ...where adjacent channels means within half the normSize for the channel
128 // The test data has only one channel, so this is simplified below.
129 std::vector<float> outputVector;
130 for (int n = 0; n < boost::numeric_cast<int>(inputNum); ++n)
131 {
132 for (int h = 0; h < boost::numeric_cast<int>(inputHeight); ++h)
133 {
134 for (int w = 0; w < boost::numeric_cast<int>(inputWidth); ++w)
135 {
136 float accumulatedScale = input[n][0][h][w]*input[n][0][h][w];
137 float scale = powf((kappa + accumulatedScale * alpha), -beta);
138 outputVector.push_back(input[n][0][h][w] * scale);
139 }
140 }
141 }
142 ret.outputExpected = MakeTensor<float, 4>(outputTensorInfo, outputVector);
143 break;
144 }
145 default:
146 {
147 throw armnn::UnimplementedException("Unsupported normalisation channel type, "
148 "only Across and Within are supported");
149 }
150 }
151 break;
152 }
telsoa01c577f2c2018-08-31 09:22:23 +0100153 case armnn::NormalizationAlgorithmMethod::LocalContrast: // NOTE: intentional fallthrough.
telsoa014fcda012018-03-09 14:13:49 +0000154 default:
155 {
156 throw armnn::UnimplementedException("Unsupported normalisation method type, "
157 "only LocalBrightness is supported");
158 }
159 }
160
161 return ret;
162}
163
Aron Virginas-Tar5caf9072018-11-14 18:35:18 +0000164LayerTestResult<float,4> SimpleNormalizationNhwcTestImpl(
165 armnn::IWorkloadFactory& workloadFactory,
166 const armnn::IBackendInternal::IMemoryManagerSharedPtr& memoryManager,
167 armnn::NormalizationAlgorithmChannel normChannel,
168 armnn::NormalizationAlgorithmMethod normMethod)
narpra0155a97bc2018-10-02 14:35:53 +0100169{
170 const unsigned int inputHeight = 2;
171 const unsigned int inputWidth = 2;
172 const unsigned int inputChannels = 1;
173 const unsigned int inputNum = 2;
174
175 unsigned int outputHeight = inputHeight;
176 unsigned int outputWidth = inputWidth;
177 unsigned int outputChannels = inputChannels;
178 unsigned int outputNum = inputNum;
179
180 unsigned int inputShape[] = { inputNum, inputHeight, inputWidth, inputChannels };
181 unsigned int outputShape[] = { outputNum, outputHeight, outputWidth, outputChannels };
182
183 auto inputTensorInfo = armnn::TensorInfo(4, inputShape, armnn::DataType::Float32);
184 auto outputTensorInfo = armnn::TensorInfo(4, outputShape, armnn::DataType::Float32);
185
186 LayerTestResult<float,4> ret(outputTensorInfo);
187
188 auto input = MakeTensor<float, 4>(inputTensorInfo, std::vector<float>({
189 // Batch #0
190 1.0f, 2.0f,
191 3.0f, 4.0f,
192 // Batch #1
193 5.0f, 6.0f,
194 7.0f, 8.0f
195 }));
196
197 float alpha = 1.f;
198 float beta = 1.f;
199 float kappa = 1.f;
200 uint32_t normSize = 3;
201
202 std::unique_ptr<armnn::ITensorHandle> inputHandle = workloadFactory.CreateTensorHandle(inputTensorInfo);
203 std::unique_ptr<armnn::ITensorHandle> outputHandle = workloadFactory.CreateTensorHandle(outputTensorInfo);
204
205 armnn::NormalizationQueueDescriptor data;
206 armnn::WorkloadInfo info;
207 AddInputToWorkload(data, info, inputTensorInfo, inputHandle.get());
208 AddOutputToWorkload(data, info, outputTensorInfo, outputHandle.get());
209 data.m_Parameters.m_NormChannelType = normChannel;
210 data.m_Parameters.m_NormMethodType = normMethod;
211 data.m_Parameters.m_NormSize = normSize;
212 data.m_Parameters.m_Alpha = alpha;
213 data.m_Parameters.m_Beta = beta;
214 data.m_Parameters.m_K = kappa;
215 data.m_Parameters.m_DataLayout = armnn::DataLayout::NHWC;
216
217 armnn::PassthroughCpuTensorHandle refHandle(outputTensorInfo, &ret.outputExpected[0][0][0][0]);
218 armnn::NormalizationQueueDescriptor refData = data;
219 armnn::WorkloadInfo refInfo = info;
220 SetWorkloadOutput(refData, refInfo, 0, outputTensorInfo, &refHandle);
221
222 std::unique_ptr<armnn::IWorkload> workload = workloadFactory.CreateNormalization(data, info);
223
224 inputHandle->Allocate();
225 outputHandle->Allocate();
226
227 CopyDataToITensorHandle(inputHandle.get(), &input[0][0][0][0]);
228
Aron Virginas-Tar5caf9072018-11-14 18:35:18 +0000229 ExecuteWorkload(*workload, memoryManager);
narpra0155a97bc2018-10-02 14:35:53 +0100230
231 CopyDataFromITensorHandle(&ret.output[0][0][0][0], outputHandle.get());
232
233 switch (normMethod)
234 {
235 case armnn::NormalizationAlgorithmMethod::LocalBrightness:
236 {
237 switch (normChannel)
238 {
239 case armnn::NormalizationAlgorithmChannel::Across:
240 {
241 std::vector<float> expectedOutput{ 0.5f, 0.400000006f, 0.300000012f, 0.235294119f,
242 0.192307696f, 0.16216217f, 0.140000001f, 0.123076923f };
243 ret.outputExpected = MakeTensor<float, 4>(outputTensorInfo, expectedOutput);
244 break;
245 }
246 default:
247 {
248 throw armnn::UnimplementedException("Unsupported normalisation channel type, "
249 "Only Cross-map is supported for NHWC layout");
250 }
251 }
252 break;
253 }
254 case armnn::NormalizationAlgorithmMethod::LocalContrast: // NOTE: intentional fallthrough.
255 default:
256 {
257 throw armnn::UnimplementedException("Unsupported normalisation method type, "
258 "only LocalBrightness is supported");
259 }
260 }
261
262 return ret;
263}
264
Aron Virginas-Tar5caf9072018-11-14 18:35:18 +0000265LayerTestResult<float,4> CompareNormalizationTestImpl(
266 armnn::IWorkloadFactory& workloadFactory,
267 const armnn::IBackendInternal::IMemoryManagerSharedPtr& memoryManager,
268 armnn::IWorkloadFactory& refWorkloadFactory,
269 armnn::NormalizationAlgorithmChannel normChannel,
270 armnn::NormalizationAlgorithmMethod normMethod)
telsoa014fcda012018-03-09 14:13:49 +0000271{
272 constexpr unsigned int inputNum = 5;
273 constexpr unsigned int inputChannels = 3;
274 constexpr unsigned int inputHeight = 32;
275 constexpr unsigned int inputWidth = 24;
276
277 constexpr unsigned int outputNum = inputNum;
278 constexpr unsigned int outputChannels = inputChannels;
279 constexpr unsigned int outputHeight = inputHeight;
280 constexpr unsigned int outputWidth = inputWidth;
281
282 armnn::TensorInfo inputTensorInfo;
283 armnn::TensorInfo outputTensorInfo;
284
285 unsigned int inputShape[] = {inputNum, inputChannels, inputHeight, inputWidth};
286 unsigned int outputShape[] = {outputNum, outputChannels, outputHeight, outputWidth};
287
288 inputTensorInfo = armnn::TensorInfo(4, inputShape, armnn::DataType::Float32);
289 outputTensorInfo = armnn::TensorInfo(4, outputShape, armnn::DataType::Float32);
290
291 LayerTestResult<float,4> ret(outputTensorInfo);
292
293 auto input = MakeRandomTensor<float, 4>(inputTensorInfo, 111234);
294
295 constexpr float alpha = 1.f;
296 constexpr float beta = 1.f;
297 constexpr float kappa = 1.f;
298 constexpr uint32_t normSize = 5;
299
300 std::unique_ptr<armnn::ITensorHandle> inputHandle = workloadFactory.CreateTensorHandle(inputTensorInfo);
301 std::unique_ptr<armnn::ITensorHandle> outputHandle = workloadFactory.CreateTensorHandle(outputTensorInfo);
302
303 armnn::NormalizationQueueDescriptor data;
304 armnn::WorkloadInfo info;
305 AddInputToWorkload(data, info, inputTensorInfo, inputHandle.get());
306 AddOutputToWorkload(data, info, outputTensorInfo, outputHandle.get());
307 data.m_Parameters.m_NormChannelType = normChannel;
308 data.m_Parameters.m_NormMethodType = normMethod;
309 data.m_Parameters.m_NormSize = normSize;
310 data.m_Parameters.m_Alpha = alpha;
311 data.m_Parameters.m_Beta = beta;
312 data.m_Parameters.m_K = kappa;
313
314 std::unique_ptr<armnn::ITensorHandle> outputHandleRef = refWorkloadFactory.CreateTensorHandle(outputTensorInfo);
315 std::unique_ptr<armnn::ITensorHandle> inputHandleRef = refWorkloadFactory.CreateTensorHandle(inputTensorInfo);
316
317 armnn::NormalizationQueueDescriptor refData = data;
318 armnn::WorkloadInfo refInfo = info;
319 SetWorkloadInput(refData, refInfo, 0, inputTensorInfo, inputHandleRef.get());
320 SetWorkloadOutput(refData, refInfo, 0, outputTensorInfo, outputHandleRef.get());
321
322 // 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 +0100323 armnn::BackendId backend = workloadFactory.GetBackendId();
telsoa014fcda012018-03-09 14:13:49 +0000324 const size_t reasonIfUnsupportedMaxLen = 255;
325 char reasonIfUnsupported[reasonIfUnsupportedMaxLen+1];
David Beck79141b92018-10-23 16:09:36 +0100326 ret.supported = armnn::IsNormalizationSupported(backend, inputTensorInfo, outputTensorInfo, data.m_Parameters,
telsoa014fcda012018-03-09 14:13:49 +0000327 reasonIfUnsupported, reasonIfUnsupportedMaxLen);
328 if (!ret.supported)
329 {
330 return ret;
331 }
332
333 std::unique_ptr<armnn::IWorkload> workload = workloadFactory.CreateNormalization(data, info);
334 std::unique_ptr<armnn::IWorkload> workloadRef = refWorkloadFactory.CreateNormalization(refData, refInfo);
335
336 outputHandleRef->Allocate();
337 inputHandleRef->Allocate();
338
339 inputHandle->Allocate();
340 outputHandle->Allocate();
341
342 CopyDataToITensorHandle(inputHandle.get(), &input[0][0][0][0]);
343 CopyDataToITensorHandle(inputHandleRef.get(), &input[0][0][0][0]);
344
Aron Virginas-Tar5caf9072018-11-14 18:35:18 +0000345 ExecuteWorkload(*workload, memoryManager);
Aron Virginas-Tar60578952018-10-31 11:04:01 +0000346
telsoa014fcda012018-03-09 14:13:49 +0000347 workloadRef->Execute();
348
349 CopyDataFromITensorHandle(&ret.output[0][0][0][0], outputHandle.get());
350 CopyDataFromITensorHandle(&ret.outputExpected[0][0][0][0], outputHandleRef.get());
351
352 return ret;
353}
354
Aron Virginas-Tar00d306e2019-08-28 18:08:46 +0100355} // anonymous namespace
356
357LayerTestResult<float,4> SimpleNormalizationAcrossTest(
358 armnn::IWorkloadFactory& workloadFactory,
359 const armnn::IBackendInternal::IMemoryManagerSharedPtr& memoryManager)
360{
361 auto normMethod = armnn::NormalizationAlgorithmMethod::LocalBrightness;
362 auto normChannel = armnn::NormalizationAlgorithmChannel::Across;
363 return SimpleNormalizationTestImpl(workloadFactory, memoryManager, normChannel, normMethod);
364}
365
366LayerTestResult<float,4> SimpleNormalizationWithinTest(
367 armnn::IWorkloadFactory& workloadFactory,
368 const armnn::IBackendInternal::IMemoryManagerSharedPtr& memoryManager)
369{
370 auto normMethod = armnn::NormalizationAlgorithmMethod::LocalBrightness;
371 auto normChannel = armnn::NormalizationAlgorithmChannel::Within;
372 return SimpleNormalizationTestImpl(workloadFactory, memoryManager, normChannel, normMethod);
373}
374
375LayerTestResult<float,4> SimpleNormalizationAcrossNhwcTest(
376 armnn::IWorkloadFactory& workloadFactory,
377 const armnn::IBackendInternal::IMemoryManagerSharedPtr& memoryManager)
378{
379 auto normMethod = armnn::NormalizationAlgorithmMethod::LocalBrightness;
380 auto normChannel = armnn::NormalizationAlgorithmChannel::Across;
381 return SimpleNormalizationNhwcTestImpl(workloadFactory, memoryManager, normChannel, normMethod);
382}
383
384LayerTestResult<float,4> CompareNormalizationTest(
385 armnn::IWorkloadFactory& workloadFactory,
386 const armnn::IBackendInternal::IMemoryManagerSharedPtr& memoryManager,
387 armnn::IWorkloadFactory& refWorkloadFactory,
388 armnn::NormalizationAlgorithmChannel normChannel,
389 armnn::NormalizationAlgorithmMethod normMethod)
390{
391 return CompareNormalizationTestImpl(workloadFactory, memoryManager, refWorkloadFactory, normChannel, normMethod);
392}