hadamard: Add 4x4 test.
[aom.git] / test / transform_test_base.h
blob260f4ffef8055373ee55a5c7c23e26adff12b8a3
1 /*
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 {
23 // Note:
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 {
42 public:
43 virtual ~TransformTestBase() {}
45 protected:
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) {
79 src[j] = rnd.Rand8();
80 dst[j] = rnd.Rand8();
81 test_input_block[j] = src[j] - dst[j];
82 } else {
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_));
93 } else {
94 API_REGISTER_STATE_CHECK(
95 RunInvTxfm(test_temp_block, CONVERT_TO_BYTEPTR(dst16), pitch_));
98 for (int j = 0; j < num_coeffs_; ++j) {
99 const int diff =
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 > "
111 << ref_max_error;
113 EXPECT_GE(ref_avg_error, avg_error)
114 << "Error: FHT/IHT has average round trip error > " << ref_avg_error
115 << " per block";
117 aom_free(test_input_block);
118 aom_free(test_temp_block);
119 aom_free(dst);
120 aom_free(src);
121 aom_free(dst16);
122 aom_free(src16);
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.
131 int stride = 96;
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) {
144 int j, k;
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();
152 } else {
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.
183 int stride = 96;
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_].
200 int j, k;
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);
231 void RunMemCheck() {
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_;
250 if (i == 0) {
251 for (int j = 0; j < num_coeffs_; ++j) input_extreme_block[j] = mask_;
252 } else if (i == 1) {
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];
307 } else {
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_));
318 } else {
319 API_REGISTER_STATE_CHECK(
320 RunInvTxfm(coeff, CONVERT_TO_BYTEPTR(dst16), pitch_));
323 for (int j = 0; j < num_coeffs_; ++j) {
324 const int diff =
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;
331 aom_free(in);
332 aom_free(coeff);
333 aom_free(dst);
334 aom_free(src);
335 aom_free(src16);
336 aom_free(dst16);
339 int pitch_;
340 int height_;
341 FhtFunc<OutType> fwd_txfm_ref;
342 IhtFunc<OutType> inv_txfm_ref;
343 aom_bit_depth_t bit_depth_;
344 int mask_;
345 int num_coeffs_;
346 TxfmParam txfm_param_;
348 private:
349 // Assume transform size is 4x4, 8x8, 16x16,...
350 int FindRowLength() const {
351 int row = 4;
352 if (16 == num_coeffs_) {
353 row = 4;
354 } else if (64 == num_coeffs_) {
355 row = 8;
356 } else if (256 == num_coeffs_) {
357 row = 16;
358 } else if (1024 == num_coeffs_) {
359 row = 32;
361 return row;
365 } // namespace libaom_test
367 #endif // AOM_TEST_TRANSFORM_TEST_BASE_H_