• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2016, Alliance for Open Media. All rights reserved
3  *
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.
10  */
11 
12 #ifndef AOM_TEST_TRANSFORM_TEST_BASE_H_
13 #define AOM_TEST_TRANSFORM_TEST_BASE_H_
14 
15 #include "config/aom_config.h"
16 
17 #include "aom_mem/aom_mem.h"
18 #include "aom/aom_codec.h"
19 #include "aom_dsp/txfm_common.h"
20 
21 namespace libaom_test {
22 
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;
31 
32 template <typename OutputType>
33 using FhtFunc = void (*)(const int16_t *in, OutputType *out, int stride,
34                          TxfmParam *txfm_param);
35 
36 template <typename OutputType>
37 using IhtFunc = void (*)(const tran_low_t *in, uint8_t *out, int stride,
38                          const TxfmParam *txfm_param);
39 
40 template <typename OutType>
41 class TransformTestBase {
42  public:
~TransformTestBase()43   virtual ~TransformTestBase() {}
44 
45  protected:
46   virtual void RunFwdTxfm(const int16_t *in, OutType *out, int stride) = 0;
47 
48   virtual void RunInvTxfm(const OutType *out, uint8_t *dst, int stride) = 0;
49 
RunAccuracyCheck(uint32_t ref_max_error,double ref_avg_error)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;
55 
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);
74 
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];
86         }
87       }
88 
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_));
96       }
97 
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;
104       }
105     }
106 
107     double avg_error = total_error * 1. / count_test_block / num_coeffs_;
108 
109     EXPECT_GE(ref_max_error, max_error)
110         << "Error: FHT/IHT has an individual round trip error > "
111         << ref_max_error;
112 
113     EXPECT_GE(ref_avg_error, avg_error)
114         << "Error: FHT/IHT has average round trip error > " << ref_avg_error
115         << " per block";
116 
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);
123   }
124 
RunCoeffCheck()125   void RunCoeffCheck() {
126     ACMRandom rnd(ACMRandom::DeterministicSeed());
127     const int count_test_block = 5000;
128 
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;
132 
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);
142 
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_;
155           }
156         }
157       }
158 
159       fwd_txfm_ref(input_block, output_ref_block, stride, &txfm_param_);
160       API_REGISTER_STATE_CHECK(RunFwdTxfm(input_block, output_block, stride));
161 
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;
169         }
170       }
171     }
172     aom_free(input_block);
173     aom_free(output_ref_block);
174     aom_free(output_block);
175   }
176 
RunInvCoeffCheck()177   void RunInvCoeffCheck() {
178     ACMRandom rnd(ACMRandom::DeterministicSeed());
179     const int count_test_block = 5000;
180 
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;
184 
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);
197 
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];
208         }
209       }
210 
211       fwd_txfm_ref(input_block, trans_block, pitch_, &txfm_param_);
212 
213       inv_txfm_ref(trans_block, output_ref_block, stride, &txfm_param_);
214       API_REGISTER_STATE_CHECK(RunInvTxfm(trans_block, output_block, stride));
215 
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;
222         }
223       }
224     }
225     aom_free(input_block);
226     aom_free(trans_block);
227     aom_free(output_ref_block);
228     aom_free(output_block);
229   }
230 
RunMemCheck()231   void RunMemCheck() {
232     ACMRandom rnd(ACMRandom::DeterministicSeed());
233     const int count_test_block = 5000;
234 
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);
244 
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_;
249       }
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_;
254       }
255 
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_));
259 
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";
269       }
270     }
271     aom_free(input_extreme_block);
272     aom_free(output_ref_block);
273     aom_free(output_block);
274   }
275 
RunInvAccuracyCheck(int limit)276   void RunInvAccuracyCheck(int limit) {
277     ACMRandom rnd(ACMRandom::DeterministicSeed());
278     const int count_test_block = 1000;
279 
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);
292 
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);
299 
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];
311         }
312       }
313 
314       fwd_txfm_ref(in, coeff, pitch_, &txfm_param_);
315 
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_));
321       }
322 
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;
329       }
330     }
331     aom_free(in);
332     aom_free(coeff);
333     aom_free(dst);
334     aom_free(src);
335     aom_free(src16);
336     aom_free(dst16);
337   }
338 
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_;
347 
348  private:
349   //  Assume transform size is 4x4, 8x8, 16x16,...
FindRowLength()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;
360     }
361     return row;
362   }
363 };
364 
365 }  // namespace libaom_test
366 
367 #endif  // AOM_TEST_TRANSFORM_TEST_BASE_H_
368