1 /*
2 * Copyright (c) 2021, Alliance for Open Media. All rights reserved
3 *
4 * This source code is subject to the terms of the BSD 2 Clause License and
5 * the Alliance for Open Media Patent License 1.0. If the BSD 2 Clause License
6 * was not distributed with this source code in the LICENSE file, you can
7 * obtain it at www.aomedia.org/license/software. If the Alliance for Open
8 * Media Patent License 1.0 was not distributed with this source code in the
9 * PATENTS file, you can obtain it at www.aomedia.org/license/patent.
10 */
11
12 #include <cstdlib>
13 #include <vector>
14
15 #include "av1/encoder/cost.h"
16 #include "av1/encoder/tpl_model.h"
17 #include "av1/encoder/encoder.h"
18 #include "third_party/googletest/src/googletest/include/gtest/gtest.h"
19
20 namespace {
21
laplace_prob(double q_step,double b,double zero_bin_ratio,int qcoeff)22 double laplace_prob(double q_step, double b, double zero_bin_ratio,
23 int qcoeff) {
24 int abs_qcoeff = abs(qcoeff);
25 double z0 = fmax(exp(-zero_bin_ratio / 2 * q_step / b), TPL_EPSILON);
26 if (abs_qcoeff == 0) {
27 double p0 = 1 - z0;
28 return p0;
29 } else {
30 assert(abs_qcoeff > 0);
31 double z = fmax(exp(-q_step / b), TPL_EPSILON);
32 double p = z0 / 2 * (1 - z) * pow(z, abs_qcoeff - 1);
33 return p;
34 }
35 }
TEST(TplModelTest,ExponentialEntropyBoundaryTest1)36 TEST(TplModelTest, ExponentialEntropyBoundaryTest1) {
37 double b = 0;
38 double q_step = 1;
39 double entropy = av1_exponential_entropy(q_step, b);
40 EXPECT_NEAR(entropy, 0, 0.00001);
41 }
42
TEST(TplModelTest,TransformCoeffEntropyTest1)43 TEST(TplModelTest, TransformCoeffEntropyTest1) {
44 // Check the consistency between av1_estimate_coeff_entropy() and
45 // laplace_prob()
46 double b = 1;
47 double q_step = 1;
48 double zero_bin_ratio = 2;
49 for (int qcoeff = -256; qcoeff < 256; ++qcoeff) {
50 double rate = av1_estimate_coeff_entropy(q_step, b, zero_bin_ratio, qcoeff);
51 double prob = laplace_prob(q_step, b, zero_bin_ratio, qcoeff);
52 double ref_rate = -log2(prob);
53 EXPECT_DOUBLE_EQ(rate, ref_rate);
54 }
55 }
56
TEST(TplModelTest,TransformCoeffEntropyTest2)57 TEST(TplModelTest, TransformCoeffEntropyTest2) {
58 // Check the consistency between av1_estimate_coeff_entropy(), laplace_prob()
59 // and av1_laplace_entropy()
60 double b = 1;
61 double q_step = 1;
62 double zero_bin_ratio = 2;
63 double est_expected_rate = 0;
64 for (int qcoeff = -20; qcoeff < 20; ++qcoeff) {
65 double rate = av1_estimate_coeff_entropy(q_step, b, zero_bin_ratio, qcoeff);
66 double prob = laplace_prob(q_step, b, zero_bin_ratio, qcoeff);
67 est_expected_rate += prob * rate;
68 }
69 double expected_rate = av1_laplace_entropy(q_step, b, zero_bin_ratio);
70 EXPECT_NEAR(expected_rate, est_expected_rate, 0.001);
71 }
72
TEST(TplModelTest,DeltaRateCostZeroFlow)73 TEST(TplModelTest, DeltaRateCostZeroFlow) {
74 // When srcrf_dist equal to recrf_dist, av1_delta_rate_cost should return 0
75 int64_t srcrf_dist = 256;
76 int64_t recrf_dist = 256;
77 int64_t delta_rate = 512;
78 int pixel_num = 256;
79 int64_t rate_cost =
80 av1_delta_rate_cost(delta_rate, recrf_dist, srcrf_dist, pixel_num);
81 EXPECT_EQ(rate_cost, 0);
82 }
83
84 // a reference function of av1_delta_rate_cost() with delta_rate using bit as
85 // basic unit
ref_delta_rate_cost(int64_t delta_rate,double src_rec_ratio,int pixel_count)86 double ref_delta_rate_cost(int64_t delta_rate, double src_rec_ratio,
87 int pixel_count) {
88 assert(src_rec_ratio <= 1 && src_rec_ratio >= 0);
89 double bits_per_pixel = (double)delta_rate / pixel_count;
90 double p = pow(2, bits_per_pixel);
91 double flow_rate_per_pixel =
92 sqrt(p * p / (src_rec_ratio * p * p + (1 - src_rec_ratio)));
93 double rate_cost = pixel_count * log2(flow_rate_per_pixel);
94 return rate_cost;
95 }
96
TEST(TplModelTest,DeltaRateCostReference)97 TEST(TplModelTest, DeltaRateCostReference) {
98 const int64_t scale = TPL_DEP_COST_SCALE_LOG2 + AV1_PROB_COST_SHIFT;
99 std::vector<int64_t> srcrf_dist_arr = { 256, 257, 312 };
100 std::vector<int64_t> recrf_dist_arr = { 512, 288, 620 };
101 std::vector<int64_t> delta_rate_arr = { 10, 278, 100 };
102 for (size_t t = 0; t < srcrf_dist_arr.size(); ++t) {
103 int64_t srcrf_dist = srcrf_dist_arr[t];
104 int64_t recrf_dist = recrf_dist_arr[t];
105 int64_t delta_rate = delta_rate_arr[t];
106 int64_t scaled_delta_rate = delta_rate << scale;
107 int pixel_count = 256;
108 int64_t rate_cost = av1_delta_rate_cost(scaled_delta_rate, recrf_dist,
109 srcrf_dist, pixel_count);
110 rate_cost >>= scale;
111 double src_rec_ratio = (double)srcrf_dist / recrf_dist;
112 double ref_rate_cost =
113 ref_delta_rate_cost(delta_rate, src_rec_ratio, pixel_count);
114 EXPECT_NEAR((double)rate_cost, ref_rate_cost, 1);
115 }
116 }
117
TEST(TplModelTest,GetOverlapAreaHasOverlap)118 TEST(TplModelTest, GetOverlapAreaHasOverlap) {
119 // The block a's area is [10, 17) x [18, 24).
120 // The block b's area is [8, 15) x [17, 23).
121 // The overlapping area between block a and block b is [10, 15) x [18, 23).
122 // Therefore, the size of the area is (15 - 10) * (23 - 18) = 25.
123 int row_a = 10;
124 int col_a = 18;
125 int row_b = 8;
126 int col_b = 17;
127 int height = 7;
128 int width = 6;
129 int overlap_area =
130 av1_get_overlap_area(row_a, col_a, row_b, col_b, width, height);
131 EXPECT_EQ(overlap_area, 25);
132 }
133
TEST(TplModelTest,GetOverlapAreaNoOverlap)134 TEST(TplModelTest, GetOverlapAreaNoOverlap) {
135 // The block a's area is [10, 14) x [18, 22).
136 // The block b's area is [5, 9) x [5, 9).
137 // Threre is no overlapping area between block a and block b.
138 // Therefore, the return value should be zero.
139 int row_a = 10;
140 int col_a = 18;
141 int row_b = 5;
142 int col_b = 5;
143 int height = 4;
144 int width = 4;
145 int overlap_area =
146 av1_get_overlap_area(row_a, col_a, row_b, col_b, width, height);
147 EXPECT_EQ(overlap_area, 0);
148 }
149
TEST(TPLModelTest,EstimateFrameRateTest)150 TEST(TPLModelTest, EstimateFrameRateTest) {
151 /*
152 * Transform size: 16x16
153 * Frame count: 16
154 * Transform block count: 20
155 */
156 const int txfm_size = 256; // 16x16
157 const int frame_count = 16;
158 int q_index_list[16];
159 int valid_list[16];
160 TplTxfmStats stats_list[16];
161
162 for (int i = 0; i < frame_count; i++) {
163 q_index_list[i] = 1;
164 valid_list[i] = 1;
165 stats_list[i].txfm_block_count = 8;
166
167 for (int j = 0; j < txfm_size; j++) {
168 stats_list[i].abs_coeff_sum[j] = 0;
169 }
170 }
171
172 double result = av1_estimate_gop_bitrate(q_index_list, frame_count,
173 stats_list, valid_list, NULL);
174 EXPECT_NEAR(result, 0, 0.1);
175 }
176
TEST(TPLModelTest,TxfmStatsInitTest)177 TEST(TPLModelTest, TxfmStatsInitTest) {
178 TplTxfmStats tpl_txfm_stats;
179 av1_init_tpl_txfm_stats(&tpl_txfm_stats);
180 EXPECT_EQ(tpl_txfm_stats.coeff_num, 256);
181 EXPECT_EQ(tpl_txfm_stats.txfm_block_count, 0);
182 for (int i = 0; i < tpl_txfm_stats.coeff_num; ++i) {
183 EXPECT_DOUBLE_EQ(tpl_txfm_stats.abs_coeff_sum[i], 0);
184 }
185 }
186
TEST(TPLModelTest,TxfmStatsAccumulateTest)187 TEST(TPLModelTest, TxfmStatsAccumulateTest) {
188 TplTxfmStats sub_stats;
189 av1_init_tpl_txfm_stats(&sub_stats);
190 sub_stats.txfm_block_count = 17;
191 for (int i = 0; i < sub_stats.coeff_num; ++i) {
192 sub_stats.abs_coeff_sum[i] = i;
193 }
194
195 TplTxfmStats accumulated_stats;
196 av1_init_tpl_txfm_stats(&accumulated_stats);
197 accumulated_stats.txfm_block_count = 13;
198 for (int i = 0; i < accumulated_stats.coeff_num; ++i) {
199 accumulated_stats.abs_coeff_sum[i] = 5 * i;
200 }
201
202 av1_accumulate_tpl_txfm_stats(&sub_stats, &accumulated_stats);
203 EXPECT_DOUBLE_EQ(accumulated_stats.txfm_block_count, 30);
204 for (int i = 0; i < accumulated_stats.coeff_num; ++i) {
205 EXPECT_DOUBLE_EQ(accumulated_stats.abs_coeff_sum[i], 6 * i);
206 }
207 }
208
TEST(TPLModelTest,TxfmStatsRecordTest)209 TEST(TPLModelTest, TxfmStatsRecordTest) {
210 TplTxfmStats stats1;
211 TplTxfmStats stats2;
212 av1_init_tpl_txfm_stats(&stats1);
213 av1_init_tpl_txfm_stats(&stats2);
214
215 tran_low_t coeff[256];
216 for (int i = 0; i < 256; ++i) {
217 coeff[i] = i;
218 }
219 av1_record_tpl_txfm_block(&stats1, coeff);
220 EXPECT_EQ(stats1.txfm_block_count, 1);
221
222 // we record the same transform block twice for testing purpose
223 av1_record_tpl_txfm_block(&stats2, coeff);
224 av1_record_tpl_txfm_block(&stats2, coeff);
225 EXPECT_EQ(stats2.txfm_block_count, 2);
226
227 EXPECT_EQ(stats1.coeff_num, 256);
228 EXPECT_EQ(stats2.coeff_num, 256);
229 for (int i = 0; i < 256; ++i) {
230 EXPECT_DOUBLE_EQ(stats2.abs_coeff_sum[i], 2 * stats1.abs_coeff_sum[i]);
231 }
232 }
233
234 /*
235 * Helper method to brute-force search for the closest q_index
236 * that achieves the specified bit budget.
237 */
find_gop_q_iterative(double bit_budget,double arf_qstep_ratio,GF_GROUP gf_group,const int * stats_valid_list,TplTxfmStats * stats_list,int gf_frame_index,aom_bit_depth_t bit_depth)238 int find_gop_q_iterative(double bit_budget, double arf_qstep_ratio,
239 GF_GROUP gf_group, const int *stats_valid_list,
240 TplTxfmStats *stats_list, int gf_frame_index,
241 aom_bit_depth_t bit_depth) {
242 // Brute force iterative method to find the optimal q.
243 // Use the result to test against the binary search result.
244
245 // Initial estimate when q = 255
246 av1_q_mode_compute_gop_q_indices(gf_frame_index, 255, arf_qstep_ratio,
247 bit_depth, &gf_group, gf_group.q_val);
248 double curr_estimate = av1_estimate_gop_bitrate(
249 gf_group.q_val, gf_group.size, stats_list, stats_valid_list, NULL);
250 double best_estimate_budget_distance = fabs(curr_estimate - bit_budget);
251 int best_q = 255;
252
253 // Start at q = 254 because we already have an estimate for q = 255.
254 for (int q = 254; q >= 0; q--) {
255 av1_q_mode_compute_gop_q_indices(gf_frame_index, q, arf_qstep_ratio,
256 bit_depth, &gf_group, gf_group.q_val);
257 curr_estimate = av1_estimate_gop_bitrate(
258 gf_group.q_val, gf_group.size, stats_list, stats_valid_list, NULL);
259 double curr_estimate_budget_distance = fabs(curr_estimate - bit_budget);
260 if (curr_estimate_budget_distance <= best_estimate_budget_distance) {
261 best_estimate_budget_distance = curr_estimate_budget_distance;
262 best_q = q;
263 }
264 }
265 return best_q;
266 }
267
TEST(TplModelTest,QModeEstimateBaseQTest)268 TEST(TplModelTest, QModeEstimateBaseQTest) {
269 GF_GROUP gf_group = {};
270 gf_group.size = 25;
271 TplTxfmStats stats_list[25];
272 int q_index_list[25];
273 const int gf_group_update_types[25] = { 0, 3, 6, 6, 6, 1, 5, 1, 5, 6, 1, 5, 1,
274 5, 6, 6, 1, 5, 1, 5, 6, 1, 5, 1, 4 };
275 int stats_valid_list[25] = { 0 };
276 const int gf_frame_index = 0;
277 const double arf_qstep_ratio = 2;
278 const aom_bit_depth_t bit_depth = AOM_BITS_8;
279 const double scale_factor = 1.0;
280
281 for (int i = 0; i < gf_group.size; i++) {
282 stats_valid_list[i] = 1;
283 gf_group.update_type[i] = gf_group_update_types[i];
284 stats_list[i].txfm_block_count = 8;
285
286 for (int j = 0; j < 256; j++) {
287 stats_list[i].abs_coeff_sum[j] = 1000 + j;
288 }
289 }
290
291 // Test multiple bit budgets.
292 const std::vector<double> bit_budgets = { 0, 100, 1000, 10000,
293 100000, 300000, 500000, 750000,
294 800000, DBL_MAX };
295
296 for (double bit_budget : bit_budgets) {
297 // Binary search method to find the optimal q.
298 const int result = av1_q_mode_estimate_base_q(
299 &gf_group, stats_list, stats_valid_list, bit_budget, gf_frame_index,
300 arf_qstep_ratio, bit_depth, scale_factor, q_index_list, NULL);
301 const int test_result = find_gop_q_iterative(
302 bit_budget, arf_qstep_ratio, gf_group, stats_valid_list, stats_list,
303 gf_frame_index, bit_depth);
304
305 if (bit_budget == 0) {
306 EXPECT_EQ(result, 255);
307 } else if (bit_budget == DBL_MAX) {
308 EXPECT_EQ(result, 0);
309 }
310
311 EXPECT_EQ(result, test_result);
312 }
313 }
314
TEST(TplModelTest,ComputeMVDifferenceTest)315 TEST(TplModelTest, ComputeMVDifferenceTest) {
316 TplDepFrame tpl_frame_small;
317 tpl_frame_small.is_valid = true;
318 tpl_frame_small.mi_rows = 4;
319 tpl_frame_small.mi_cols = 4;
320 tpl_frame_small.stride = 1;
321 uint8_t right_shift_small = 1;
322 int step_small = 1 << right_shift_small;
323
324 // Test values for motion vectors.
325 int mv_vals_small[4] = { 1, 2, 3, 4 };
326 int index = 0;
327
328 // 4x4 blocks means we need to allocate a 4 size array.
329 // According to av1_tpl_ptr_pos:
330 // (row >> right_shift) * stride + (col >> right_shift)
331 // (4 >> 1) * 1 + (4 >> 1) = 4
332 TplDepStats stats_buf_small[4];
333 tpl_frame_small.tpl_stats_ptr = stats_buf_small;
334
335 for (int row = 0; row < tpl_frame_small.mi_rows; row += step_small) {
336 for (int col = 0; col < tpl_frame_small.mi_cols; col += step_small) {
337 TplDepStats tpl_stats;
338 tpl_stats.ref_frame_index[0] = 0;
339 int_mv mv;
340 mv.as_mv.row = mv_vals_small[index];
341 mv.as_mv.col = mv_vals_small[index];
342 index++;
343 tpl_stats.mv[0] = mv;
344 tpl_frame_small.tpl_stats_ptr[av1_tpl_ptr_pos(
345 row, col, tpl_frame_small.stride, right_shift_small)] = tpl_stats;
346 }
347 }
348
349 int_mv result_mv =
350 av1_compute_mv_difference(&tpl_frame_small, 1, 1, step_small,
351 tpl_frame_small.stride, right_shift_small);
352
353 // Expect the result to be exactly equal to 1 because this is the difference
354 // between neighboring motion vectors in this instance.
355 EXPECT_EQ(result_mv.as_mv.row, 1);
356 EXPECT_EQ(result_mv.as_mv.col, 1);
357 }
358
TEST(TplModelTest,ComputeMVBitsTest)359 TEST(TplModelTest, ComputeMVBitsTest) {
360 TplDepFrame tpl_frame;
361 tpl_frame.is_valid = true;
362 tpl_frame.mi_rows = 16;
363 tpl_frame.mi_cols = 16;
364 tpl_frame.stride = 24;
365 uint8_t right_shift = 2;
366 int step = 1 << right_shift;
367 // Test values for motion vectors.
368 int mv_vals_ordered[16] = { 1, 2, 3, 4, 5, 6, 7, 8,
369 9, 10, 11, 12, 13, 14, 15, 16 };
370 int mv_vals[16] = { 1, 16, 2, 15, 3, 14, 4, 13, 5, 12, 6, 11, 7, 10, 8, 9 };
371 int index = 0;
372
373 // 16x16 blocks means we need to allocate a 100 size array.
374 // According to av1_tpl_ptr_pos:
375 // (row >> right_shift) * stride + (col >> right_shift)
376 // (16 >> 2) * 24 + (16 >> 2) = 100
377 TplDepStats stats_buf[100];
378 tpl_frame.tpl_stats_ptr = stats_buf;
379
380 for (int row = 0; row < tpl_frame.mi_rows; row += step) {
381 for (int col = 0; col < tpl_frame.mi_cols; col += step) {
382 TplDepStats tpl_stats;
383 tpl_stats.ref_frame_index[0] = 0;
384 int_mv mv;
385 mv.as_mv.row = mv_vals_ordered[index];
386 mv.as_mv.col = mv_vals_ordered[index];
387 index++;
388 tpl_stats.mv[0] = mv;
389 tpl_frame.tpl_stats_ptr[av1_tpl_ptr_pos(row, col, tpl_frame.stride,
390 right_shift)] = tpl_stats;
391 }
392 }
393
394 double result = av1_tpl_compute_frame_mv_entropy(&tpl_frame, right_shift);
395
396 // Expect the result to be low because the motion vectors are ordered.
397 // The estimation algorithm takes this into account and reduces the cost.
398 EXPECT_NEAR(result, 20, 5);
399
400 index = 0;
401 for (int row = 0; row < tpl_frame.mi_rows; row += step) {
402 for (int col = 0; col < tpl_frame.mi_cols; col += step) {
403 TplDepStats tpl_stats;
404 tpl_stats.ref_frame_index[0] = 0;
405 int_mv mv;
406 mv.as_mv.row = mv_vals[index];
407 mv.as_mv.col = mv_vals[index];
408 index++;
409 tpl_stats.mv[0] = mv;
410 tpl_frame.tpl_stats_ptr[av1_tpl_ptr_pos(row, col, tpl_frame.stride,
411 right_shift)] = tpl_stats;
412 }
413 }
414
415 result = av1_tpl_compute_frame_mv_entropy(&tpl_frame, right_shift);
416
417 // Expect the result to be higher because the vectors are not ordered.
418 // Neighboring vectors will have different values, increasing the cost.
419 EXPECT_NEAR(result, 70, 5);
420 }
421
422 } // namespace
423