fdct8x8_test.cc 9.7 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
typedef void (*fdct_t)(int16_t *in, int16_t *out, int stride);
32
typedef void (*idct_t)(const int16_t *in, uint8_t *dst, int stride);
33
typedef void (*fht_t) (int16_t *in, int16_t *out, int stride, int tx_type);
34
35
typedef void (*iht_t) (const int16_t *in, uint8_t *dst, int stride,
              int tx_type);
36
37

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

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

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

 protected:
50
51
  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
52

53
54
55
56
57
58
  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
59

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

62
63
64
65
66
67
    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
68

69
70
71
72
73
74
75
      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
76
77

    for (int j = 0; j < 64; ++j) {
78
79
80
81
82
83
84
85
86
      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
87
88
    }

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

91
92
93
94
95
96
    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
97

98
99
100
101
102
103
104
      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
105
106

    for (int j = 0; j < 64; ++j) {
107
108
109
110
111
112
113
114
115
      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
116
117
118
    }
  }

119
120
121
122
123
  void RunRoundTripErrorCheck() {
    ACMRandom rnd(ACMRandom::DeterministicSeed());
    int max_error = 0;
    int total_error = 0;
    const int count_test_block = 100000;
124
125
126
127
    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
128

129
130
131
132
133
134
135
    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
136

137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
      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
160
161
    }

162
163
164
    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
165

166
167
168
169
    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
170

171
172
173
174
175
  void RunExtremalCheck() {
    ACMRandom rnd(ACMRandom::DeterministicSeed());
    int max_error = 0;
    int total_error = 0;
    const int count_test_block = 100000;
176
177
178
179
    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
180

181
182
183
184
185
186
187
    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
188

189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
      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
209
    }
210
  }
Daniel Kang's avatar
Daniel Kang committed
211

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

217
218
219
220
221
222
223
224
225
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);
226
    pitch_    = 8;
227
228
229
230
231
232
233
234
235
236
    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) {
237
    inv_txfm_(out, dst, stride);
Daniel Kang's avatar
Daniel Kang committed
238
  }
239
240
241
242
243
244
245

  fdct_t fwd_txfm_;
  idct_t inv_txfm_;
};

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

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
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(
300
        make_tuple(&vp9_short_fdct8x8_c, &vp9_idct8x8_64_add_c, 0)));
301
302
303
INSTANTIATE_TEST_CASE_P(
    C, FwdTrans8x8HT,
    ::testing::Values(
304
305
306
307
        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)));
308
309
310
311
312

#if HAVE_SSE2
INSTANTIATE_TEST_CASE_P(
    SSE2, FwdTrans8x8DCT,
    ::testing::Values(
313
        make_tuple(&vp9_short_fdct8x8_sse2, &vp9_idct8x8_64_add_sse2, 0)));
314
315
316
INSTANTIATE_TEST_CASE_P(
    SSE2, FwdTrans8x8HT,
    ::testing::Values(
317
318
319
320
        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)));
321
#endif
Daniel Kang's avatar
Daniel Kang committed
322
}  // namespace