dct16x16_test.cc 12.3 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 "vpx_ports/mem.h"
Daniel Kang's avatar
Daniel Kang committed
17 18

extern "C" {
Yaowu Xu's avatar
Yaowu Xu committed
19
#include "vp9/common/vp9_entropy.h"
Yaowu Xu's avatar
Yaowu Xu committed
20 21
#include "./vp9_rtcd.h"
void vp9_short_idct16x16_add_c(int16_t *input, uint8_t *output, int pitch);
Daniel Kang's avatar
Daniel Kang committed
22 23
}

Yaowu Xu's avatar
Yaowu Xu committed
24
#include "test/acm_random.h"
Daniel Kang's avatar
Daniel Kang committed
25 26 27 28 29
#include "vpx/vpx_integer.h"

using libvpx_test::ACMRandom;

namespace {
30 31 32 33

#ifdef _MSC_VER
static int round(double x) {
  if (x < 0)
Yaowu Xu's avatar
Yaowu Xu committed
34
    return static_cast<int>(ceil(x - 0.5));
35
  else
Yaowu Xu's avatar
Yaowu Xu committed
36
    return static_cast<int>(floor(x + 0.5));
37 38
}
#endif
Daniel Kang's avatar
Daniel Kang committed
39 40 41 42 43 44 45 46 47

const double PI = 3.1415926535898;
void reference2_16x16_idct_2d(double *input, double *output) {
  double x;
  for (int l = 0; l < 16; ++l) {
    for (int k = 0; k < 16; ++k) {
      double s = 0;
      for (int i = 0; i < 16; ++i) {
        for (int j = 0; j < 16; ++j) {
Yaowu Xu's avatar
Yaowu Xu committed
48 49 50
          x = cos(PI * j * (l + 0.5) / 16.0) *
              cos(PI * i * (k + 0.5) / 16.0) *
              input[i * 16 + j] / 256;
Daniel Kang's avatar
Daniel Kang committed
51 52 53 54 55 56 57 58 59 60 61 62
          if (i != 0)
            x *= sqrt(2.0);
          if (j != 0)
            x *= sqrt(2.0);
          s += x;
        }
      }
      output[k*16+l] = s;
    }
  }
}

63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79

static const double C1 = 0.995184726672197;
static const double C2 = 0.98078528040323;
static const double C3 = 0.956940335732209;
static const double C4 = 0.923879532511287;
static const double C5 = 0.881921264348355;
static const double C6 = 0.831469612302545;
static const double C7 = 0.773010453362737;
static const double C8 = 0.707106781186548;
static const double C9 = 0.634393284163646;
static const double C10 = 0.555570233019602;
static const double C11 = 0.471396736825998;
static const double C12 = 0.38268343236509;
static const double C13 = 0.290284677254462;
static const double C14 = 0.195090322016128;
static const double C15 = 0.098017140329561;

Jingning Han's avatar
Jingning Han committed
80
void butterfly_16x16_dct_1d(double input[16], double output[16]) {
Daniel Kang's avatar
Daniel Kang committed
81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 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 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 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 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269
  double step[16];
  double intermediate[16];
  double temp1, temp2;

  // step 1
  step[ 0] = input[0] + input[15];
  step[ 1] = input[1] + input[14];
  step[ 2] = input[2] + input[13];
  step[ 3] = input[3] + input[12];
  step[ 4] = input[4] + input[11];
  step[ 5] = input[5] + input[10];
  step[ 6] = input[6] + input[ 9];
  step[ 7] = input[7] + input[ 8];
  step[ 8] = input[7] - input[ 8];
  step[ 9] = input[6] - input[ 9];
  step[10] = input[5] - input[10];
  step[11] = input[4] - input[11];
  step[12] = input[3] - input[12];
  step[13] = input[2] - input[13];
  step[14] = input[1] - input[14];
  step[15] = input[0] - input[15];

  // step 2
  output[0] = step[0] + step[7];
  output[1] = step[1] + step[6];
  output[2] = step[2] + step[5];
  output[3] = step[3] + step[4];
  output[4] = step[3] - step[4];
  output[5] = step[2] - step[5];
  output[6] = step[1] - step[6];
  output[7] = step[0] - step[7];

  temp1 = step[ 8]*C7;
  temp2 = step[15]*C9;
  output[ 8] = temp1 + temp2;

  temp1 = step[ 9]*C11;
  temp2 = step[14]*C5;
  output[ 9] = temp1 - temp2;

  temp1 = step[10]*C3;
  temp2 = step[13]*C13;
  output[10] = temp1 + temp2;

  temp1 = step[11]*C15;
  temp2 = step[12]*C1;
  output[11] = temp1 - temp2;

  temp1 = step[11]*C1;
  temp2 = step[12]*C15;
  output[12] = temp2 + temp1;

  temp1 = step[10]*C13;
  temp2 = step[13]*C3;
  output[13] = temp2 - temp1;

  temp1 = step[ 9]*C5;
  temp2 = step[14]*C11;
  output[14] = temp2 + temp1;

  temp1 = step[ 8]*C9;
  temp2 = step[15]*C7;
  output[15] = temp2 - temp1;

  // step 3
  step[ 0] = output[0] + output[3];
  step[ 1] = output[1] + output[2];
  step[ 2] = output[1] - output[2];
  step[ 3] = output[0] - output[3];

  temp1 = output[4]*C14;
  temp2 = output[7]*C2;
  step[ 4] = temp1 + temp2;

  temp1 = output[5]*C10;
  temp2 = output[6]*C6;
  step[ 5] = temp1 + temp2;

  temp1 = output[5]*C6;
  temp2 = output[6]*C10;
  step[ 6] = temp2 - temp1;

  temp1 = output[4]*C2;
  temp2 = output[7]*C14;
  step[ 7] = temp2 - temp1;

  step[ 8] = output[ 8] + output[11];
  step[ 9] = output[ 9] + output[10];
  step[10] = output[ 9] - output[10];
  step[11] = output[ 8] - output[11];

  step[12] = output[12] + output[15];
  step[13] = output[13] + output[14];
  step[14] = output[13] - output[14];
  step[15] = output[12] - output[15];

  // step 4
  output[ 0] = (step[ 0] + step[ 1]);
  output[ 8] = (step[ 0] - step[ 1]);

  temp1 = step[2]*C12;
  temp2 = step[3]*C4;
  temp1 = temp1 + temp2;
  output[ 4] = 2*(temp1*C8);

  temp1 = step[2]*C4;
  temp2 = step[3]*C12;
  temp1 = temp2 - temp1;
  output[12] = 2*(temp1*C8);

  output[ 2] = 2*((step[4] + step[ 5])*C8);
  output[14] = 2*((step[7] - step[ 6])*C8);

  temp1 = step[4] - step[5];
  temp2 = step[6] + step[7];
  output[ 6] = (temp1 + temp2);
  output[10] = (temp1 - temp2);

  intermediate[8] = step[8] + step[14];
  intermediate[9] = step[9] + step[15];

  temp1 = intermediate[8]*C12;
  temp2 = intermediate[9]*C4;
  temp1 = temp1 - temp2;
  output[3] = 2*(temp1*C8);

  temp1 = intermediate[8]*C4;
  temp2 = intermediate[9]*C12;
  temp1 = temp2 + temp1;
  output[13] = 2*(temp1*C8);

  output[ 9] = 2*((step[10] + step[11])*C8);

  intermediate[11] = step[10] - step[11];
  intermediate[12] = step[12] + step[13];
  intermediate[13] = step[12] - step[13];
  intermediate[14] = step[ 8] - step[14];
  intermediate[15] = step[ 9] - step[15];

  output[15] = (intermediate[11] + intermediate[12]);
  output[ 1] = -(intermediate[11] - intermediate[12]);

  output[ 7] = 2*(intermediate[13]*C8);

  temp1 = intermediate[14]*C12;
  temp2 = intermediate[15]*C4;
  temp1 = temp1 - temp2;
  output[11] = -2*(temp1*C8);

  temp1 = intermediate[14]*C4;
  temp2 = intermediate[15]*C12;
  temp1 = temp2 + temp1;
  output[ 5] = 2*(temp1*C8);
}

static void reference_16x16_dct_1d(double in[16], double out[16]) {
  const double kPi = 3.141592653589793238462643383279502884;
  const double kInvSqrt2 = 0.707106781186547524400844362104;
  for (int k = 0; k < 16; k++) {
    out[k] = 0.0;
    for (int n = 0; n < 16; n++)
      out[k] += in[n]*cos(kPi*(2*n+1)*k/32.0);
    if (k == 0)
      out[k] = out[k]*kInvSqrt2;
  }
}

void reference_16x16_dct_2d(int16_t input[16*16], double output[16*16]) {
  // First transform columns
  for (int i = 0; i < 16; ++i) {
    double temp_in[16], temp_out[16];
    for (int j = 0; j < 16; ++j)
      temp_in[j] = input[j*16 + i];
    butterfly_16x16_dct_1d(temp_in, temp_out);
    for (int j = 0; j < 16; ++j)
      output[j*16 + i] = temp_out[j];
  }
  // Then transform rows
  for (int i = 0; i < 16; ++i) {
    double temp_in[16], temp_out[16];
    for (int j = 0; j < 16; ++j)
      temp_in[j] = output[j + i*16];
    butterfly_16x16_dct_1d(temp_in, temp_out);
    // Scale by some magic number
    for (int j = 0; j < 16; ++j)
      output[j + i*16] = temp_out[j]/2;
  }
}

270 271 272 273 274 275 276 277 278 279
void fdct16x16(int16_t *in, int16_t *out, uint8_t* /*dst*/,
               int stride, int /*tx_type*/) {
  vp9_short_fdct16x16_c(in, out, stride);
}
void idct16x16_add(int16_t* /*in*/, int16_t *out, uint8_t *dst,
                   int stride, int /*tx_type*/) {
  vp9_short_idct16x16_add_c(out, dst, stride >> 1);
}
void fht16x16(int16_t *in, int16_t *out, uint8_t* /*dst*/,
              int stride, int tx_type) {
280 281
  // FIXME(jingning): need to test both SSE2 and c
#if HAVE_SSE2
282 283 284 285 286 287 288 289 290
  vp9_short_fht16x16_sse2(in, out, stride >> 1, tx_type);
#else
  vp9_short_fht16x16_c(in, out, stride >> 1, tx_type);
#endif
}
void iht16x16_add(int16_t* /*in*/, int16_t *out, uint8_t *dst,
                  int stride, int tx_type) {
  vp9_short_iht16x16_add_c(out, dst, stride >> 1, tx_type);
}
Daniel Kang's avatar
Daniel Kang committed
291

Jingning Han's avatar
Jingning Han committed
292
class Trans16x16Test : public ::testing::TestWithParam<int> {
293
 public:
Jingning Han's avatar
Jingning Han committed
294
  virtual ~Trans16x16Test() {}
295

296
  virtual void SetUp() {
297 298
    tx_type_ = GetParam();
    if (tx_type_ == 0) {
Jingning Han's avatar
Jingning Han committed
299 300
      fwd_txfm_ = fdct16x16;
      inv_txfm_ = idct16x16_add;
301
    } else {
Jingning Han's avatar
Jingning Han committed
302 303
      fwd_txfm_ = fht16x16;
      inv_txfm_ = iht16x16_add;
Scott LaVarnway's avatar
Scott LaVarnway committed
304
    }
305
  }
Daniel Kang's avatar
Daniel Kang committed
306

307 308 309
 protected:
  void RunFwdTxfm(int16_t *in, int16_t *out, uint8_t *dst,
                  int stride, int tx_type) {
Jingning Han's avatar
Jingning Han committed
310
    (*fwd_txfm_)(in, out, dst, stride, tx_type);
Daniel Kang's avatar
Daniel Kang committed
311
  }
312 313
  void RunInvTxfm(int16_t *in, int16_t *out, uint8_t *dst,
                  int stride, int tx_type) {
Jingning Han's avatar
Jingning Han committed
314
    (*inv_txfm_)(in, out, dst, stride, tx_type);
315 316 317
  }

