2 * Copyright (c) 2016, Alliance for Open Media. All rights reserved
4 * This source code is subject to the terms of the BSD 2 Clause License and
5 * the Alliance for Open Media Patent License 1.0. If the BSD 2 Clause License
6 * was not distributed with this source code in the LICENSE file, you can
7 * obtain it at www.aomedia.org/license/software. If the Alliance for Open
8 * Media Patent License 1.0 was not distributed with this source code in the
9 * PATENTS file, you can obtain it at www.aomedia.org/license/patent.
12 #ifndef AOM_TEST_TRANSFORM_TEST_BASE_H_
13 #define AOM_TEST_TRANSFORM_TEST_BASE_H_
15 #include "config/aom_config.h"
17 #include "aom_mem/aom_mem.h"
18 #include "aom/aom_codec.h"
19 #include "aom_dsp/txfm_common.h"
21 namespace libaom_test
{
24 // Same constant are defined in av1/common/av1_entropy.h and
25 // av1/common/entropy.h. Goal is to make this base class
26 // to use for future codec transform testing. But including
27 // either of them would lead to compiling error when we do
28 // unit test for another codec. Suggest to move the definition
29 // to a aom header file.
30 const int kDctMaxValue
= 16384;
32 template <typename OutputType
>
33 using FhtFunc
= void (*)(const int16_t *in
, OutputType
*out
, int stride
,
34 TxfmParam
*txfm_param
);
36 template <typename OutputType
>
37 using IhtFunc
= void (*)(const tran_low_t
*in
, uint8_t *out
, int stride
,
38 const TxfmParam
*txfm_param
);
40 template <typename OutType
>
41 class TransformTestBase
{
43 virtual ~TransformTestBase() {}
46 virtual void RunFwdTxfm(const int16_t *in
, OutType
*out
, int stride
) = 0;
48 virtual void RunInvTxfm(const OutType
*out
, uint8_t *dst
, int stride
) = 0;
50 void RunAccuracyCheck(uint32_t ref_max_error
, double ref_avg_error
) {
51 ACMRandom
rnd(ACMRandom::DeterministicSeed());
52 uint32_t max_error
= 0;
53 int64_t total_error
= 0;
54 const int count_test_block
= 10000;
56 int16_t *test_input_block
= reinterpret_cast<int16_t *>(
57 aom_memalign(16, sizeof(int16_t) * num_coeffs_
));
58 ASSERT_NE(test_input_block
, nullptr);
59 OutType
*test_temp_block
= reinterpret_cast<OutType
*>(
60 aom_memalign(16, sizeof(test_temp_block
[0]) * num_coeffs_
));
61 ASSERT_NE(test_temp_block
, nullptr);
62 uint8_t *dst
= reinterpret_cast<uint8_t *>(
63 aom_memalign(16, sizeof(uint8_t) * num_coeffs_
));
64 ASSERT_NE(dst
, nullptr);
65 uint8_t *src
= reinterpret_cast<uint8_t *>(
66 aom_memalign(16, sizeof(uint8_t) * num_coeffs_
));
67 ASSERT_NE(src
, nullptr);
68 uint16_t *dst16
= reinterpret_cast<uint16_t *>(
69 aom_memalign(16, sizeof(uint16_t) * num_coeffs_
));
70 ASSERT_NE(dst16
, nullptr);
71 uint16_t *src16
= reinterpret_cast<uint16_t *>(
72 aom_memalign(16, sizeof(uint16_t) * num_coeffs_
));
73 ASSERT_NE(src16
, nullptr);
75 for (int i
= 0; i
< count_test_block
; ++i
) {
76 // Initialize a test block with input range [-255, 255].
77 for (int j
= 0; j
< num_coeffs_
; ++j
) {
78 if (bit_depth_
== AOM_BITS_8
) {
81 test_input_block
[j
] = src
[j
] - dst
[j
];
83 src16
[j
] = rnd
.Rand16() & mask_
;
84 dst16
[j
] = rnd
.Rand16() & mask_
;
85 test_input_block
[j
] = src16
[j
] - dst16
[j
];
89 API_REGISTER_STATE_CHECK(
90 RunFwdTxfm(test_input_block
, test_temp_block
, pitch_
));
91 if (bit_depth_
== AOM_BITS_8
) {
92 API_REGISTER_STATE_CHECK(RunInvTxfm(test_temp_block
, dst
, pitch_
));
94 API_REGISTER_STATE_CHECK(
95 RunInvTxfm(test_temp_block
, CONVERT_TO_BYTEPTR(dst16
), pitch_
));
98 for (int j
= 0; j
< num_coeffs_
; ++j
) {
100 bit_depth_
== AOM_BITS_8
? dst
[j
] - src
[j
] : dst16
[j
] - src16
[j
];
101 const uint32_t error
= diff
* diff
;
102 if (max_error
< error
) max_error
= error
;
103 total_error
+= error
;
107 double avg_error
= total_error
* 1. / count_test_block
/ num_coeffs_
;
109 EXPECT_GE(ref_max_error
, max_error
)
110 << "Error: FHT/IHT has an individual round trip error > "
113 EXPECT_GE(ref_avg_error
, avg_error
)
114 << "Error: FHT/IHT has average round trip error > " << ref_avg_error
117 aom_free(test_input_block
);
118 aom_free(test_temp_block
);
125 void RunCoeffCheck() {
126 ACMRandom
rnd(ACMRandom::DeterministicSeed());
127 const int count_test_block
= 5000;
129 // Use a stride value which is not the width of any transform, to catch
130 // cases where the transforms use the stride incorrectly.
133 int16_t *input_block
= reinterpret_cast<int16_t *>(
134 aom_memalign(16, sizeof(int16_t) * stride
* height_
));
135 ASSERT_NE(input_block
, nullptr);
136 OutType
*output_ref_block
= reinterpret_cast<OutType
*>(
137 aom_memalign(16, sizeof(output_ref_block
[0]) * num_coeffs_
));
138 ASSERT_NE(output_ref_block
, nullptr);
139 OutType
*output_block
= reinterpret_cast<OutType
*>(
140 aom_memalign(16, sizeof(output_block
[0]) * num_coeffs_
));
141 ASSERT_NE(output_block
, nullptr);
143 for (int i
= 0; i
< count_test_block
; ++i
) {
145 for (j
= 0; j
< height_
; ++j
) {
146 for (k
= 0; k
< pitch_
; ++k
) {
147 int in_idx
= j
* stride
+ k
;
148 int out_idx
= j
* pitch_
+ k
;
149 input_block
[in_idx
] = (rnd
.Rand16() & mask_
) - (rnd
.Rand16() & mask_
);
150 if (bit_depth_
== AOM_BITS_8
) {
151 output_block
[out_idx
] = output_ref_block
[out_idx
] = rnd
.Rand8();
153 output_block
[out_idx
] = output_ref_block
[out_idx
] =
154 rnd
.Rand16() & mask_
;
159 fwd_txfm_ref(input_block
, output_ref_block
, stride
, &txfm_param_
);
160 API_REGISTER_STATE_CHECK(RunFwdTxfm(input_block
, output_block
, stride
));
162 // The minimum quant value is 4.
163 for (j
= 0; j
< height_
; ++j
) {
164 for (k
= 0; k
< pitch_
; ++k
) {
165 int out_idx
= j
* pitch_
+ k
;
166 ASSERT_EQ(output_block
[out_idx
], output_ref_block
[out_idx
])
167 << "Error: not bit-exact result at index: " << out_idx
168 << " at test block: " << i
;
172 aom_free(input_block
);
173 aom_free(output_ref_block
);
174 aom_free(output_block
);
177 void RunInvCoeffCheck() {
178 ACMRandom
rnd(ACMRandom::DeterministicSeed());
179 const int count_test_block
= 5000;
181 // Use a stride value which is not the width of any transform, to catch
182 // cases where the transforms use the stride incorrectly.
185 int16_t *input_block
= reinterpret_cast<int16_t *>(
186 aom_memalign(16, sizeof(int16_t) * num_coeffs_
));
187 ASSERT_NE(input_block
, nullptr);
188 OutType
*trans_block
= reinterpret_cast<OutType
*>(
189 aom_memalign(16, sizeof(trans_block
[0]) * num_coeffs_
));
190 ASSERT_NE(trans_block
, nullptr);
191 uint8_t *output_block
= reinterpret_cast<uint8_t *>(
192 aom_memalign(16, sizeof(uint8_t) * stride
* height_
));
193 ASSERT_NE(output_block
, nullptr);
194 uint8_t *output_ref_block
= reinterpret_cast<uint8_t *>(
195 aom_memalign(16, sizeof(uint8_t) * stride
* height_
));
196 ASSERT_NE(output_ref_block
, nullptr);
198 for (int i
= 0; i
< count_test_block
; ++i
) {
199 // Initialize a test block with input range [-mask_, mask_].
201 for (j
= 0; j
< height_
; ++j
) {
202 for (k
= 0; k
< pitch_
; ++k
) {
203 int in_idx
= j
* pitch_
+ k
;
204 int out_idx
= j
* stride
+ k
;
205 input_block
[in_idx
] = (rnd
.Rand16() & mask_
) - (rnd
.Rand16() & mask_
);
206 output_ref_block
[out_idx
] = rnd
.Rand16() & mask_
;
207 output_block
[out_idx
] = output_ref_block
[out_idx
];
211 fwd_txfm_ref(input_block
, trans_block
, pitch_
, &txfm_param_
);
213 inv_txfm_ref(trans_block
, output_ref_block
, stride
, &txfm_param_
);
214 API_REGISTER_STATE_CHECK(RunInvTxfm(trans_block
, output_block
, stride
));
216 for (j
= 0; j
< height_
; ++j
) {
217 for (k
= 0; k
< pitch_
; ++k
) {
218 int out_idx
= j
* stride
+ k
;
219 ASSERT_EQ(output_block
[out_idx
], output_ref_block
[out_idx
])
220 << "Error: not bit-exact result at index: " << out_idx
221 << " j = " << j
<< " k = " << k
<< " at test block: " << i
;
225 aom_free(input_block
);
226 aom_free(trans_block
);
227 aom_free(output_ref_block
);
228 aom_free(output_block
);
232 ACMRandom
rnd(ACMRandom::DeterministicSeed());
233 const int count_test_block
= 5000;
235 int16_t *input_extreme_block
= reinterpret_cast<int16_t *>(
236 aom_memalign(16, sizeof(int16_t) * num_coeffs_
));
237 ASSERT_NE(input_extreme_block
, nullptr);
238 OutType
*output_ref_block
= reinterpret_cast<OutType
*>(
239 aom_memalign(16, sizeof(output_ref_block
[0]) * num_coeffs_
));
240 ASSERT_NE(output_ref_block
, nullptr);
241 OutType
*output_block
= reinterpret_cast<OutType
*>(
242 aom_memalign(16, sizeof(output_block
[0]) * num_coeffs_
));
243 ASSERT_NE(output_block
, nullptr);
245 for (int i
= 0; i
< count_test_block
; ++i
) {
246 // Initialize a test block with input range [-mask_, mask_].
247 for (int j
= 0; j
< num_coeffs_
; ++j
) {
248 input_extreme_block
[j
] = rnd
.Rand8() % 2 ? mask_
: -mask_
;
251 for (int j
= 0; j
< num_coeffs_
; ++j
) input_extreme_block
[j
] = mask_
;
253 for (int j
= 0; j
< num_coeffs_
; ++j
) input_extreme_block
[j
] = -mask_
;
256 fwd_txfm_ref(input_extreme_block
, output_ref_block
, pitch_
, &txfm_param_
);
257 API_REGISTER_STATE_CHECK(
258 RunFwdTxfm(input_extreme_block
, output_block
, pitch_
));
260 int row_length
= FindRowLength();
261 // The minimum quant value is 4.
262 for (int j
= 0; j
< num_coeffs_
; ++j
) {
263 ASSERT_EQ(output_block
[j
], output_ref_block
[j
])
264 << "Not bit-exact at test index: " << i
<< ", "
265 << "j = " << j
<< std::endl
;
266 EXPECT_GE(row_length
* kDctMaxValue
<< (bit_depth_
- 8),
267 abs(output_block
[j
]))
268 << "Error: NxN FDCT has coefficient larger than N*DCT_MAX_VALUE";
271 aom_free(input_extreme_block
);
272 aom_free(output_ref_block
);
273 aom_free(output_block
);
276 void RunInvAccuracyCheck(int limit
) {
277 ACMRandom
rnd(ACMRandom::DeterministicSeed());
278 const int count_test_block
= 1000;
280 int16_t *in
= reinterpret_cast<int16_t *>(
281 aom_memalign(16, sizeof(int16_t) * num_coeffs_
));
282 ASSERT_NE(in
, nullptr);
283 OutType
*coeff
= reinterpret_cast<OutType
*>(
284 aom_memalign(16, sizeof(coeff
[0]) * num_coeffs_
));
285 ASSERT_NE(coeff
, nullptr);
286 uint8_t *dst
= reinterpret_cast<uint8_t *>(
287 aom_memalign(16, sizeof(uint8_t) * num_coeffs_
));
288 ASSERT_NE(dst
, nullptr);
289 uint8_t *src
= reinterpret_cast<uint8_t *>(
290 aom_memalign(16, sizeof(uint8_t) * num_coeffs_
));
291 ASSERT_NE(src
, nullptr);
293 uint16_t *dst16
= reinterpret_cast<uint16_t *>(
294 aom_memalign(16, sizeof(uint16_t) * num_coeffs_
));
295 ASSERT_NE(dst16
, nullptr);
296 uint16_t *src16
= reinterpret_cast<uint16_t *>(
297 aom_memalign(16, sizeof(uint16_t) * num_coeffs_
));
298 ASSERT_NE(src16
, nullptr);
300 for (int i
= 0; i
< count_test_block
; ++i
) {
301 // Initialize a test block with input range [-mask_, mask_].
302 for (int j
= 0; j
< num_coeffs_
; ++j
) {
303 if (bit_depth_
== AOM_BITS_8
) {
304 src
[j
] = rnd
.Rand8();
305 dst
[j
] = rnd
.Rand8();
306 in
[j
] = src
[j
] - dst
[j
];
308 src16
[j
] = rnd
.Rand16() & mask_
;
309 dst16
[j
] = rnd
.Rand16() & mask_
;
310 in
[j
] = src16
[j
] - dst16
[j
];
314 fwd_txfm_ref(in
, coeff
, pitch_
, &txfm_param_
);
316 if (bit_depth_
== AOM_BITS_8
) {
317 API_REGISTER_STATE_CHECK(RunInvTxfm(coeff
, dst
, pitch_
));
319 API_REGISTER_STATE_CHECK(
320 RunInvTxfm(coeff
, CONVERT_TO_BYTEPTR(dst16
), pitch_
));
323 for (int j
= 0; j
< num_coeffs_
; ++j
) {
325 bit_depth_
== AOM_BITS_8
? dst
[j
] - src
[j
] : dst16
[j
] - src16
[j
];
326 const uint32_t error
= diff
* diff
;
327 ASSERT_GE(static_cast<uint32_t>(limit
), error
)
328 << "Error: 4x4 IDCT has error " << error
<< " at index " << j
;
341 FhtFunc
<OutType
> fwd_txfm_ref
;
342 IhtFunc
<OutType
> inv_txfm_ref
;
343 aom_bit_depth_t bit_depth_
;
346 TxfmParam txfm_param_
;
349 // Assume transform size is 4x4, 8x8, 16x16,...
350 int FindRowLength() const {
352 if (16 == num_coeffs_
) {
354 } else if (64 == num_coeffs_
) {
356 } else if (256 == num_coeffs_
) {
358 } else if (1024 == num_coeffs_
) {
365 } // namespace libaom_test
367 #endif // AOM_TEST_TRANSFORM_TEST_BASE_H_