fdct8x8_test.cc 9.73 KB
Newer Older
Daniel Kang's avatar
Daniel Kang committed
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
/*
 *  Copyright (c) 2012 The WebM project authors. All Rights Reserved.
 *
 *  Use of this source code is governed by a BSD-style license
 *  that can be found in the LICENSE file in the root of the source
 *  tree. An additional intellectual property rights grant can be found
 *  in the file PATENTS.  All contributing project authors may
 *  be found in the AUTHORS file in the root of the source tree.
 */

#include <math.h>
#include <stdlib.h>
#include <string.h>

#include "third_party/googletest/src/include/gtest/gtest.h"
16
#include "test/acm_random.h"
17 18
#include "test/clear_system_state.h"
#include "test/register_state_check.h"
19
#include "test/util.h"
Daniel Kang's avatar
Daniel Kang committed
20 21

extern "C" {
22
#include "vp9/common/vp9_entropy.h"
23
#include "./vp9_rtcd.h"
24
void vp9_idct8x8_64_add_c(const int16_t *input, uint8_t *output, int pitch);
Daniel Kang's avatar
Daniel Kang committed
25 26 27 28 29 30
}
#include "vpx/vpx_integer.h"

using libvpx_test::ACMRandom;

namespace {
31 32 33 34 35 36 37 38
typedef void (*fdct_t)(const int16_t *in, int16_t *out, int stride);
typedef void (*idct_t)(const int16_t *in, uint8_t *out, int stride);
typedef void (*fht_t) (const int16_t *in, int16_t *out, int stride,
                       int tx_type);
typedef void (*iht_t) (const int16_t *in, uint8_t *out, int stride,
                       int tx_type);

void fdct8x8_ref(const int16_t *in, int16_t *out, int stride, int tx_type) {
39
  vp9_fdct8x8_c(in, out, stride);
Jingning Han's avatar
Jingning Han committed
40
}
41

42
void fht8x8_ref(const int16_t *in, int16_t *out, int stride, int tx_type) {
43
  vp9_short_fht8x8_c(in, out, stride, tx_type);
Jingning Han's avatar
Jingning Han committed
44 45
}

46
class FwdTrans8x8TestBase {
Jingning Han's avatar
Jingning Han committed
47
 public:
48
  virtual ~FwdTrans8x8TestBase() {}
Jingning Han's avatar
Jingning Han committed
49 50

