Home | History | Annotate | Download | only in test
      1 /*
      2  *  Copyright (c) 2012 The WebM project authors. All Rights Reserved.
      3  *
      4  *  Use of this source code is governed by a BSD-style license
      5  *  that can be found in the LICENSE file in the root of the source
      6  *  tree. An additional intellectual property rights grant can be found
      7  *  in the file PATENTS.  All contributing project authors may
      8  *  be found in the AUTHORS file in the root of the source tree.
      9  */
     10 
     11 #include <math.h>
     12 #include <stdlib.h>
     13 #include <string.h>
     14 
     15 #include "third_party/googletest/src/include/gtest/gtest.h"
     16 #include "test/acm_random.h"
     17 #include "test/clear_system_state.h"
     18 #include "test/register_state_check.h"
     19 #include "test/util.h"
     20 
     21 #include "./vp9_rtcd.h"
     22 #include "vp9/common/vp9_entropy.h"
     23 #include "vpx/vpx_integer.h"
     24 
     25 extern "C" {
     26 void vp9_idct8x8_64_add_c(const int16_t *input, uint8_t *output, int pitch);
     27 }
     28 
     29 using libvpx_test::ACMRandom;
     30 
     31 namespace {
     32 typedef void (*fdct_t)(const int16_t *in, int16_t *out, int stride);
     33 typedef void (*idct_t)(const int16_t *in, uint8_t *out, int stride);
     34 typedef void (*fht_t) (const int16_t *in, int16_t *out, int stride,
     35                        int tx_type);
     36 typedef void (*iht_t) (const int16_t *in, uint8_t *out, int stride,
     37                        int tx_type);
     38 
     39 typedef std::tr1::tuple<fdct_t, idct_t, int> dct_8x8_param_t;
     40 typedef std::tr1::tuple<fht_t, iht_t, int> ht_8x8_param_t;
     41 
     42 void fdct8x8_ref(const int16_t *in, int16_t *out, int stride, int tx_type) {
     43   vp9_fdct8x8_c(in, out, stride);
     44 }
     45 
     46 void fht8x8_ref(const int16_t *in, int16_t *out, int stride, int tx_type) {
     47   vp9_fht8x8_c(in, out, stride, tx_type);
     48 }
     49 
     50 class FwdTrans8x8TestBase {
     51  public:
     52   virtual ~FwdTrans8x8TestBase() {}
     53 
     54  protected:
     55   virtual void RunFwdTxfm(int16_t *in, int16_t *out, int stride) = 0;
     56   virtual void RunInvTxfm(int16_t *out, uint8_t *dst, int stride) = 0;
     57 
     58   void RunSignBiasCheck() {
     59     ACMRandom rnd(ACMRandom::DeterministicSeed());
     60     DECLARE_ALIGNED_ARRAY(16, int16_t, test_input_block, 64);
     61     DECLARE_ALIGNED_ARRAY(16, int16_t, test_output_block, 64);
     62     int count_sign_block[64][2];
     63     const int count_test_block = 100000;
     64 
     65     memset(count_sign_block, 0, sizeof(count_sign_block));
     66 
     67     for (int i = 0; i < count_test_block; ++i) {
     68       // Initialize a test block with input range [-255, 255].
     69       for (int j = 0; j < 64; ++j)
     70         test_input_block[j] = rnd.Rand8() - rnd.Rand8();
     71       REGISTER_STATE_CHECK(
     72           RunFwdTxfm(test_input_block, test_output_block, pitch_));
     73 
     74       for (int j = 0; j < 64; ++j) {
     75         if (test_output_block[j] < 0)
     76           ++count_sign_block[j][0];
     77         else if (test_output_block[j] > 0)
     78           ++count_sign_block[j][1];
     79       }
     80     }
     81 
     82     for (int j = 0; j < 64; ++j) {
     83       const int diff = abs(count_sign_block[j][0] - count_sign_block[j][1]);
     84       const int max_diff = 1125;
     85       EXPECT_LT(diff, max_diff)
     86           << "Error: 8x8 FDCT/FHT has a sign bias > "
     87           << 1. * max_diff / count_test_block * 100 << "%"
     88           << " for input range [-255, 255] at index " << j
     89           << " count0: " << count_sign_block[j][0]
     90           << " count1: " << count_sign_block[j][1]
     91           << " diff: " << diff;
     92     }
     93 
     94     memset(count_sign_block, 0, sizeof(count_sign_block));
     95 
     96     for (int i = 0; i < count_test_block; ++i) {
     97       // Initialize a test block with input range [-15, 15].
     98       for (int j = 0; j < 64; ++j)
     99         test_input_block[j] = (rnd.Rand8() >> 4) - (rnd.Rand8() >> 4);
    100       REGISTER_STATE_CHECK(
    101           RunFwdTxfm(test_input_block, test_output_block, pitch_));
    102 
    103       for (int j = 0; j < 64; ++j) {
    104         if (test_output_block[j] < 0)
    105           ++count_sign_block[j][0];
    106         else if (test_output_block[j] > 0)
    107           ++count_sign_block[j][1];
    108       }
    109     }
    110 
    111     for (int j = 0; j < 64; ++j) {
    112       const int diff = abs(count_sign_block[j][0] - count_sign_block[j][1]);
    113       const int max_diff = 10000;
    114       EXPECT_LT(diff, max_diff)
    115           << "Error: 4x4 FDCT/FHT has a sign bias > "
    116           << 1. * max_diff / count_test_block * 100 << "%"
    117           << " for input range [-15, 15] at index " << j
    118           << " count0: " << count_sign_block[j][0]
    119           << " count1: " << count_sign_block[j][1]
    120           << " diff: " << diff;
    121     }
    122   }
    123 
    124   void RunRoundTripErrorCheck() {
    125     ACMRandom rnd(ACMRandom::DeterministicSeed());
    126     int max_error = 0;
    127     int total_error = 0;
    128     const int count_test_block = 100000;
    129     DECLARE_ALIGNED_ARRAY(16, int16_t, test_input_block, 64);
    130     DECLARE_ALIGNED_ARRAY(16, int16_t, test_temp_block, 64);
    131     DECLARE_ALIGNED_ARRAY(16, uint8_t, dst, 64);
    132     DECLARE_ALIGNED_ARRAY(16, uint8_t, src, 64);
    133 
    134     for (int i = 0; i < count_test_block; ++i) {
    135       // Initialize a test block with input range [-255, 255].
    136       for (int j = 0; j < 64; ++j) {
    137         src[j] = rnd.Rand8();
    138         dst[j] = rnd.Rand8();
    139         test_input_block[j] = src[j] - dst[j];
    140       }
    141 
    142       REGISTER_STATE_CHECK(
    143           RunFwdTxfm(test_input_block, test_temp_block, pitch_));
    144       for (int j = 0; j < 64; ++j) {
    145           if (test_temp_block[j] > 0) {
    146             test_temp_block[j] += 2;
    147             test_temp_block[j] /= 4;
    148             test_temp_block[j] *= 4;
    149           } else {
    150             test_temp_block[j] -= 2;
    151             test_temp_block[j] /= 4;
    152             test_temp_block[j] *= 4;
    153           }
    154       }
    155       REGISTER_STATE_CHECK(
    156           RunInvTxfm(test_temp_block, dst, pitch_));
    157 
    158       for (int j = 0; j < 64; ++j) {
    159         const int diff = dst[j] - src[j];
    160         const int error = diff * diff;
    161         if (max_error < error)
    162           max_error = error;
    163         total_error += error;
    164       }
    165     }
    166 
    167     EXPECT_GE(1, max_error)
    168       << "Error: 8x8 FDCT/IDCT or FHT/IHT has an individual"
    169       << " roundtrip error > 1";
    170 
    171     EXPECT_GE(count_test_block/5, total_error)
    172       << "Error: 8x8 FDCT/IDCT or FHT/IHT has average roundtrip "
    173       << "error > 1/5 per block";
    174   }
    175 
    176   void RunExtremalCheck() {
    177     ACMRandom rnd(ACMRandom::DeterministicSeed());
    178     int max_error = 0;
    179     int total_error = 0;
    180     const int count_test_block = 100000;
    181     DECLARE_ALIGNED_ARRAY(16, int16_t, test_input_block, 64);
    182     DECLARE_ALIGNED_ARRAY(16, int16_t, test_temp_block, 64);
    183     DECLARE_ALIGNED_ARRAY(16, uint8_t, dst, 64);
    184     DECLARE_ALIGNED_ARRAY(16, uint8_t, src, 64);
    185 
    186     for (int i = 0; i < count_test_block; ++i) {
    187       // Initialize a test block with input range [-255, 255].
    188       for (int j = 0; j < 64; ++j) {
    189         src[j] = rnd.Rand8() % 2 ? 255 : 0;
    190         dst[j] = src[j] > 0 ? 0 : 255;
    191         test_input_block[j] = src[j] - dst[j];
    192       }
    193 
    194       REGISTER_STATE_CHECK(
    195           RunFwdTxfm(test_input_block, test_temp_block, pitch_));
    196       REGISTER_STATE_CHECK(
    197           RunInvTxfm(test_temp_block, dst, pitch_));
    198 
    199       for (int j = 0; j < 64; ++j) {
    200         const int diff = dst[j] - src[j];
    201         const int error = diff * diff;
    202         if (max_error < error)
    203           max_error = error;
    204         total_error += error;
    205       }
    206 
    207       EXPECT_GE(1, max_error)
    208           << "Error: Extremal 8x8 FDCT/IDCT or FHT/IHT has"
    209           << "an individual roundtrip error > 1";
    210 
    211       EXPECT_GE(count_test_block/5, total_error)
    212           << "Error: Extremal 8x8 FDCT/IDCT or FHT/IHT has average"
    213           << " roundtrip error > 1/5 per block";
    214     }
    215   }
    216 
    217   int pitch_;
    218   int tx_type_;
    219   fht_t fwd_txfm_ref;
    220 };
    221 
    222 class FwdTrans8x8DCT
    223     : public FwdTrans8x8TestBase,
    224       public ::testing::TestWithParam<dct_8x8_param_t> {
    225  public:
    226   virtual ~FwdTrans8x8DCT() {}
    227 
    228   virtual void SetUp() {
    229     fwd_txfm_ = GET_PARAM(0);
    230     inv_txfm_ = GET_PARAM(1);
    231     tx_type_  = GET_PARAM(2);
    232     pitch_    = 8;
    233     fwd_txfm_ref = fdct8x8_ref;
    234   }
    235 
    236   virtual void TearDown() { libvpx_test::ClearSystemState(); }
    237 
    238  protected:
    239   void RunFwdTxfm(int16_t *in, int16_t *out, int stride) {
    240     fwd_txfm_(in, out, stride);
    241   }
    242   void RunInvTxfm(int16_t *out, uint8_t *dst, int stride) {
    243     inv_txfm_(out, dst, stride);
    244   }
    245 
    246   fdct_t fwd_txfm_;
    247   idct_t inv_txfm_;
    248 };
    249 
    250 TEST_P(FwdTrans8x8DCT, SignBiasCheck) {
    251   RunSignBiasCheck();
    252 }
    253 
    254 TEST_P(FwdTrans8x8DCT, RoundTripErrorCheck) {
    255   RunRoundTripErrorCheck();
    256 }
    257 
    258 TEST_P(FwdTrans8x8DCT, ExtremalCheck) {
    259   RunExtremalCheck();
    260 }
    261 
    262 class FwdTrans8x8HT
    263     : public FwdTrans8x8TestBase,
    264       public ::testing::TestWithParam<ht_8x8_param_t> {
    265  public:
    266   virtual ~FwdTrans8x8HT() {}
    267 
    268   virtual void SetUp() {
    269     fwd_txfm_ = GET_PARAM(0);
    270     inv_txfm_ = GET_PARAM(1);
    271     tx_type_  = GET_PARAM(2);
    272     pitch_    = 8;
    273     fwd_txfm_ref = fht8x8_ref;
    274   }
    275 
    276   virtual void TearDown() { libvpx_test::ClearSystemState(); }
    277 
    278  protected:
    279   void RunFwdTxfm(int16_t *in, int16_t *out, int stride) {
    280     fwd_txfm_(in, out, stride, tx_type_);
    281   }
    282   void RunInvTxfm(int16_t *out, uint8_t *dst, int stride) {
    283     inv_txfm_(out, dst, stride, tx_type_);
    284   }
    285 
    286   fht_t fwd_txfm_;
    287   iht_t inv_txfm_;
    288 };
    289 
    290 TEST_P(FwdTrans8x8HT, SignBiasCheck) {
    291   RunSignBiasCheck();
    292 }
    293 
    294 TEST_P(FwdTrans8x8HT, RoundTripErrorCheck) {
    295   RunRoundTripErrorCheck();
    296 }
    297 
    298 TEST_P(FwdTrans8x8HT, ExtremalCheck) {
    299   RunExtremalCheck();
    300 }
    301 
    302 using std::tr1::make_tuple;
    303 
    304 INSTANTIATE_TEST_CASE_P(
    305     C, FwdTrans8x8DCT,
    306     ::testing::Values(
    307         make_tuple(&vp9_fdct8x8_c, &vp9_idct8x8_64_add_c, 0)));
    308 INSTANTIATE_TEST_CASE_P(
    309     C, FwdTrans8x8HT,
    310     ::testing::Values(
    311         make_tuple(&vp9_fht8x8_c, &vp9_iht8x8_64_add_c, 0),
    312         make_tuple(&vp9_fht8x8_c, &vp9_iht8x8_64_add_c, 1),
    313         make_tuple(&vp9_fht8x8_c, &vp9_iht8x8_64_add_c, 2),
    314         make_tuple(&vp9_fht8x8_c, &vp9_iht8x8_64_add_c, 3)));
    315 
    316 #if HAVE_NEON
    317 INSTANTIATE_TEST_CASE_P(
    318     NEON, FwdTrans8x8DCT,
    319     ::testing::Values(
    320         make_tuple(&vp9_fdct8x8_c, &vp9_idct8x8_64_add_neon, 0)));
    321 INSTANTIATE_TEST_CASE_P(
    322     DISABLED_NEON, FwdTrans8x8HT,
    323     ::testing::Values(
    324         make_tuple(&vp9_fht8x8_c, &vp9_iht8x8_64_add_neon, 0),
    325         make_tuple(&vp9_fht8x8_c, &vp9_iht8x8_64_add_neon, 1),
    326         make_tuple(&vp9_fht8x8_c, &vp9_iht8x8_64_add_neon, 2),
    327         make_tuple(&vp9_fht8x8_c, &vp9_iht8x8_64_add_neon, 3)));
    328 #endif
    329 
    330 #if HAVE_SSE2
    331 INSTANTIATE_TEST_CASE_P(
    332     SSE2, FwdTrans8x8DCT,
    333     ::testing::Values(
    334         make_tuple(&vp9_fdct8x8_sse2, &vp9_idct8x8_64_add_sse2, 0)));
    335 INSTANTIATE_TEST_CASE_P(
    336     SSE2, FwdTrans8x8HT,
    337     ::testing::Values(
    338         make_tuple(&vp9_fht8x8_sse2, &vp9_iht8x8_64_add_sse2, 0),
    339         make_tuple(&vp9_fht8x8_sse2, &vp9_iht8x8_64_add_sse2, 1),
    340         make_tuple(&vp9_fht8x8_sse2, &vp9_iht8x8_64_add_sse2, 2),
    341         make_tuple(&vp9_fht8x8_sse2, &vp9_iht8x8_64_add_sse2, 3)));
    342 #endif
    343 }  // namespace
    344