warp_filter_test_util.cc 9.94 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
/*
 * Copyright (c) 2016, Alliance for Open Media. All rights reserved
 *
 * This source code is subject to the terms of the BSD 2 Clause License and
 * the Alliance for Open Media Patent License 1.0. If the BSD 2 Clause License
 * was not distributed with this source code in the LICENSE file, you can
 * obtain it at www.aomedia.org/license/software. If the Alliance for Open
 * Media Patent License 1.0 was not distributed with this source code in the
 * PATENTS file, you can obtain it at www.aomedia.org/license/patent.
 */

#include "test/warp_filter_test_util.h"

using std::tr1::tuple;
using std::tr1::make_tuple;
using std::vector;
using libaom_test::ACMRandom;
using libaom_test::AV1WarpFilter::AV1WarpFilterTest;
using libaom_test::AV1WarpFilter::WarpTestParam;
20
#if CONFIG_HIGHBITDEPTH
21
22
23
using libaom_test::AV1HighbdWarpFilter::AV1HighbdWarpFilterTest;
using libaom_test::AV1HighbdWarpFilter::HighbdWarpTestParam;
#endif
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48

::testing::internal::ParamGenerator<WarpTestParam>
libaom_test::AV1WarpFilter::GetDefaultParams() {
  const WarpTestParam defaultParams[] = {
    make_tuple(4, 4, 50000),  make_tuple(8, 8, 50000),
    make_tuple(64, 64, 1000), make_tuple(4, 16, 20000),
    make_tuple(32, 8, 10000),
  };
  return ::testing::ValuesIn(defaultParams);
}

AV1WarpFilterTest::~AV1WarpFilterTest() {}
void AV1WarpFilterTest::SetUp() { rnd_.Reset(ACMRandom::DeterministicSeed()); }

void AV1WarpFilterTest::TearDown() { libaom_test::ClearSystemState(); }

int32_t AV1WarpFilterTest::random_param(int bits) {
  // 1 in 8 chance of generating zero (arbitrarily chosen)
  if (((rnd_.Rand8()) & 7) == 0) return 0;
  // Otherwise, enerate uniform values in the range
  // [-(1 << bits), 1] U [1, 1<<bits]
  int32_t v = 1 + (rnd_.Rand16() & ((1 << bits) - 1));
  if ((rnd_.Rand8()) & 1) return -v;
  return v;
}
49

50
51
52
void AV1WarpFilterTest::generate_model(int32_t *mat, int16_t *alpha,
                                       int16_t *beta, int16_t *gamma,
                                       int16_t *delta) {
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
  while (1) {
    mat[0] = random_param(WARPEDMODEL_PREC_BITS + 6);
    mat[1] = random_param(WARPEDMODEL_PREC_BITS + 6);
    mat[2] = (random_param(WARPEDMODEL_PREC_BITS - 3)) +
             (1 << WARPEDMODEL_PREC_BITS);
    mat[3] = random_param(WARPEDMODEL_PREC_BITS - 3);
    // 50/50 chance of generating ROTZOOM vs. AFFINE models
    if (rnd_.Rand8() & 1) {
      // AFFINE
      mat[4] = random_param(WARPEDMODEL_PREC_BITS - 3);
      mat[5] = (random_param(WARPEDMODEL_PREC_BITS - 3)) +
               (1 << WARPEDMODEL_PREC_BITS);
    } else {
      mat[4] = -mat[3];
      mat[5] = mat[2];
    }

    // Calculate the derived parameters and check that they are suitable
    // for the warp filter.
    assert(mat[2] != 0);

74
75
76
77
78
79
80
81
    *alpha = clamp(mat[2] - (1 << WARPEDMODEL_PREC_BITS), INT16_MIN, INT16_MAX);
    *beta = clamp(mat[3], INT16_MIN, INT16_MAX);
    *gamma = clamp(((int64_t)mat[4] << WARPEDMODEL_PREC_BITS) / mat[2],
                   INT16_MIN, INT16_MAX);
    *delta =
        clamp(mat[5] - (((int64_t)mat[3] * mat[4] + (mat[2] / 2)) / mat[2]) -
                  (1 << WARPEDMODEL_PREC_BITS),
              INT16_MIN, INT16_MAX);
82

83
84
    if ((4 * abs(*alpha) + 7 * abs(*beta) >= (1 << WARPEDMODEL_PREC_BITS)) ||
        (4 * abs(*gamma) + 4 * abs(*delta) >= (1 << WARPEDMODEL_PREC_BITS)))
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
      continue;

    // We have a valid model, so finish
    return;
  }
}

