OLD | NEW |
1 /* | 1 /* |
2 * Copyright (c) 2012 The WebM project authors. All Rights Reserved. | 2 * Copyright (c) 2012 The WebM project authors. All Rights Reserved. |
3 * | 3 * |
4 * Use of this source code is governed by a BSD-style license | 4 * Use of this source code is governed by a BSD-style license |
5 * that can be found in the LICENSE file in the root of the source | 5 * that can be found in the LICENSE file in the root of the source |
6 * tree. An additional intellectual property rights grant can be found | 6 * tree. An additional intellectual property rights grant can be found |
7 * in the file PATENTS. All contributing project authors may | 7 * in the file PATENTS. All contributing project authors may |
8 * be found in the AUTHORS file in the root of the source tree. | 8 * be found in the AUTHORS file in the root of the source tree. |
9 */ | 9 */ |
10 | 10 |
11 #include <math.h> | 11 #include <math.h> |
12 #include <stdlib.h> | 12 #include <stdlib.h> |
13 #include <string.h> | 13 #include <string.h> |
14 | 14 |
15 #include "third_party/googletest/src/include/gtest/gtest.h" | 15 #include "third_party/googletest/src/include/gtest/gtest.h" |
| 16 #include "test/acm_random.h" |
| 17 #include "test/clear_system_state.h" |
| 18 #include "test/register_state_check.h" |
| 19 #include "test/util.h" |
16 | 20 |
17 extern "C" { | 21 extern "C" { |
| 22 #include "vp9/common/vp9_entropy.h" |
18 #include "./vp9_rtcd.h" | 23 #include "./vp9_rtcd.h" |
19 } | 24 void vp9_idct4x4_16_add_c(const int16_t *input, uint8_t *output, int pitch); |
20 | 25 } |
21 #include "test/acm_random.h" | |
22 #include "vpx/vpx_integer.h" | 26 #include "vpx/vpx_integer.h" |
23 #include "vpx_ports/mem.h" | |
24 | 27 |
25 using libvpx_test::ACMRandom; | 28 using libvpx_test::ACMRandom; |
26 | 29 |
27 namespace { | 30 namespace { |
28 void fdct4x4(int16_t *in, int16_t *out, uint8_t* /*dst*/, | 31 const int kNumCoeffs = 16; |
29 int stride, int /*tx_type*/) { | 32 typedef void (*fdct_t)(const int16_t *in, int16_t *out, int stride); |
| 33 typedef void (*idct_t)(const int16_t *in, uint8_t *out, int stride); |
| 34 typedef void (*fht_t) (const int16_t *in, int16_t *out, int stride, |
| 35 int tx_type); |
| 36 typedef void (*iht_t) (const int16_t *in, uint8_t *out, int stride, |
| 37 int tx_type); |
| 38 |
| 39 typedef std::tr1::tuple<fdct_t, idct_t, int> dct_4x4_param_t; |
| 40 typedef std::tr1::tuple<fht_t, iht_t, int> ht_4x4_param_t; |
| 41 |
| 42 void fdct4x4_ref(const int16_t *in, int16_t *out, int stride, int tx_type) { |
30 vp9_fdct4x4_c(in, out, stride); | 43 vp9_fdct4x4_c(in, out, stride); |
31 } | 44 } |
32 void idct4x4_add(int16_t* /*in*/, int16_t *out, uint8_t *dst, | 45 |
33 int stride, int /*tx_type*/) { | 46 void fht4x4_ref(const int16_t *in, int16_t *out, int stride, int tx_type) { |
34 vp9_idct4x4_16_add_c(out, dst, stride); | |
35 } | |
36 void fht4x4(int16_t *in, int16_t *out, uint8_t* /*dst*/, | |
37 int stride, int tx_type) { | |
38 vp9_short_fht4x4_c(in, out, stride, tx_type); | 47 vp9_short_fht4x4_c(in, out, stride, tx_type); |
39 } | 48 } |
40 void iht4x4_add(int16_t* /*in*/, int16_t *out, uint8_t *dst, | 49 |
41 int stride, int tx_type) { | 50 class Trans4x4TestBase { |
42 vp9_iht4x4_16_add_c(out, dst, stride, tx_type); | |
43 } | |
44 | |
45 class FwdTrans4x4Test : public ::testing::TestWithParam<int> { | |
46 public: | 51 public: |
47 virtual ~FwdTrans4x4Test() {} | 52 virtual ~Trans4x4TestBase() {} |
| 53 |
| 54 protected: |
| 55 virtual void RunFwdTxfm(const int16_t *in, int16_t *out, int stride) = 0; |
| 56 |
| 57 virtual void RunInvTxfm(const int16_t *out, uint8_t *dst, int stride) = 0; |
| 58 |
| 59 void RunAccuracyCheck() { |
| 60 ACMRandom rnd(ACMRandom::DeterministicSeed()); |
| 61 uint32_t max_error = 0; |
| 62 int64_t total_error = 0; |
| 63 const int count_test_block = 10000; |
| 64 for (int i = 0; i < count_test_block; ++i) { |
| 65 DECLARE_ALIGNED_ARRAY(16, int16_t, test_input_block, kNumCoeffs); |
| 66 DECLARE_ALIGNED_ARRAY(16, int16_t, test_temp_block, kNumCoeffs); |
| 67 DECLARE_ALIGNED_ARRAY(16, uint8_t, dst, kNumCoeffs); |
| 68 DECLARE_ALIGNED_ARRAY(16, uint8_t, src, kNumCoeffs); |
| 69 |
| 70 // Initialize a test block with input range [-255, 255]. |
| 71 for (int j = 0; j < kNumCoeffs; ++j) { |
| 72 src[j] = rnd.Rand8(); |
| 73 dst[j] = rnd.Rand8(); |
| 74 test_input_block[j] = src[j] - dst[j]; |
| 75 } |
| 76 |
| 77 REGISTER_STATE_CHECK(RunFwdTxfm(test_input_block, |
| 78 test_temp_block, pitch_)); |
| 79 REGISTER_STATE_CHECK(RunInvTxfm(test_temp_block, dst, pitch_)); |
| 80 |
| 81 for (int j = 0; j < kNumCoeffs; ++j) { |
| 82 const uint32_t diff = dst[j] - src[j]; |
| 83 const uint32_t error = diff * diff; |
| 84 if (max_error < error) |
| 85 max_error = error; |
| 86 total_error += error; |
| 87 } |
| 88 } |
| 89 |
| 90 EXPECT_GE(1u, max_error) |
| 91 << "Error: 4x4 FHT/IHT has an individual round trip error > 1"; |
| 92 |
| 93 EXPECT_GE(count_test_block , total_error) |
| 94 << "Error: 4x4 FHT/IHT has average round trip error > 1 per block"; |
| 95 } |
| 96 |
| 97 void RunCoeffCheck() { |
| 98 ACMRandom rnd(ACMRandom::DeterministicSeed()); |
| 99 const int count_test_block = 5000; |
| 100 DECLARE_ALIGNED_ARRAY(16, int16_t, input_block, kNumCoeffs); |
| 101 DECLARE_ALIGNED_ARRAY(16, int16_t, output_ref_block, kNumCoeffs); |
| 102 DECLARE_ALIGNED_ARRAY(16, int16_t, output_block, kNumCoeffs); |
| 103 |
| 104 for (int i = 0; i < count_test_block; ++i) { |
| 105 // Initialize a test block with input range [-255, 255]. |
| 106 for (int j = 0; j < kNumCoeffs; ++j) |
| 107 input_block[j] = rnd.Rand8() - rnd.Rand8(); |
| 108 |
| 109 fwd_txfm_ref(input_block, output_ref_block, pitch_, tx_type_); |
| 110 REGISTER_STATE_CHECK(RunFwdTxfm(input_block, output_block, pitch_)); |
| 111 |
| 112 // The minimum quant value is 4. |
| 113 for (int j = 0; j < kNumCoeffs; ++j) |
| 114 EXPECT_EQ(output_block[j], output_ref_block[j]); |
| 115 } |
| 116 } |
| 117 |
| 118 void RunMemCheck() { |
| 119 ACMRandom rnd(ACMRandom::DeterministicSeed()); |
| 120 const int count_test_block = 5000; |
| 121 DECLARE_ALIGNED_ARRAY(16, int16_t, input_block, kNumCoeffs); |
| 122 DECLARE_ALIGNED_ARRAY(16, int16_t, input_extreme_block, kNumCoeffs); |
| 123 DECLARE_ALIGNED_ARRAY(16, int16_t, output_ref_block, kNumCoeffs); |
| 124 DECLARE_ALIGNED_ARRAY(16, int16_t, output_block, kNumCoeffs); |
| 125 |
| 126 for (int i = 0; i < count_test_block; ++i) { |
| 127 // Initialize a test block with input range [-255, 255]. |
| 128 for (int j = 0; j < kNumCoeffs; ++j) { |
| 129 input_block[j] = rnd.Rand8() - rnd.Rand8(); |
| 130 input_extreme_block[j] = rnd.Rand8() % 2 ? 255 : -255; |
| 131 } |
| 132 if (i == 0) |
| 133 for (int j = 0; j < kNumCoeffs; ++j) |
| 134 input_extreme_block[j] = 255; |
| 135 if (i == 1) |
| 136 for (int j = 0; j < kNumCoeffs; ++j) |
| 137 input_extreme_block[j] = -255; |
| 138 |
| 139 fwd_txfm_ref(input_extreme_block, output_ref_block, pitch_, tx_type_); |
| 140 REGISTER_STATE_CHECK(RunFwdTxfm(input_extreme_block, |
| 141 output_block, pitch_)); |
| 142 |
| 143 // The minimum quant value is 4. |
| 144 for (int j = 0; j < kNumCoeffs; ++j) { |
| 145 EXPECT_EQ(output_block[j], output_ref_block[j]); |
| 146 EXPECT_GE(4 * DCT_MAX_VALUE, abs(output_block[j])) |
| 147 << "Error: 16x16 FDCT has coefficient larger than 4*DCT_MAX_VALUE"; |
| 148 } |
| 149 } |
| 150 } |
| 151 |
| 152 void RunInvAccuracyCheck() { |
| 153 ACMRandom rnd(ACMRandom::DeterministicSeed()); |
| 154 const int count_test_block = 1000; |
| 155 DECLARE_ALIGNED_ARRAY(16, int16_t, in, kNumCoeffs); |
| 156 DECLARE_ALIGNED_ARRAY(16, int16_t, coeff, kNumCoeffs); |
| 157 DECLARE_ALIGNED_ARRAY(16, uint8_t, dst, kNumCoeffs); |
| 158 DECLARE_ALIGNED_ARRAY(16, uint8_t, src, kNumCoeffs); |
| 159 |
| 160 for (int i = 0; i < count_test_block; ++i) { |
| 161 // Initialize a test block with input range [-255, 255]. |
| 162 for (int j = 0; j < kNumCoeffs; ++j) { |
| 163 src[j] = rnd.Rand8(); |
| 164 dst[j] = rnd.Rand8(); |
| 165 in[j] = src[j] - dst[j]; |
| 166 } |
| 167 |
| 168 fwd_txfm_ref(in, coeff, pitch_, tx_type_); |
| 169 |
| 170 REGISTER_STATE_CHECK(RunInvTxfm(coeff, dst, pitch_)); |
| 171 |
| 172 for (int j = 0; j < kNumCoeffs; ++j) { |
| 173 const uint32_t diff = dst[j] - src[j]; |
| 174 const uint32_t error = diff * diff; |
| 175 EXPECT_GE(1u, error) |
| 176 << "Error: 16x16 IDCT has error " << error |
| 177 << " at index " << j; |
| 178 } |
| 179 } |
| 180 } |
| 181 |
| 182 int pitch_; |
| 183 int tx_type_; |
| 184 fht_t fwd_txfm_ref; |
| 185 }; |
| 186 |
| 187 class Trans4x4DCT |
| 188 : public Trans4x4TestBase, |
| 189 public ::testing::TestWithParam<dct_4x4_param_t> { |
| 190 public: |
| 191 virtual ~Trans4x4DCT() {} |
| 192 |
48 virtual void SetUp() { | 193 virtual void SetUp() { |
49 tx_type_ = GetParam(); | 194 fwd_txfm_ = GET_PARAM(0); |
50 if (tx_type_ == 0) { | 195 inv_txfm_ = GET_PARAM(1); |
51 fwd_txfm_ = fdct4x4; | 196 tx_type_ = GET_PARAM(2); |
52 inv_txfm_ = idct4x4_add; | 197 pitch_ = 4; |
53 } else { | 198 fwd_txfm_ref = fdct4x4_ref; |
54 fwd_txfm_ = fht4x4; | 199 } |
55 inv_txfm_ = iht4x4_add; | 200 virtual void TearDown() { libvpx_test::ClearSystemState(); } |
56 } | |
57 } | |
58 | 201 |
59 protected: | 202 protected: |
60 void RunFwdTxfm(int16_t *in, int16_t *out, uint8_t *dst, | 203 void RunFwdTxfm(const int16_t *in, int16_t *out, int stride) { |
61 int stride, int tx_type) { | 204 fwd_txfm_(in, out, stride); |
62 (*fwd_txfm_)(in, out, dst, stride, tx_type); | 205 } |
63 } | 206 void RunInvTxfm(const int16_t *out, uint8_t *dst, int stride) { |
64 | 207 inv_txfm_(out, dst, stride); |
65 void RunInvTxfm(int16_t *in, int16_t *out, uint8_t *dst, | 208 } |
66 int stride, int tx_type) { | 209 |
67 (*inv_txfm_)(in, out, dst, stride, tx_type); | 210 fdct_t fwd_txfm_; |
68 } | 211 idct_t inv_txfm_; |
69 | |
70 int tx_type_; | |
71 void (*fwd_txfm_)(int16_t *in, int16_t *out, uint8_t *dst, | |
72 int stride, int tx_type); | |
73 void (*inv_txfm_)(int16_t *in, int16_t *out, uint8_t *dst, | |
74 int stride, int tx_type); | |
75 }; | 212 }; |
76 | 213 |
77 TEST_P(FwdTrans4x4Test, SignBiasCheck) { | 214 TEST_P(Trans4x4DCT, AccuracyCheck) { |
78 ACMRandom rnd(ACMRandom::DeterministicSeed()); | 215 RunAccuracyCheck(); |
79 DECLARE_ALIGNED_ARRAY(16, int16_t, test_input_block, 16); | 216 } |
80 DECLARE_ALIGNED_ARRAY(16, int16_t, test_output_block, 16); | 217 |
81 const int pitch = 4; | 218 TEST_P(Trans4x4DCT, CoeffCheck) { |
82 int count_sign_block[16][2]; | 219 RunCoeffCheck(); |
83 const int count_test_block = 1000000; | 220 } |
84 | 221 |
85 memset(count_sign_block, 0, sizeof(count_sign_block)); | 222 TEST_P(Trans4x4DCT, MemCheck) { |
86 for (int i = 0; i < count_test_block; ++i) { | 223 RunMemCheck(); |
87 // Initialize a test block with input range [-255, 255]. | 224 } |
88 for (int j = 0; j < 16; ++j) | 225 |
89 test_input_block[j] = rnd.Rand8() - rnd.Rand8(); | 226 TEST_P(Trans4x4DCT, InvAccuracyCheck) { |
90 | 227 RunInvAccuracyCheck(); |
91 RunFwdTxfm(test_input_block, test_output_block, NULL, pitch, tx_type_); | 228 } |
92 | 229 |
93 for (int j = 0; j < 16; ++j) { | 230 class Trans4x4HT |
94 if (test_output_block[j] < 0) | 231 : public Trans4x4TestBase, |
95 ++count_sign_block[j][0]; | 232 public ::testing::TestWithParam<ht_4x4_param_t> { |
96 else if (test_output_block[j] > 0) | 233 public: |
97 ++count_sign_block[j][1]; | 234 virtual ~Trans4x4HT() {} |
98 } | 235 |
99 } | 236 virtual void SetUp() { |
100 | 237 fwd_txfm_ = GET_PARAM(0); |
101 for (int j = 0; j < 16; ++j) { | 238 inv_txfm_ = GET_PARAM(1); |
102 const bool bias_acceptable = (abs(count_sign_block[j][0] - | 239 tx_type_ = GET_PARAM(2); |
103 count_sign_block[j][1]) < 10000); | 240 pitch_ = 4; |
104 EXPECT_TRUE(bias_acceptable) | 241 fwd_txfm_ref = fht4x4_ref; |
105 << "Error: 4x4 FDCT/FHT has a sign bias > 1%" | 242 } |
106 << " for input range [-255, 255] at index " << j | 243 virtual void TearDown() { libvpx_test::ClearSystemState(); } |
107 << " tx_type " << tx_type_; | 244 |
108 } | 245 protected: |
109 | 246 void RunFwdTxfm(const int16_t *in, int16_t *out, int stride) { |
110 memset(count_sign_block, 0, sizeof(count_sign_block)); | 247 fwd_txfm_(in, out, stride, tx_type_); |
111 for (int i = 0; i < count_test_block; ++i) { | 248 } |
112 // Initialize a test block with input range [-15, 15]. | 249 |
113 for (int j = 0; j < 16; ++j) | 250 void RunInvTxfm(const int16_t *out, uint8_t *dst, int stride) { |
114 test_input_block[j] = (rnd.Rand8() >> 4) - (rnd.Rand8() >> 4); | 251 inv_txfm_(out, dst, stride, tx_type_); |
115 | 252 } |
116 RunFwdTxfm(test_input_block, test_output_block, NULL, pitch, tx_type_); | 253 |
117 | 254 fht_t fwd_txfm_; |
118 for (int j = 0; j < 16; ++j) { | 255 iht_t inv_txfm_; |
119 if (test_output_block[j] < 0) | 256 }; |
120 ++count_sign_block[j][0]; | 257 |
121 else if (test_output_block[j] > 0) | 258 TEST_P(Trans4x4HT, AccuracyCheck) { |
122 ++count_sign_block[j][1]; | 259 RunAccuracyCheck(); |
123 } | 260 } |
124 } | 261 |
125 | 262 TEST_P(Trans4x4HT, CoeffCheck) { |
126 for (int j = 0; j < 16; ++j) { | 263 RunCoeffCheck(); |
127 const bool bias_acceptable = (abs(count_sign_block[j][0] - | 264 } |
128 count_sign_block[j][1]) < 100000); | 265 |
129 EXPECT_TRUE(bias_acceptable) | 266 TEST_P(Trans4x4HT, MemCheck) { |
130 << "Error: 4x4 FDCT/FHT has a sign bias > 10%" | 267 RunMemCheck(); |
131 << " for input range [-15, 15] at index " << j; | 268 } |
132 } | 269 |
133 } | 270 TEST_P(Trans4x4HT, InvAccuracyCheck) { |
134 | 271 RunInvAccuracyCheck(); |
135 TEST_P(FwdTrans4x4Test, RoundTripErrorCheck) { | 272 } |
136 ACMRandom rnd(ACMRandom::DeterministicSeed()); | 273 |
137 | 274 using std::tr1::make_tuple; |
138 int max_error = 0; | 275 |
139 int total_error = 0; | 276 INSTANTIATE_TEST_CASE_P( |
140 const int count_test_block = 1000000; | 277 C, Trans4x4DCT, |
141 for (int i = 0; i < count_test_block; ++i) { | 278 ::testing::Values( |
142 DECLARE_ALIGNED_ARRAY(16, int16_t, test_input_block, 16); | 279 make_tuple(&vp9_fdct4x4_c, &vp9_idct4x4_16_add_c, 0))); |
143 DECLARE_ALIGNED_ARRAY(16, int16_t, test_temp_block, 16); | 280 INSTANTIATE_TEST_CASE_P( |
144 DECLARE_ALIGNED_ARRAY(16, uint8_t, dst, 16); | 281 C, Trans4x4HT, |
145 DECLARE_ALIGNED_ARRAY(16, uint8_t, src, 16); | 282 ::testing::Values( |
146 | 283 make_tuple(&vp9_short_fht4x4_c, &vp9_iht4x4_16_add_c, 0), |
147 for (int j = 0; j < 16; ++j) { | 284 make_tuple(&vp9_short_fht4x4_c, &vp9_iht4x4_16_add_c, 1), |
148 src[j] = rnd.Rand8(); | 285 make_tuple(&vp9_short_fht4x4_c, &vp9_iht4x4_16_add_c, 2), |
149 dst[j] = rnd.Rand8(); | 286 make_tuple(&vp9_short_fht4x4_c, &vp9_iht4x4_16_add_c, 3))); |
150 } | 287 |
151 // Initialize a test block with input range [-255, 255]. | 288 #if HAVE_SSE2 |
152 for (int j = 0; j < 16; ++j) | 289 INSTANTIATE_TEST_CASE_P( |
153 test_input_block[j] = src[j] - dst[j]; | 290 SSE2, Trans4x4DCT, |
154 | 291 ::testing::Values( |
155 const int pitch = 4; | 292 make_tuple(&vp9_fdct4x4_sse2, |
156 RunFwdTxfm(test_input_block, test_temp_block, dst, pitch, tx_type_); | 293 &vp9_idct4x4_16_add_sse2, 0))); |
157 | 294 INSTANTIATE_TEST_CASE_P( |
158 for (int j = 0; j < 16; ++j) { | 295 SSE2, Trans4x4HT, |
159 if (test_temp_block[j] > 0) { | 296 ::testing::Values( |
160 test_temp_block[j] += 2; | 297 make_tuple(&vp9_short_fht4x4_sse2, &vp9_iht4x4_16_add_sse2, 0), |
161 test_temp_block[j] /= 4; | 298 make_tuple(&vp9_short_fht4x4_sse2, &vp9_iht4x4_16_add_sse2, 1), |
162 test_temp_block[j] *= 4; | 299 make_tuple(&vp9_short_fht4x4_sse2, &vp9_iht4x4_16_add_sse2, 2), |
163 } else { | 300 make_tuple(&vp9_short_fht4x4_sse2, &vp9_iht4x4_16_add_sse2, 3))); |
164 test_temp_block[j] -= 2; | 301 #endif |
165 test_temp_block[j] /= 4; | 302 |
166 test_temp_block[j] *= 4; | |
167 } | |
168 } | |
169 | |
170 // inverse transform and reconstruct the pixel block | |
171 RunInvTxfm(test_input_block, test_temp_block, dst, pitch, tx_type_); | |
172 | |
173 for (int j = 0; j < 16; ++j) { | |
174 const int diff = dst[j] - src[j]; | |
175 const int error = diff * diff; | |
176 if (max_error < error) | |
177 max_error = error; | |
178 total_error += error; | |
179 } | |
180 } | |
181 EXPECT_GE(1, max_error) | |
182 << "Error: FDCT/IDCT or FHT/IHT has an individual roundtrip error > 1"; | |
183 | |
184 EXPECT_GE(count_test_block, total_error) | |
185 << "Error: FDCT/IDCT or FHT/IHT has average " | |
186 << "roundtrip error > 1 per block"; | |
187 } | |
188 | |
189 INSTANTIATE_TEST_CASE_P(VP9, FwdTrans4x4Test, ::testing::Range(0, 4)); | |
190 } // namespace | 303 } // namespace |
OLD | NEW |