dct32x32_test.cc 8.64 KB
Newer Older
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
17
18
19
#include "test/acm_random.h"
#include "test/clear_system_state.h"
#include "test/register_state_check.h"
#include "test/util.h"
20
21

extern "C" {
22
#include "./vpx_config.h"
23
24
25
26
27
28
29
30
31
#include "vp9/common/vp9_entropy.h"
#include "./vp9_rtcd.h"
}

#include "vpx/vpx_integer.h"

using libvpx_test::ACMRandom;

namespace {
Yaowu Xu's avatar
Yaowu Xu committed
32
33
34
#ifdef _MSC_VER
static int round(double x) {
  if (x < 0)
Yaowu Xu's avatar
Yaowu Xu committed
35
    return static_cast<int>(ceil(x - 0.5));
Yaowu Xu's avatar
Yaowu Xu committed
36
  else
Yaowu Xu's avatar
Yaowu Xu committed
37
    return static_cast<int>(floor(x + 0.5));
Yaowu Xu's avatar
Yaowu Xu committed
38
39
}
#endif
40

41
42
43
const int kNumCoeffs = 1024;
const double kPi = 3.141592653589793238462643383279502884;
void reference_32x32_dct_1d(const double in[32], double out[32], int stride) {
44
45
46
47
48
49
50
51
52
53
  const double kInvSqrt2 = 0.707106781186547524400844362104;
  for (int k = 0; k < 32; k++) {
    out[k] = 0.0;
    for (int n = 0; n < 32; n++)
      out[k] += in[n] * cos(kPi * (2 * n + 1) * k / 64.0);
    if (k == 0)
      out[k] = out[k] * kInvSqrt2;
  }
}

54
55
void reference_32x32_dct_2d(const int16_t input[kNumCoeffs],
                            double output[kNumCoeffs]) {
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
  // First transform columns
  for (int i = 0; i < 32; ++i) {
    double temp_in[32], temp_out[32];
    for (int j = 0; j < 32; ++j)
      temp_in[j] = input[j*32 + i];
    reference_32x32_dct_1d(temp_in, temp_out, 1);
    for (int j = 0; j < 32; ++j)
      output[j * 32 + i] = temp_out[j];
  }
  // Then transform rows
  for (int i = 0; i < 32; ++i) {
    double temp_in[32], temp_out[32];
    for (int j = 0; j < 32; ++j)
      temp_in[j] = output[j + i*32];
    reference_32x32_dct_1d(temp_in, temp_out, 1);
    // Scale by some magic number
    for (int j = 0; j < 32; ++j)
      output[j + i * 32] = temp_out[j] / 4;
  }
}

77
78
typedef void (*fwd_txfm_t)(const int16_t *in, int16_t *out, int stride);
typedef void (*inv_txfm_t)(const int16_t *in, uint8_t *out, int stride);
79

80
81
82
typedef std::tr1::tuple<fwd_txfm_t, inv_txfm_t, int> trans_32x32_param_t;

class Trans32x32Test : public ::testing::TestWithParam<trans_32x32_param_t> {
83
84
85
86
87
88
89
 public:
  virtual ~Trans32x32Test() {}
  virtual void SetUp() {
    fwd_txfm_ = GET_PARAM(0);
    inv_txfm_ = GET_PARAM(1);
    version_  = GET_PARAM(2);  // 0: high precision forward transform
                               // 1: low precision version for rd loop
90
  }
91

92
93
94
95
96
97
98
99
100
  virtual void TearDown() { libvpx_test::ClearSystemState(); }