void AV1WarpFilterTest::RunCheckOutput(warp_affine_func test_impl) {
  const int w = 128, h = 128;
  const int border = 16;
  const int stride = w + 2 * border;
  const int out_w = GET_PARAM(0), out_h = GET_PARAM(1);
  const int num_iters = GET_PARAM(2);
  int i, j, sub_x, sub_y;

  uint8_t *input_ = new uint8_t[h * stride];
  uint8_t *input = input_ + border;
102
103

  // The warp functions always write rows with widths that are multiples of 8.
104
105
  // So to avoid a buffer overflow, we may need to pad rows to a multiple of 8.
  int output_n = ((out_w + 7) & ~7) * out_h;
106
107
  uint8_t *output = new uint8_t[output_n];
  uint8_t *output2 = new uint8_t[output_n];
108
109
  int32_t mat[8];
  int16_t alpha, beta, gamma, delta;
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

  // Generate an input block and extend its borders horizontally
  for (i = 0; i < h; ++i)
    for (j = 0; j < w; ++j) input[i * stride + j] = rnd_.Rand8();
  for (i = 0; i < h; ++i) {
    memset(input + i * stride - border, input[i * stride], border);
    memset(input + i * stride + w, input[i * stride + (w - 1)], border);
  }

  for (i = 0; i < num_iters; ++i) {
    for (sub_x = 0; sub_x < 2; ++sub_x)
      for (sub_y = 0; sub_y < 2; ++sub_y) {
        generate_model(mat, &alpha, &beta, &gamma, &delta);
        av1_warp_affine_c(mat, input, w, h, stride, output, 32, 32, out_w,
                          out_h, out_w, sub_x, sub_y, 0, alpha, beta, gamma,
                          delta);
        test_impl(mat, input, w, h, stride, output2, 32, 32, out_w, out_h,
                  out_w, sub_x, sub_y, 0, alpha, beta, gamma, delta);

        for (j = 0; j < out_w * out_h; ++j)
          ASSERT_EQ(output[j], output2[j])
              << "Pixel mismatch at index " << j << " = (" << (j % out_w)
              << ", " << (j / out_w) << ") on iteration " << i;
      }
  }
135
136
137
  delete[] input_;
  delete[] output;
  delete[] output2;
138
}
139

140
#if CONFIG_HIGHBITDEPTH
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
::testing::internal::ParamGenerator<HighbdWarpTestParam>
libaom_test::AV1HighbdWarpFilter::GetDefaultParams() {
  const HighbdWarpTestParam defaultParams[] = {
    make_tuple(4, 4, 50000, 8),   make_tuple(8, 8, 50000, 8),
    make_tuple(64, 64, 1000, 8),  make_tuple(4, 16, 20000, 8),
    make_tuple(32, 8, 10000, 8),  make_tuple(4, 4, 50000, 10),
    make_tuple(8, 8, 50000, 10),  make_tuple(64, 64, 1000, 10),
    make_tuple(4, 16, 20000, 10), make_tuple(32, 8, 10000, 10),
    make_tuple(4, 4, 50000, 12),  make_tuple(8, 8, 50000, 12),
    make_tuple(64, 64, 1000, 12), make_tuple(4, 16, 20000, 12),
    make_tuple(32, 8, 10000, 12),
  };
  return ::testing::ValuesIn(defaultParams);
}

AV1HighbdWarpFilterTest::~AV1HighbdWarpFilterTest() {}
void AV1HighbdWarpFilterTest::SetUp() {
  rnd_.Reset(ACMRandom::DeterministicSeed());
}

void AV1HighbdWarpFilterTest::TearDown() { libaom_test::ClearSystemState(); }

int32_t AV1HighbdWarpFilterTest::random_param(int bits) {
  // 1 in 8 chance of generating zero (arbitrarily chosen)
  if (((rnd_.Rand8()) & 7) == 0) return 0;
  // Otherwise, enerate uniform values in the range
  // [-(1 << bits), 1] U [1, 1<<bits]
  int32_t v = 1 + (rnd_.Rand16() & ((1 << bits) - 1));
  if ((rnd_.Rand8()) & 1) return -v;
  return v;
}

