av1_inv_txfm_test.cc 9.48 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
/*
 *  Copyright (c) 2013 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"

Yaowu Xu's avatar
Yaowu Xu committed
17
#include "./av1_rtcd.h"
Adrian Grange's avatar
Adrian Grange committed
18
#include "./aom_dsp_rtcd.h"
19 20 21 22
#include "test/acm_random.h"
#include "test/clear_system_state.h"
#include "test/register_state_check.h"
#include "test/util.h"
Yaowu Xu's avatar
Yaowu Xu committed
23 24
#include "av1/common/blockd.h"
#include "av1/common/scan.h"
Adrian Grange's avatar
Adrian Grange committed
25
#include "aom/aom_integer.h"
Yaowu Xu's avatar
Yaowu Xu committed
26
#include "av1/common/av1_inv_txfm.h"
27

Yaowu Xu's avatar
Yaowu Xu committed
28
using libaom_test::ACMRandom;
29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54

namespace {
const double PI = 3.141592653589793238462643383279502884;
const double kInvSqrt2 = 0.707106781186547524400844362104;

void reference_idct_1d(const double *in, double *out, int size) {
  for (int n = 0; n < size; ++n) {
    out[n] = 0;
    for (int k = 0; k < size; ++k) {
      if (k == 0)
        out[n] += kInvSqrt2 * in[k] * cos(PI * (2 * n + 1) * k / (2 * size));
      else
        out[n] += in[k] * cos(PI * (2 * n + 1) * k / (2 * size));
    }
  }
}

typedef void (*IdctFuncRef)(const double *in, double *out, int size);
typedef void (*IdctFunc)(const tran_low_t *in, tran_low_t *out);

class TransTestBase {
 public:
  virtual ~TransTestBase() {}

 protected:
  void RunInvAccuracyCheck() {
clang-format's avatar
clang-format committed
55
    tran_low_t *input = new tran_low_t[txfm_size_];
56
    tran_low_t *output = new tran_low_t[txfm_size_];
clang-format's avatar
clang-format committed
57
    double *ref_input = new double[txfm_size_];
58 59 60 61
    double *ref_output = new double[txfm_size_];

    ACMRandom rnd(ACMRandom::DeterministicSeed());
    const int count_test_block = 5000;
clang-format's avatar
clang-format committed
62
    for (int ti = 0; ti < count_test_block; ++ti) {
63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90
      for (int ni = 0; ni < txfm_size_; ++ni) {
        input[ni] = rnd.Rand8() - rnd.Rand8();
        ref_input[ni] = static_cast<double>(input[ni]);
      }

      fwd_txfm_(input, output);
      fwd_txfm_ref_(ref_input, ref_output, txfm_size_);

      for (int ni = 0; ni < txfm_size_; ++ni) {
        EXPECT_LE(
            abs(output[ni] - static_cast<tran_low_t>(round(ref_output[ni]))),
            max_error_);
      }
    }

    delete[] input;
    delete[] output;
    delete[] ref_input;
    delete[] ref_output;
  }

  double max_error_;
  int txfm_size_;
  IdctFunc fwd_txfm_;
  IdctFuncRef fwd_txfm_ref_;
};

typedef std::tr1::tuple<IdctFunc, IdctFuncRef, int, int> IdctParam;
clang-format's avatar
clang-format committed
91 92
class Vp10InvTxfm : public TransTestBase,
                    public ::testing::TestWithParam<IdctParam> {
93 94 95 96 97 98 99 100 101 102
 public:
  virtual void SetUp() {
    fwd_txfm_ = GET_PARAM(0);
    fwd_txfm_ref_ = GET_PARAM(1);
    txfm_size_ = GET_PARAM(2);
    max_error_ = GET_PARAM(3);
  }
  virtual void TearDown() {}
};

clang-format's avatar
clang-format committed
103
TEST_P(Vp10InvTxfm, RunInvAccuracyCheck) { RunInvAccuracyCheck(); }
104 105 106

INSTANTIATE_TEST_CASE_P(
    C, Vp10InvTxfm,
clang-format's avatar
clang-format committed
107 108 109 110
    ::testing::Values(IdctParam(&vp10_idct4_c, &reference_idct_1d, 4, 1),
                      IdctParam(&vp10_idct8_c, &reference_idct_1d, 8, 2),
                      IdctParam(&vp10_idct16_c, &reference_idct_1d, 16, 4),
                      IdctParam(&vp10_idct32_c, &reference_idct_1d, 32, 6)));
111 112 113

typedef void (*FwdTxfmFunc)(const int16_t *in, tran_low_t *out, int stride);
typedef void (*InvTxfmFunc)(const tran_low_t *in, uint8_t *out, int stride);
clang-format's avatar
clang-format committed
114 115
typedef std::tr1::tuple<FwdTxfmFunc, InvTxfmFunc, InvTxfmFunc, TX_SIZE, int>
    PartialInvTxfmParam;
116 117 118 119 120 121 122 123 124
const int kMaxNumCoeffs = 1024;
class Vp10PartialIDctTest
    : public ::testing::TestWithParam<PartialInvTxfmParam> {
 public:
  virtual ~Vp10PartialIDctTest() {}
  virtual void SetUp() {
    ftxfm_ = GET_PARAM(0);
    full_itxfm_ = GET_PARAM(1);
    partial_itxfm_ = GET_PARAM(2);
clang-format's avatar
clang-format committed
125
    tx_size_ = GET_PARAM(3);
126 127 128
    last_nonzero_ = GET_PARAM(4);
  }

Yaowu Xu's avatar
Yaowu Xu committed
129
  virtual void TearDown() { libaom_test::ClearSystemState(); }
130 131 132 133 134 135 136 137 138 139 140 141 142

 protected:
  int last_nonzero_;
  TX_SIZE tx_size_;
  FwdTxfmFunc ftxfm_;
  InvTxfmFunc full_itxfm_;
  InvTxfmFunc partial_itxfm_;
};

TEST_P(Vp10PartialIDctTest, RunQuantCheck) {
  ACMRandom rnd(ACMRandom::DeterministicSeed());
  int size;
  switch (tx_size_) {
clang-format's avatar
clang-format committed
143 144 145 146 147
    case TX_4X4: size = 4; break;
    case TX_8X8: size = 8; break;
    case TX_16X16: size = 16; break;
    case TX_32X32: size = 32; break;
    default: FAIL() << "Wrong Size!"; break;
148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172
  }
  DECLARE_ALIGNED(16, tran_low_t, test_coef_block1[kMaxNumCoeffs]);
  DECLARE_ALIGNED(16, tran_low_t, test_coef_block2[kMaxNumCoeffs]);
  DECLARE_ALIGNED(16, uint8_t, dst1[kMaxNumCoeffs]);
  DECLARE_ALIGNED(16, uint8_t, dst2[kMaxNumCoeffs]);

  const int count_test_block = 1000;
  const int block_size = size * size;

  DECLARE_ALIGNED(16, int16_t, input_extreme_block[kMaxNumCoeffs]);
  DECLARE_ALIGNED(16, tran_low_t, output_ref_block[kMaxNumCoeffs]);

  int max_error = 0;
  for (int i = 0; i < count_test_block; ++i) {
    // clear out destination buffer
    memset(dst1, 0, sizeof(*dst1) * block_size);
    memset(dst2, 0, sizeof(*dst2) * block_size);
    memset(test_coef_block1, 0, sizeof(*test_coef_block1) * block_size);
    memset(test_coef_block2, 0, sizeof(*test_coef_block2) * block_size);

    ACMRandom rnd(ACMRandom::DeterministicSeed());

    for (int i = 0; i < count_test_block; ++i) {
      // Initialize a test block with input range [-255, 255].
      if (i == 0) {
clang-format's avatar
clang-format committed
173
        for (int j = 0; j < block_size; ++j) input_extreme_block[j] = 255;
174
      } else if (i == 1) {
clang-format's avatar
clang-format committed
175
        for (int j = 0; j < block_size; ++j) input_extreme_block[j] = -255;
176 177 178 179 180 181 182 183 184 185 186
      } else {
        for (int j = 0; j < block_size; ++j) {
          input_extreme_block[j] = rnd.Rand8() % 2 ? 255 : -255;
        }
      }

      ftxfm_(input_extreme_block, output_ref_block, size);

      // quantization with maximum allowed step sizes
      test_coef_block1[0] = (output_ref_block[0] / 1336) * 1336;
      for (int j = 1; j < last_nonzero_; ++j)
clang-format's avatar
clang-format committed
187 188
        test_coef_block1[vp10_default_scan_orders[tx_size_].scan[j]] =
            (output_ref_block[j] / 1828) * 1828;
189 190 191 192 193 194 195 196
    }

    ASM_REGISTER_STATE_CHECK(full_itxfm_(test_coef_block1, dst1, size));
    ASM_REGISTER_STATE_CHECK(partial_itxfm_(test_coef_block1, dst2, size));

    for (int j = 0; j < block_size; ++j) {
      const int diff = dst1[j] - dst2[j];
      const int error = diff * diff;
clang-format's avatar
clang-format committed
197
      if (max_error < error) max_error = error;
198 199 200 201 202 203 204 205 206 207 208
    }
  }

  EXPECT_EQ(0, max_error)
      << "Error: partial inverse transform produces different results";
}

TEST_P(Vp10PartialIDctTest, ResultsMatch) {
  ACMRandom rnd(ACMRandom::DeterministicSeed());
  int size;
  switch (tx_size_) {
clang-format's avatar
clang-format committed
209 210 211 212 213
    case TX_4X4: size = 4; break;
    case TX_8X8: size = 8; break;
    case TX_16X16: size = 16; break;
    case TX_32X32: size = 32; break;
    default: FAIL() << "Wrong Size!"; break;
214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249
  }
  DECLARE_ALIGNED(16, tran_low_t, test_coef_block1[kMaxNumCoeffs]);
  DECLARE_ALIGNED(16, tran_low_t, test_coef_block2[kMaxNumCoeffs]);
  DECLARE_ALIGNED(16, uint8_t, dst1[kMaxNumCoeffs]);
  DECLARE_ALIGNED(16, uint8_t, dst2[kMaxNumCoeffs]);
  const int count_test_block = 1000;
  const int max_coeff = 32766 / 4;
  const int block_size = size * size;
  int max_error = 0;
  for (int i = 0; i < count_test_block; ++i) {
    // clear out destination buffer
    memset(dst1, 0, sizeof(*dst1) * block_size);
    memset(dst2, 0, sizeof(*dst2) * block_size);
    memset(test_coef_block1, 0, sizeof(*test_coef_block1) * block_size);
    memset(test_coef_block2, 0, sizeof(*test_coef_block2) * block_size);
    int max_energy_leftover = max_coeff * max_coeff;
    for (int j = 0; j < last_nonzero_; ++j) {
      int16_t coef = static_cast<int16_t>(sqrt(1.0 * max_energy_leftover) *
                                          (rnd.Rand16() - 32768) / 65536);
      max_energy_leftover -= coef * coef;
      if (max_energy_leftover < 0) {
        max_energy_leftover = 0;
        coef = 0;
      }
      test_coef_block1[vp10_default_scan_orders[tx_size_].scan[j]] = coef;
    }

    memcpy(test_coef_block2, test_coef_block1,
           sizeof(*test_coef_block2) * block_size);

    ASM_REGISTER_STATE_CHECK(full_itxfm_(test_coef_block1, dst1, size));
    ASM_REGISTER_STATE_CHECK(partial_itxfm_(test_coef_block2, dst2, size));

    for (int j = 0; j < block_size; ++j) {
      const int diff = dst1[j] - dst2[j];
      const int error = diff * diff;
clang-format's avatar
clang-format committed
250
      if (max_error < error) max_error = error;
251 252 253 254 255 256 257 258 259 260
    }
  }

  EXPECT_EQ(0, max_error)
      << "Error: partial inverse transform produces different results";
}
using std::tr1::make_tuple;

INSTANTIATE_TEST_CASE_P(
    C, Vp10PartialIDctTest,
Adrian Grange's avatar
Adrian Grange committed
261
    ::testing::Values(make_tuple(&aom_fdct32x32_c, &vp10_idct32x32_1024_add_c,
clang-format's avatar
clang-format committed
262
                                 &vp10_idct32x32_34_add_c, TX_32X32, 34),
Adrian Grange's avatar
Adrian Grange committed
263
                      make_tuple(&aom_fdct32x32_c, &vp10_idct32x32_1024_add_c,
clang-format's avatar
clang-format committed
264
                                 &vp10_idct32x32_1_add_c, TX_32X32, 1),
Adrian Grange's avatar
Adrian Grange committed
265
                      make_tuple(&aom_fdct16x16_c, &vp10_idct16x16_256_add_c,
clang-format's avatar
clang-format committed
266
                                 &vp10_idct16x16_10_add_c, TX_16X16, 10),
Adrian Grange's avatar
Adrian Grange committed
267
                      make_tuple(&aom_fdct16x16_c, &vp10_idct16x16_256_add_c,
clang-format's avatar
clang-format committed
268
                                 &vp10_idct16x16_1_add_c, TX_16X16, 1),
Adrian Grange's avatar
Adrian Grange committed
269
                      make_tuple(&aom_fdct8x8_c, &vp10_idct8x8_64_add_c,
clang-format's avatar
clang-format committed
270
                                 &vp10_idct8x8_12_add_c, TX_8X8, 12),
Adrian Grange's avatar
Adrian Grange committed
271
                      make_tuple(&aom_fdct8x8_c, &vp10_idct8x8_64_add_c,
clang-format's avatar
clang-format committed
272
                                 &vp10_idct8x8_1_add_c, TX_8X8, 1),
Adrian Grange's avatar
Adrian Grange committed
273
                      make_tuple(&aom_fdct4x4_c, &vp10_idct4x4_16_add_c,
clang-format's avatar
clang-format committed
274
                                 &vp10_idct4x4_1_add_c, TX_4X4, 1)));
275
}  // namespace