  int tx_type_;
Jingning Han's avatar
Jingning Han committed
318 319
  void (*fwd_txfm_)(int16_t*, int16_t*, uint8_t*, int, int);
  void (*inv_txfm_)(int16_t*, int16_t*, uint8_t*, int, int);
320
};
Scott LaVarnway's avatar
Scott LaVarnway committed
321

Jingning Han's avatar
Jingning Han committed
322
TEST_P(Trans16x16Test, AccuracyCheck) {
Daniel Kang's avatar
Daniel Kang committed
323 324
  ACMRandom rnd(ACMRandom::DeterministicSeed());
  int max_error = 0;
325
  int total_error = 0;
326
  const int count_test_block = 10000;
Daniel Kang's avatar
Daniel Kang committed
327
  for (int i = 0; i < count_test_block; ++i) {
328 329 330 331
    DECLARE_ALIGNED_ARRAY(16, int16_t, test_input_block, 256);
    DECLARE_ALIGNED_ARRAY(16, int16_t, test_temp_block, 256);
    DECLARE_ALIGNED_ARRAY(16, uint8_t, dst, 256);
    DECLARE_ALIGNED_ARRAY(16, uint8_t, src, 256);
Daniel Kang's avatar
Daniel Kang committed
332

Scott LaVarnway's avatar
Scott LaVarnway committed
333 334 335
    for (int j = 0; j < 256; ++j) {
      src[j] = rnd.Rand8();
      dst[j] = rnd.Rand8();
336
      // Initialize a test block with input range [-255, 255].
Scott LaVarnway's avatar
Scott LaVarnway committed
337
      test_input_block[j] = src[j] - dst[j];
338
    }
Daniel Kang's avatar
Daniel Kang committed
339 340

    const int pitch = 32;
341 342
    RunFwdTxfm(test_input_block, test_temp_block, dst, pitch, tx_type_);
    RunInvTxfm(test_input_block, test_temp_block, dst, pitch, tx_type_);
Daniel Kang's avatar
Daniel Kang committed
343 344

    for (int j = 0; j < 256; ++j) {
Scott LaVarnway's avatar
Scott LaVarnway committed
345
      const int diff = dst[j] - src[j];
Daniel Kang's avatar
Daniel Kang committed
346 347 348 349 350 351 352 353
      const int error = diff * diff;
      if (max_error < error)
        max_error = error;
      total_error += error;
    }
  }

  EXPECT_GE(1, max_error)
354
      << "Error: 16x16 FHT/IHT has an individual round trip error > 1";
Daniel Kang's avatar
Daniel Kang committed
355

Yaowu Xu's avatar
Yaowu Xu committed
356
  EXPECT_GE(count_test_block , total_error)
357
      << "Error: 16x16 FHT/IHT has average round trip error > 1 per block";
Daniel Kang's avatar
Daniel Kang committed
358 359
}

Jingning Han's avatar
Jingning Han committed
360
TEST_P(Trans16x16Test, CoeffSizeCheck) {
Daniel Kang's avatar
Daniel Kang committed
361 362 363
  ACMRandom rnd(ACMRandom::DeterministicSeed());
  const int count_test_block = 1000;
  for (int i = 0; i < count_test_block; ++i) {
364 365 366 367 368
    DECLARE_ALIGNED_ARRAY(16, int16_t, input_block, 256);
    DECLARE_ALIGNED_ARRAY(16, int16_t, input_extreme_block, 256);
    DECLARE_ALIGNED_ARRAY(16, int16_t, output_block, 256);
    DECLARE_ALIGNED_ARRAY(16, int16_t, output_extreme_block, 256);
    DECLARE_ALIGNED_ARRAY(16, uint8_t, dst, 256);
Daniel Kang's avatar
Daniel Kang committed
369 370 371 372 373 374 375 376 377 378 379

    // Initialize a test block with input range [-255, 255].
    for (int j = 0; j < 256; ++j) {
      input_block[j] = rnd.Rand8() - rnd.Rand8();
      input_extreme_block[j] = rnd.Rand8() % 2 ? 255 : -255;
    }
    if (i == 0)
      for (int j = 0; j < 256; ++j)
        input_extreme_block[j] = 255;

    const int pitch = 32;
380 381
    RunFwdTxfm(input_block, output_block, dst, pitch, tx_type_);
    RunFwdTxfm(input_extreme_block, output_extreme_block, dst, pitch, tx_type_);
Daniel Kang's avatar
Daniel Kang committed
382 383 384

    // The minimum quant value is 4.
    for (int j = 0; j < 256; ++j) {
385
      EXPECT_GE(4 * DCT_MAX_VALUE, abs(output_block[j]))
Daniel Kang's avatar
Daniel Kang committed
386
          << "Error: 16x16 FDCT has coefficient larger than 4*DCT_MAX_VALUE";
387 388 389
      EXPECT_GE(4 * DCT_MAX_VALUE, abs(output_extreme_block[j]))
          << "Error: 16x16 FDCT extreme has coefficient larger "
          << "than 4*DCT_MAX_VALUE";
Daniel Kang's avatar
Daniel Kang committed
390 391 392
    }
  }
}
393

Jingning Han's avatar
Jingning Han committed
394
TEST_P(Trans16x16Test, InvAccuracyCheck) {
395 396
  ACMRandom rnd(ACMRandom::DeterministicSeed());
  const int count_test_block = 1000;
Jingning Han's avatar
Jingning Han committed
397 398 399 400 401
  // TODO(jingning): is this unit test necessary? if so, need to add
  // check sets for inverse hybrid transforms too.
  if (tx_type_ != DCT_DCT)
    return;

402
  for (int i = 0; i < count_test_block; ++i) {
Jingning Han's avatar
Jingning Han committed
403 404 405 406
    DECLARE_ALIGNED_ARRAY(16, int16_t, in, 256);
    DECLARE_ALIGNED_ARRAY(16, int16_t, coeff, 256);
    DECLARE_ALIGNED_ARRAY(16, uint8_t, dst, 256);
    DECLARE_ALIGNED_ARRAY(16, uint8_t, src, 256);
407 408 409 410 411 412 413 414 415 416 417 418 419
    double out_r[256];

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

    reference_16x16_dct_2d(in, out_r);
    for (int j = 0; j < 256; j++)
      coeff[j] = round(out_r[j]);
Jingning Han's avatar
Jingning Han committed
420 421 422 423

    const int pitch = 32;
    RunInvTxfm(coeff, coeff, dst, pitch, tx_type_);

424 425 426 427 428 429 430 431 432 433
    for (int j = 0; j < 256; ++j) {
      const int diff = dst[j] - src[j];
      const int error = diff * diff;
      EXPECT_GE(1, error)
          << "Error: 16x16 IDCT has error " << error
          << " at index " << j;
    }
  }
}

Jingning Han's avatar
Jingning Han committed
434
INSTANTIATE_TEST_CASE_P(VP9, Trans16x16Test, ::testing::Range(0, 4));
Daniel Kang's avatar
Daniel Kang committed
435
}  // namespace