173
174
175
void AV1HighbdWarpFilterTest::generate_model(int32_t *mat, int16_t *alpha,
                                             int16_t *beta, int16_t *gamma,
                                             int16_t *delta) {
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
  while (1) {
    mat[0] = random_param(WARPEDMODEL_PREC_BITS + 6);
    mat[1] = random_param(WARPEDMODEL_PREC_BITS + 6);
    mat[2] = (random_param(WARPEDMODEL_PREC_BITS - 3)) +
             (1 << WARPEDMODEL_PREC_BITS);
    mat[3] = random_param(WARPEDMODEL_PREC_BITS - 3);
    // 50/50 chance of generating ROTZOOM vs. AFFINE models
    if (rnd_.Rand8() & 1) {
      // AFFINE
      mat[4] = random_param(WARPEDMODEL_PREC_BITS - 3);
      mat[5] = (random_param(WARPEDMODEL_PREC_BITS - 3)) +
               (1 << WARPEDMODEL_PREC_BITS);
    } else {
      mat[4] = -mat[3];
      mat[5] = mat[2];
    }

    // Calculate the derived parameters and check that they are suitable
    // for the warp filter.
    assert(mat[2] != 0);

197
198
199
200
201
202
203
204
    *alpha = clamp(mat[2] - (1 << WARPEDMODEL_PREC_BITS), INT16_MIN, INT16_MAX);
    *beta = clamp(mat[3], INT16_MIN, INT16_MAX);
    *gamma = clamp(((int64_t)mat[4] << WARPEDMODEL_PREC_BITS) / mat[2],
                   INT16_MIN, INT16_MAX);
    *delta =
        clamp(mat[5] - (((int64_t)mat[3] * mat[4] + (mat[2] / 2)) / mat[2]) -
                  (1 << WARPEDMODEL_PREC_BITS),
              INT16_MIN, INT16_MAX);
205

206
207
    if ((4 * abs(*alpha) + 7 * abs(*beta) >= (1 << WARPEDMODEL_PREC_BITS)) ||
        (4 * abs(*gamma) + 4 * abs(*delta) >= (1 << WARPEDMODEL_PREC_BITS)))
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
      continue;

    // We have a valid model, so finish
    return;
  }
}

void AV1HighbdWarpFilterTest::RunCheckOutput(
    highbd_warp_affine_func test_impl) {
  const int w = 128, h = 128;
  const int border = 16;
  const int stride = w + 2 * border;
  const int out_w = GET_PARAM(0), out_h = GET_PARAM(1);
  const int num_iters = GET_PARAM(2);
  const int bd = GET_PARAM(3);
  const int mask = (1 << bd) - 1;
  int i, j, sub_x, sub_y;

226
  // The warp functions always write rows with widths that are multiples of 8.
227
228
  // So to avoid a buffer overflow, we may need to pad rows to a multiple of 8.
  int output_n = ((out_w + 7) & ~7) * out_h;
229
230
  uint16_t *input_ = new uint16_t[h * stride];
  uint16_t *input = input_ + border;
231
232
  uint16_t *output = new uint16_t[output_n];
  uint16_t *output2 = new uint16_t[output_n];
233
234
  int32_t mat[8];
  int16_t alpha, beta, gamma, delta;
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

  // Generate an input block and extend its borders horizontally
  for (i = 0; i < h; ++i)
    for (j = 0; j < w; ++j) input[i * stride + j] = rnd_.Rand16() & mask;
  for (i = 0; i < h; ++i) {
    for (j = 0; j < border; ++j) {
      input[i * stride - border + j] = input[i * stride];
      input[i * stride + w + j] = input[i * stride + (w - 1)];
    }
  }

  for (i = 0; i < num_iters; ++i) {
    for (sub_x = 0; sub_x < 2; ++sub_x)
      for (sub_y = 0; sub_y < 2; ++sub_y) {
        generate_model(mat, &alpha, &beta, &gamma, &delta);

        av1_highbd_warp_affine_c(mat, input, w, h, stride, output, 32, 32,
                                 out_w, out_h, out_w, sub_x, sub_y, bd, 0,
                                 alpha, beta, gamma, delta);
        test_impl(mat, input, w, h, stride, output2, 32, 32, out_w, out_h,
                  out_w, sub_x, sub_y, bd, 0, alpha, beta, gamma, delta);

        for (j = 0; j < out_w * out_h; ++j)
          ASSERT_EQ(output[j], output2[j])
              << "Pixel mismatch at index " << j << " = (" << (j % out_w)
              << ", " << (j / out_w) << ") on iteration " << i;
      }
  }
263
264
265
266

  delete[] input_;
  delete[] output;
  delete[] output2;
267
}
268
#endif  // CONFIG_HIGHBITDEPTH