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