1 // Copyright 2021 The libgav1 Authors
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14
15 #include "src/warp_prediction.h"
16
17 #include <cstddef>
18 #include <cstdint>
19 #include <ostream>
20
21 #include "absl/base/macros.h"
22 #include "gtest/gtest.h"
23 #include "src/obu_parser.h"
24 #include "src/utils/common.h"
25 #include "src/utils/constants.h"
26 #include "src/utils/types.h"
27 #include "tests/third_party/libvpx/acm_random.h"
28
29 namespace libgav1 {
30 namespace {
31
32 constexpr int16_t kExpectedWarpParamsOutput[10][4] = {
33 {0, 0, 0, 0},
34 {2880, 2880, 2752, 2752},
35 {-1408, -1408, -1472, -1472},
36 {0, 0, 0, 0},
37 {6784, 6784, 6144, 6144}, // Invalid.
38 {-5312, -5312, -5824, -5824},
39 {-3904, -3904, -4160, -4160},
40 {2496, 2496, 2368, 2368},
41 {1024, 1024, 1024, 1024},
42 {-7808, -7808, -8832, -8832}, // Invalid.
43 };
44
45 constexpr bool kExpectedWarpValid[10] = {
46 true, true, true, true, false, true, true, true, true, false,
47 };
48
RandomWarpedParam(int seed_offset,int bits)49 int RandomWarpedParam(int seed_offset, int bits) {
50 libvpx_test::ACMRandom rnd(seed_offset +
51 libvpx_test::ACMRandom::DeterministicSeed());
52 // 1 in 8 chance of generating zero (arbitrary).
53 const bool zero = (rnd.Rand16() & 7) == 0;
54 if (zero) return 0;
55 // Generate uniform values in the range [-(1 << bits), 1] U [1, 1 << bits].
56 const int mask = (1 << bits) - 1;
57 const int value = 1 + (rnd.RandRange(1U << 31) & mask);
58 const bool sign = (rnd.Rand16() & 1) != 0;
59 return sign ? value : -value;
60 }
61
GenerateWarpedModel(GlobalMotion * warp_params,int seed)62 void GenerateWarpedModel(GlobalMotion* warp_params, int seed) {
63 do {
64 warp_params->params[0] =
65 RandomWarpedParam(seed, kWarpedModelPrecisionBits + 6);
66 warp_params->params[1] =
67 RandomWarpedParam(seed, kWarpedModelPrecisionBits + 6);
68 warp_params->params[2] =
69 RandomWarpedParam(seed, kWarpedModelPrecisionBits - 3) +
70 (1 << kWarpedModelPrecisionBits);
71 warp_params->params[3] =
72 RandomWarpedParam(seed, kWarpedModelPrecisionBits - 3);
73 warp_params->params[4] =
74 RandomWarpedParam(seed, kWarpedModelPrecisionBits - 3);
75 warp_params->params[5] =
76 RandomWarpedParam(seed, kWarpedModelPrecisionBits - 3) +
77 (1 << kWarpedModelPrecisionBits);
78 } while (warp_params->params[2] == 0);
79 }
80
TEST(WarpPredictionTest,SetupShear)81 TEST(WarpPredictionTest, SetupShear) {
82 for (size_t i = 0; i < ABSL_ARRAYSIZE(kExpectedWarpParamsOutput); ++i) {
83 GlobalMotion warp_params;
84 GenerateWarpedModel(&warp_params, static_cast<int>(i));
85 const bool warp_valid = SetupShear(&warp_params);
86
87 SCOPED_TRACE(testing::Message() << "Test failure at iteration: " << i);
88 EXPECT_EQ(warp_valid, kExpectedWarpValid[i]);
89 EXPECT_EQ(warp_params.alpha, kExpectedWarpParamsOutput[i][0]);
90 EXPECT_EQ(warp_params.beta, kExpectedWarpParamsOutput[i][1]);
91 EXPECT_EQ(warp_params.gamma, kExpectedWarpParamsOutput[i][2]);
92 EXPECT_EQ(warp_params.delta, kExpectedWarpParamsOutput[i][3]);
93 }
94
95 // Test signed shift behavior in delta and gamma generation.
96 GlobalMotion warp_params;
97 warp_params.params[0] = 24748;
98 warp_params.params[1] = -142530;
99 warp_params.params[2] = 65516;
100 warp_params.params[3] = -640;
101 warp_params.params[4] = 256;
102 warp_params.params[5] = 65310;
103 EXPECT_TRUE(SetupShear(&warp_params));
104 EXPECT_EQ(warp_params.alpha, 0);
105 EXPECT_EQ(warp_params.beta, -640);
106 EXPECT_EQ(warp_params.gamma, 256);
107 EXPECT_EQ(warp_params.delta, -192);
108
109 warp_params.params[0] = 24748;
110 warp_params.params[1] = -142530;
111 warp_params.params[2] = 61760;
112 warp_params.params[3] = -640;
113 warp_params.params[4] = -13312;
114 warp_params.params[5] = 65310;
115 EXPECT_TRUE(SetupShear(&warp_params));
116 EXPECT_EQ(warp_params.alpha, -3776);
117 EXPECT_EQ(warp_params.beta, -640);
118 EXPECT_EQ(warp_params.gamma, -14144);
119 EXPECT_EQ(warp_params.delta, -384);
120 }
121
122 struct WarpInputParam {
WarpInputParamlibgav1::__anona5d33bf00111::WarpInputParam123 WarpInputParam(int num_samples, int block_width4x4, int block_height4x4)
124 : num_samples(num_samples),
125 block_width4x4(block_width4x4),
126 block_height4x4(block_height4x4) {}
127 int num_samples;
128 int block_width4x4;
129 int block_height4x4;
130 };
131
operator <<(std::ostream & os,const WarpInputParam & param)132 std::ostream& operator<<(std::ostream& os, const WarpInputParam& param) {
133 return os << "num_samples: " << param.num_samples
134 << ", block_(width/height)4x4: " << param.block_width4x4 << "x"
135 << param.block_height4x4;
136 }
137
138 const WarpInputParam warp_test_param[] = {
139 // sample = 1.
140 WarpInputParam(1, 1, 1),
141 WarpInputParam(1, 1, 2),
142 WarpInputParam(1, 2, 1),
143 WarpInputParam(1, 2, 2),
144 WarpInputParam(1, 2, 4),
145 WarpInputParam(1, 4, 2),
146 WarpInputParam(1, 4, 4),
147 WarpInputParam(1, 4, 8),
148 WarpInputParam(1, 8, 4),
149 WarpInputParam(1, 8, 8),
150 WarpInputParam(1, 8, 16),
151 WarpInputParam(1, 16, 8),
152 WarpInputParam(1, 16, 16),
153 WarpInputParam(1, 16, 32),
154 WarpInputParam(1, 32, 16),
155 WarpInputParam(1, 32, 32),
156 // sample = 8.
157 WarpInputParam(8, 1, 1),
158 WarpInputParam(8, 1, 2),
159 WarpInputParam(8, 2, 1),
160 WarpInputParam(8, 2, 2),
161 WarpInputParam(8, 2, 4),
162 WarpInputParam(8, 4, 2),
163 WarpInputParam(8, 4, 4),
164 WarpInputParam(8, 4, 8),
165 WarpInputParam(8, 8, 4),
166 WarpInputParam(8, 8, 8),
167 WarpInputParam(8, 8, 16),
168 WarpInputParam(8, 16, 8),
169 WarpInputParam(8, 16, 16),
170 WarpInputParam(8, 16, 32),
171 WarpInputParam(8, 32, 16),
172 WarpInputParam(8, 32, 32),
173 };
174
175 constexpr bool kExpectedWarpEstimationValid[2] = {false, true};
176
177 constexpr int kExpectedWarpEstimationOutput[16][6] = {
178 {8388607, 8388607, 57345, -8191, -8191, 57345},
179 {8388607, 8388607, 57345, -8191, -8191, 57345},
180 {8388607, 8388607, 57345, -8191, -8191, 57345},
181 {8388607, 8388607, 57345, -8191, -8191, 57345},
182 {8388607, 8388607, 57345, -8191, -8191, 57345},
183 {8388607, 8388607, 57345, -8191, -8191, 57345},
184 {8388607, 8388607, 57345, -8191, -8191, 57345},
185 {8388607, 8388607, 57345, -8191, -8191, 57345},
186 {8388607, 8388607, 57345, -8191, -8191, 57345},
187 {8388607, 8388607, 57345, -8191, -8191, 57345},
188 {2146296, 1589240, 57345, 8191, -8191, 73727},
189 {1753128, 1196072, 73727, -8191, 8191, 57345},
190 {-8388608, -8388608, 73727, 8191, 8191, 73727},
191 {-4435485, -8388608, 65260, 8191, 8191, 73727},
192 {-8388608, -7552929, 73727, 8191, 8191, 68240},
193 {-8388608, -8388608, 73727, 8191, 8191, 70800},
194 };
195
196 class WarpEstimationTest : public testing::TestWithParam<WarpInputParam> {
197 public:
198 WarpEstimationTest() = default;
199 ~WarpEstimationTest() override = default;
200
201 protected:
202 WarpInputParam param_ = GetParam();
203 };
204
TEST_P(WarpEstimationTest,WarpEstimation)205 TEST_P(WarpEstimationTest, WarpEstimation) {
206 // Set input params.
207 libvpx_test::ACMRandom rnd(libvpx_test::ACMRandom::DeterministicSeed());
208 const int row4x4 = rnd.Rand8();
209 const int column4x4 = rnd.Rand8();
210 MotionVector mv;
211 mv.mv[0] = rnd.Rand8();
212 mv.mv[1] = rnd.Rand8();
213 int candidates[kMaxLeastSquaresSamples][4];
214 for (int i = 0; i < param_.num_samples; ++i) {
215 // Make candidates relative to the top left of frame.
216 candidates[i][0] = rnd.Rand8() + MultiplyBy32(row4x4);
217 candidates[i][1] = rnd.Rand8() + MultiplyBy32(column4x4);
218 candidates[i][2] = rnd.Rand8() + MultiplyBy32(row4x4);
219 candidates[i][3] = rnd.Rand8() + MultiplyBy32(column4x4);
220 }
221
222 // Get output.
223 GlobalMotion warp_params;
224 const bool warp_success = WarpEstimation(
225 param_.num_samples, param_.block_width4x4, param_.block_height4x4, row4x4,
226 column4x4, mv, candidates, &warp_params);
227 if (param_.num_samples == 1) {
228 EXPECT_EQ(warp_success, kExpectedWarpEstimationValid[0]);
229 } else {
230 EXPECT_EQ(warp_success, kExpectedWarpEstimationValid[1]);
231 int index = FloorLog2(param_.block_width4x4) * 3 - 1;
232 if (param_.block_width4x4 == param_.block_height4x4) {
233 index += 1;
234 } else if (param_.block_width4x4 < param_.block_height4x4) {
235 index += 2;
236 }
237 for (size_t i = 0; i < ABSL_ARRAYSIZE(warp_params.params); ++i) {
238 EXPECT_EQ(warp_params.params[i], kExpectedWarpEstimationOutput[index][i]);
239 }
240 }
241 }
242
243 INSTANTIATE_TEST_SUITE_P(WarpFuncTest, WarpEstimationTest,
244 testing::ValuesIn(warp_test_param));
245 } // namespace
246 } // namespace libgav1
247