 protected:
51 52
  virtual void RunFwdTxfm(int16_t *in, int16_t *out, int stride) = 0;
  virtual void RunInvTxfm(int16_t *out, uint8_t *dst, int stride) = 0;
Jingning Han's avatar
Jingning Han committed
53

54 55 56 57 58 59
  void RunSignBiasCheck() {
    ACMRandom rnd(ACMRandom::DeterministicSeed());
    DECLARE_ALIGNED_ARRAY(16, int16_t, test_input_block, 64);
    DECLARE_ALIGNED_ARRAY(16, int16_t, test_output_block, 64);
    int count_sign_block[64][2];
    const int count_test_block = 100000;
Daniel Kang's avatar
Daniel Kang committed
60

61
    memset(count_sign_block, 0, sizeof(count_sign_block));
Daniel Kang's avatar
Daniel Kang committed
62

63 64 65 66 67 68
    for (int i = 0; i < count_test_block; ++i) {
      // Initialize a test block with input range [-255, 255].
      for (int j = 0; j < 64; ++j)
        test_input_block[j] = rnd.Rand8() - rnd.Rand8();
      REGISTER_STATE_CHECK(
          RunFwdTxfm(test_input_block, test_output_block, pitch_));
Daniel Kang's avatar
Daniel Kang committed
69

70 71 72 73 74 75 76
      for (int j = 0; j < 64; ++j) {
        if (test_output_block[j] < 0)
          ++count_sign_block[j][0];
        else if (test_output_block[j] > 0)
          ++count_sign_block[j][1];
      }
    }
Daniel Kang's avatar
Daniel Kang committed
77 78

    for (int j = 0; j < 64; ++j) {
79 80 81 82 83 84 85 86 87
      const int diff = abs(count_sign_block[j][0] - count_sign_block[j][1]);
      const int max_diff = 1125;
      EXPECT_LT(diff, max_diff)
          << "Error: 8x8 FDCT/FHT has a sign bias > "
          << 1. * max_diff / count_test_block * 100 << "%"
          << " for input range [-255, 255] at index " << j
          << " count0: " << count_sign_block[j][0]
          << " count1: " << count_sign_block[j][1]
          << " diff: " << diff;
Daniel Kang's avatar
Daniel Kang committed
88 89
    }

90
    memset(count_sign_block, 0, sizeof(count_sign_block));
Daniel Kang's avatar
Daniel Kang committed
91

92 93 94 95 96 97
    for (int i = 0; i < count_test_block; ++i) {
      // Initialize a test block with input range [-15, 15].
      for (int j = 0; j < 64; ++j)
        test_input_block[j] = (rnd.Rand8() >> 4) - (rnd.Rand8() >> 4);
      REGISTER_STATE_CHECK(
          RunFwdTxfm(test_input_block, test_output_block, pitch_));
Daniel Kang's avatar
Daniel Kang committed
98

99 100 101 102 103 104 105
      for (int j = 0; j < 64; ++j) {
        if (test_output_block[j] < 0)
          ++count_sign_block[j][0];
        else if (test_output_block[j] > 0)
          ++count_sign_block[j][1];
      }
    }
Daniel Kang's avatar
Daniel Kang committed
106 107

    for (int j = 0; j < 64; ++j) {
108 109 110 111 112 113 114 115 116
      const int diff = abs(count_sign_block[j][0] - count_sign_block[j][1]);
      const int max_diff = 10000;
      EXPECT_LT(diff, max_diff)
          << "Error: 4x4 FDCT/FHT has a sign bias > "
          << 1. * max_diff / count_test_block * 100 << "%"
          << " for input range [-15, 15] at index " << j
          << " count0: " << count_sign_block[j][0]
          << " count1: " << count_sign_block[j][1]
          << " diff: " << diff;
Daniel Kang's avatar
Daniel Kang committed
117 118 119
    }
  }

120 121 122 123 124
  void RunRoundTripErrorCheck() {
    ACMRandom rnd(ACMRandom::DeterministicSeed());
    int max_error = 0;
    int total_error = 0;
    const int count_test_block = 100000;
125 126 127 128
    DECLARE_ALIGNED_ARRAY(16, int16_t, test_input_block, 64);
    DECLARE_ALIGNED_ARRAY(16, int16_t, test_temp_block, 64);
    DECLARE_ALIGNED_ARRAY(16, uint8_t, dst, 64);
    DECLARE_ALIGNED_ARRAY(16, uint8_t, src, 64);
Daniel Kang's avatar
Daniel Kang committed
129

130 131 132 133 134 135 136
    for (int i = 0; i < count_test_block; ++i) {
      // Initialize a test block with input range [-255, 255].
      for (int j = 0; j < 64; ++j) {
        src[j] = rnd.Rand8();
        dst[j] = rnd.Rand8();
        test_input_block[j] = src[j] - dst[j];
      }
Daniel Kang's avatar
Daniel Kang committed
137

138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160
      REGISTER_STATE_CHECK(
          RunFwdTxfm(test_input_block, test_temp_block, pitch_));
      for (int j = 0; j < 64; ++j) {
          if (test_temp_block[j] > 0) {
            test_temp_block[j] += 2;
            test_temp_block[j] /= 4;
            test_temp_block[j] *= 4;
          } else {
            test_temp_block[j] -= 2;
            test_temp_block[j] /= 4;
            test_temp_block[j] *= 4;
          }
      }
      REGISTER_STATE_CHECK(
          RunInvTxfm(test_temp_block, dst, pitch_));

      for (int j = 0; j < 64; ++j) {
        const int diff = dst[j] - src[j];
        const int error = diff * diff;
        if (max_error < error)
          max_error = error;
        total_error += error;
      }
Daniel Kang's avatar
Daniel Kang committed
161 162
    }

163 164 165
    EXPECT_GE(1, max_error)
      << "Error: 8x8 FDCT/IDCT or FHT/IHT has an individual"
      << " roundtrip error > 1";
Daniel Kang's avatar
Daniel Kang committed
166

167 168 169 170
    EXPECT_GE(count_test_block/5, total_error)
      << "Error: 8x8 FDCT/IDCT or FHT/IHT has average roundtrip "
      << "error > 1/5 per block";
  }
Daniel Kang's avatar
Daniel Kang committed
171

172 173 174 175 176
  void RunExtremalCheck() {
    ACMRandom rnd(ACMRandom::DeterministicSeed());
    int max_error = 0;
    int total_error = 0;
    const int count_test_block = 100000;
177 178 179 180
    DECLARE_ALIGNED_ARRAY(16, int16_t, test_input_block, 64);
    DECLARE_ALIGNED_ARRAY(16, int16_t, test_temp_block, 64);
    DECLARE_ALIGNED_ARRAY(16, uint8_t, dst, 64);
    DECLARE_ALIGNED_ARRAY(16, uint8_t, src, 64);
Daniel Kang's avatar
Daniel Kang committed
181

182 183 184 185 186 187 188
    for (int i = 0; i < count_test_block; ++i) {
      // Initialize a test block with input range [-255, 255].
      for (int j = 0; j < 64; ++j) {
        src[j] = rnd.Rand8() % 2 ? 255 : 0;
        dst[j] = src[j] > 0 ? 0 : 255;
        test_input_block[j] = src[j] - dst[j];
      }
Daniel Kang's avatar
Daniel Kang committed
189

190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209
      REGISTER_STATE_CHECK(
          RunFwdTxfm(test_input_block, test_temp_block, pitch_));
      REGISTER_STATE_CHECK(
          RunInvTxfm(test_temp_block, dst, pitch_));

      for (int j = 0; j < 64; ++j) {
        const int diff = dst[j] - src[j];
        const int error = diff * diff;
        if (max_error < error)
          max_error = error;
        total_error += error;
      }

      EXPECT_GE(1, max_error)
          << "Error: Extremal 8x8 FDCT/IDCT or FHT/IHT has"
          << "an individual roundtrip error > 1";

      EXPECT_GE(count_test_block/5, total_error)
          << "Error: Extremal 8x8 FDCT/IDCT or FHT/IHT has average"
          << " roundtrip error > 1/5 per block";
Daniel Kang's avatar
Daniel Kang committed
210
    }
211
  }
Daniel Kang's avatar
Daniel Kang committed
212

213 214 215 216
  int pitch_;
  int tx_type_;
  fht_t fwd_txfm_ref;
};
Daniel Kang's avatar
Daniel Kang committed
217

218 219 220 221 222 223 224 225 226
class FwdTrans8x8DCT : public FwdTrans8x8TestBase,
                       public PARAMS(fdct_t, idct_t, int) {
 public:
  virtual ~FwdTrans8x8DCT() {}

  virtual void SetUp() {
    fwd_txfm_ = GET_PARAM(0);
    inv_txfm_ = GET_PARAM(1);
    tx_type_  = GET_PARAM(2);
227
    pitch_    = 8;
228 229 230 231 232 233 234 235 236 237
    fwd_txfm_ref = fdct8x8_ref;
  }

  virtual void TearDown() { libvpx_test::ClearSystemState(); }

 protected:
  void RunFwdTxfm(int16_t *in, int16_t *out, int stride) {
    fwd_txfm_(in, out, stride);
  }
  void RunInvTxfm(int16_t *out, uint8_t *dst, int stride) {
238
    inv_txfm_(out, dst, stride);
Daniel Kang's avatar
Daniel Kang committed
239
  }
240 241 242 243 244 245 246

  fdct_t fwd_txfm_;
  idct_t inv_txfm_;
};

TEST_P(FwdTrans8x8DCT, SignBiasCheck) {
  RunSignBiasCheck();
Jingning Han's avatar
Jingning Han committed
247
}
Daniel Kang's avatar
Daniel Kang committed
248

249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300
TEST_P(FwdTrans8x8DCT, RoundTripErrorCheck) {
  RunRoundTripErrorCheck();
}

TEST_P(FwdTrans8x8DCT, ExtremalCheck) {
  RunExtremalCheck();
}

class FwdTrans8x8HT : public FwdTrans8x8TestBase,
                      public PARAMS(fht_t, iht_t, int) {
 public:
  virtual ~FwdTrans8x8HT() {}

  virtual void SetUp() {
    fwd_txfm_ = GET_PARAM(0);
    inv_txfm_ = GET_PARAM(1);
    tx_type_  = GET_PARAM(2);
    pitch_    = 8;
    fwd_txfm_ref = fht8x8_ref;
  }

  virtual void TearDown() { libvpx_test::ClearSystemState(); }

 protected:
  void RunFwdTxfm(int16_t *in, int16_t *out, int stride) {
    fwd_txfm_(in, out, stride, tx_type_);
  }
  void RunInvTxfm(int16_t *out, uint8_t *dst, int stride) {
    inv_txfm_(out, dst, stride, tx_type_);
  }

  fht_t fwd_txfm_;
  iht_t inv_txfm_;
};

TEST_P(FwdTrans8x8HT, SignBiasCheck) {
  RunSignBiasCheck();
}

TEST_P(FwdTrans8x8HT, RoundTripErrorCheck) {
  RunRoundTripErrorCheck();
}

TEST_P(FwdTrans8x8HT, ExtremalCheck) {
  RunExtremalCheck();
}

using std::tr1::make_tuple;

INSTANTIATE_TEST_CASE_P(
    C, FwdTrans8x8DCT,
    ::testing::Values(
301
        make_tuple(&vp9_fdct8x8_c, &vp9_idct8x8_64_add_c, 0)));
302 303 304
INSTANTIATE_TEST_CASE_P(
    C, FwdTrans8x8HT,
    ::testing::Values(
305 306 307 308
        make_tuple(&vp9_short_fht8x8_c, &vp9_iht8x8_64_add_c, 0),
        make_tuple(&vp9_short_fht8x8_c, &vp9_iht8x8_64_add_c, 1),
        make_tuple(&vp9_short_fht8x8_c, &vp9_iht8x8_64_add_c, 2),
        make_tuple(&vp9_short_fht8x8_c, &vp9_iht8x8_64_add_c, 3)));
309 310 311 312 313

#if HAVE_SSE2
INSTANTIATE_TEST_CASE_P(
    SSE2, FwdTrans8x8DCT,
    ::testing::Values(
314
        make_tuple(&vp9_fdct8x8_sse2, &vp9_idct8x8_64_add_sse2, 0)));
315 316 317
INSTANTIATE_TEST_CASE_P(
    SSE2, FwdTrans8x8HT,
    ::testing::Values(
318 319 320 321
        make_tuple(&vp9_short_fht8x8_sse2, &vp9_iht8x8_64_add_sse2, 0),
        make_tuple(&vp9_short_fht8x8_sse2, &vp9_iht8x8_64_add_sse2, 1),
        make_tuple(&vp9_short_fht8x8_sse2, &vp9_iht8x8_64_add_sse2, 2),
        make_tuple(&vp9_short_fht8x8_sse2, &vp9_iht8x8_64_add_sse2, 3)));
322
#endif
Daniel Kang's avatar
Daniel Kang committed
323
}  // namespace