 protected:
  int version_;
  fwd_txfm_t fwd_txfm_;
  inv_txfm_t inv_txfm_;
};

TEST_P(Trans32x32Test, AccuracyCheck) {
101
  ACMRandom rnd(ACMRandom::DeterministicSeed());
102
  uint32_t max_error = 0;
103
104
  int64_t total_error = 0;
  const int count_test_block = 1000;
105
106
107
108
  DECLARE_ALIGNED_ARRAY(16, int16_t, test_input_block, kNumCoeffs);
  DECLARE_ALIGNED_ARRAY(16, int16_t, test_temp_block, kNumCoeffs);
  DECLARE_ALIGNED_ARRAY(16, uint8_t, dst, kNumCoeffs);
  DECLARE_ALIGNED_ARRAY(16, uint8_t, src, kNumCoeffs);
109

110
111
112
  for (int i = 0; i < count_test_block; ++i) {
    // Initialize a test block with input range [-255, 255].
    for (int j = 0; j < kNumCoeffs; ++j) {
Scott LaVarnway's avatar
Scott LaVarnway committed
113
114
115
      src[j] = rnd.Rand8();
      dst[j] = rnd.Rand8();
      test_input_block[j] = src[j] - dst[j];
116
    }
117

118
    REGISTER_STATE_CHECK(fwd_txfm_(test_input_block, test_temp_block, 32));
119
    REGISTER_STATE_CHECK(inv_txfm_(test_temp_block, dst, 32));
120

121
122
123
    for (int j = 0; j < kNumCoeffs; ++j) {
      const uint32_t diff = dst[j] - src[j];
      const uint32_t error = diff * diff;
124
125
126
127
128
129
      if (max_error < error)
        max_error = error;
      total_error += error;
    }
  }

130
131
132
133
134
  if (version_ == 1) {
    max_error /= 2;
    total_error /= 45;
  }

135
  EXPECT_GE(1u, max_error)
136
      << "Error: 32x32 FDCT/IDCT has an individual round-trip error > 1";
137

Yaowu Xu's avatar
Yaowu Xu committed
138
  EXPECT_GE(count_test_block, total_error)
139
      << "Error: 32x32 FDCT/IDCT has average round-trip error > 1 per block";
140
141
}

142
TEST_P(Trans32x32Test, CoeffCheck) {
143
144
  ACMRandom rnd(ACMRandom::DeterministicSeed());
  const int count_test_block = 1000;
145
146
147
148
149

  DECLARE_ALIGNED_ARRAY(16, int16_t, input_block, kNumCoeffs);
  DECLARE_ALIGNED_ARRAY(16, int16_t, output_ref_block, kNumCoeffs);
  DECLARE_ALIGNED_ARRAY(16, int16_t, output_block, kNumCoeffs);

150
  for (int i = 0; i < count_test_block; ++i) {
151
152
153
    for (int j = 0; j < kNumCoeffs; ++j)
      input_block[j] = rnd.Rand8() - rnd.Rand8();

154
    const int stride = 32;
155
    vp9_fdct32x32_c(input_block, output_ref_block, stride);
156
    REGISTER_STATE_CHECK(fwd_txfm_(input_block, output_block, stride));
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177

    if (version_ == 0) {
      for (int j = 0; j < kNumCoeffs; ++j)
        EXPECT_EQ(output_block[j], output_ref_block[j])
            << "Error: 32x32 FDCT versions have mismatched coefficients";
    } else {
      for (int j = 0; j < kNumCoeffs; ++j)
        EXPECT_GE(6, abs(output_block[j] - output_ref_block[j]))
            << "Error: 32x32 FDCT rd has mismatched coefficients";
    }
  }
}

TEST_P(Trans32x32Test, MemCheck) {
  ACMRandom rnd(ACMRandom::DeterministicSeed());
  const int count_test_block = 2000;

  DECLARE_ALIGNED_ARRAY(16, int16_t, input_block, kNumCoeffs);
  DECLARE_ALIGNED_ARRAY(16, int16_t, input_extreme_block, kNumCoeffs);
  DECLARE_ALIGNED_ARRAY(16, int16_t, output_ref_block, kNumCoeffs);
  DECLARE_ALIGNED_ARRAY(16, int16_t, output_block, kNumCoeffs);
178

179
  for (int i = 0; i < count_test_block; ++i) {
180
    // Initialize a test block with input range [-255, 255].
181
    for (int j = 0; j < kNumCoeffs; ++j) {
182
      input_block[j] = rnd.Rand8() - rnd.Rand8();
183
      input_extreme_block[j] = rnd.Rand8() & 1 ? 255 : -255;
184
185
    }
    if (i == 0)
186
      for (int j = 0; j < kNumCoeffs; ++j)
187
        input_extreme_block[j] = 255;
188
189
190
    if (i == 1)
      for (int j = 0; j < kNumCoeffs; ++j)
        input_extreme_block[j] = -255;
191

192
    const int stride = 32;
193
    vp9_fdct32x32_c(input_extreme_block, output_ref_block, stride);
194
    REGISTER_STATE_CHECK(fwd_txfm_(input_extreme_block, output_block, stride));
195
196

    // The minimum quant value is 4.
197
198
199
200
201
202
203
204
205
206
207
208
209
    for (int j = 0; j < kNumCoeffs; ++j) {
      if (version_ == 0) {
        EXPECT_EQ(output_block[j], output_ref_block[j])
            << "Error: 32x32 FDCT versions have mismatched coefficients";
      } else {
        EXPECT_GE(6, abs(output_block[j] - output_ref_block[j]))
            << "Error: 32x32 FDCT rd has mismatched coefficients";
      }
      EXPECT_GE(4 * DCT_MAX_VALUE, abs(output_ref_block[j]))
          << "Error: 32x32 FDCT C has coefficient larger than 4*DCT_MAX_VALUE";
      EXPECT_GE(4 * DCT_MAX_VALUE, abs(output_block[j]))
          << "Error: 32x32 FDCT has coefficient larger than "
          << "4*DCT_MAX_VALUE";
210
211
212
    }
  }
}
213
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
250

TEST_P(Trans32x32Test, InverseAccuracy) {
  ACMRandom rnd(ACMRandom::DeterministicSeed());
  const int count_test_block = 1000;
  DECLARE_ALIGNED_ARRAY(16, int16_t, in, kNumCoeffs);
  DECLARE_ALIGNED_ARRAY(16, int16_t, coeff, kNumCoeffs);
  DECLARE_ALIGNED_ARRAY(16, uint8_t, dst, kNumCoeffs);
  DECLARE_ALIGNED_ARRAY(16, uint8_t, src, kNumCoeffs);

  for (int i = 0; i < count_test_block; ++i) {
    double out_r[kNumCoeffs];

    // Initialize a test block with input range [-255, 255]
    for (int j = 0; j < kNumCoeffs; ++j) {
      src[j] = rnd.Rand8();
      dst[j] = rnd.Rand8();
      in[j] = src[j] - dst[j];
    }

    reference_32x32_dct_2d(in, out_r);
    for (int j = 0; j < kNumCoeffs; ++j)
      coeff[j] = round(out_r[j]);
    REGISTER_STATE_CHECK(inv_txfm_(coeff, dst, 32));
    for (int j = 0; j < kNumCoeffs; ++j) {
      const int diff = dst[j] - src[j];
      const int error = diff * diff;
      EXPECT_GE(1, error)
          << "Error: 32x32 IDCT has error " << error
          << " at index " << j;
    }
  }
}

using std::tr1::make_tuple;

INSTANTIATE_TEST_CASE_P(
    C, Trans32x32Test,
    ::testing::Values(
251
252
        make_tuple(&vp9_fdct32x32_c, &vp9_idct32x32_1024_add_c, 0),
        make_tuple(&vp9_fdct32x32_rd_c, &vp9_idct32x32_1024_add_c, 1)));
253
254
255
256
257

#if HAVE_SSE2
INSTANTIATE_TEST_CASE_P(
    SSE2, Trans32x32Test,
    ::testing::Values(
258
        make_tuple(&vp9_fdct32x32_sse2,
259
                   &vp9_idct32x32_1024_add_sse2, 0),
260
        make_tuple(&vp9_fdct32x32_rd_sse2,
261
                   &vp9_idct32x32_1024_add_sse2, 1)));
262
#endif
263
264
265
266
267
268
269
270
271
272

#if HAVE_AVX2
INSTANTIATE_TEST_CASE_P(
    AVX2, Trans32x32Test,
    ::testing::Values(
        make_tuple(&vp9_fdct32x32_avx2,
                   &vp9_idct32x32_1024_add_sse2, 0),
        make_tuple(&vp9_fdct32x32_rd_avx2,
                   &vp9_idct32x32_1024_add_sse2, 1)));
#endif
273
}  // namespace