/work/svt-av1/Source/Lib/Codec/ac_bias.c
Line | Count | Source |
1 | | /* |
2 | | * Copyright(c) 2024-2025 Psychovisual Experts Group |
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 https://www.aomedia.org/license/software-license. 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 https://www.aomedia.org/license/patent-license. |
10 | | */ |
11 | | |
12 | | #include <math.h> |
13 | | #include <stdbool.h> |
14 | | #include "ac_bias.h" |
15 | | #include "aom_dsp_rtcd.h" |
16 | | #include "common_dsp_rtcd.h" |
17 | | |
18 | 0 | static int psy_energy_4x4_c(const uint8_t* src, ptrdiff_t src_stride) { |
19 | 0 | int16_t src16[16]; |
20 | 0 | int32_t coeff[16]; |
21 | |
|
22 | 0 | for (int row = 0; row < 4; ++row) { |
23 | 0 | for (int col = 0; col < 4; ++col) { |
24 | 0 | src16[row * 4 + col] = src[row * src_stride + col]; |
25 | 0 | } |
26 | 0 | } |
27 | |
|
28 | 0 | svt_aom_hadamard_4x4(src16, 4, coeff); |
29 | |
|
30 | 0 | return (svt_aom_satd(coeff, 16) << 1) - coeff[0]; |
31 | 0 | } |
32 | | |
33 | 0 | static int psy_energy_8x8_c(const uint8_t* src, ptrdiff_t src_stride) { |
34 | 0 | int16_t src16[64]; |
35 | 0 | int32_t coeff[64]; |
36 | |
|
37 | 0 | for (int row = 0; row < 8; ++row) { |
38 | 0 | for (int col = 0; col < 8; ++col) { |
39 | 0 | src16[row * 8 + col] = src[row * src_stride + col]; |
40 | 0 | } |
41 | 0 | } |
42 | |
|
43 | 0 | svt_aom_hadamard_8x8(src16, 8, coeff); |
44 | |
|
45 | 0 | return ((svt_aom_satd(coeff, 64) + 2) >> 2) - ((coeff[0] + 2) >> 2); |
46 | 0 | } |
47 | | |
48 | | /* Regular version of "AC Bias" |
49 | | * |
50 | | * Based on adding an "energy gap" term to each candidate block's distortion, which is the difference |
51 | | * of the "energy" (SATD - SAD) of the source and recon blocks |
52 | | */ |
53 | | uint64_t svt_psy_distortion_c(const uint8_t* input, const uint32_t input_stride, const uint8_t* recon, |
54 | 0 | const uint32_t recon_stride, const uint32_t width, const uint32_t height) { |
55 | 0 | uint64_t energy_gap = 0; |
56 | |
|
57 | 0 | if (width >= 8 && height >= 8) { /* >8x8 */ |
58 | 0 | for (uint32_t j = 0; j < height; j += 8) { |
59 | 0 | for (uint32_t i = 0; i < width; i += 8) { |
60 | 0 | const int input_energy = psy_energy_8x8_c(input + j * input_stride + i, input_stride); |
61 | 0 | const int recon_energy = psy_energy_8x8_c(recon + j * recon_stride + i, recon_stride); |
62 | |
|
63 | 0 | energy_gap += abs(input_energy - recon_energy); |
64 | 0 | } |
65 | 0 | } |
66 | 0 | } else { |
67 | 0 | for (uint32_t j = 0; j < height; j += 4) { /* 4x4, 4x8, 4x16, 8x4, and 16x4 */ |
68 | 0 | for (uint32_t i = 0; i < width; i += 4) { |
69 | 0 | const int input_energy = psy_energy_4x4_c(input + j * input_stride + i, input_stride); |
70 | 0 | const int recon_energy = psy_energy_4x4_c(recon + j * recon_stride + i, recon_stride); |
71 | |
|
72 | 0 | energy_gap += abs(input_energy - recon_energy); |
73 | 0 | } |
74 | 0 | } |
75 | 0 | } |
76 | |
|
77 | 0 | return energy_gap; |
78 | 0 | } |
79 | | |
80 | | #if CONFIG_ENABLE_HIGH_BIT_DEPTH |
81 | 0 | static int highbd_psy_energy_4x4_c(const uint16_t* src, ptrdiff_t src_stride) { |
82 | 0 | int32_t coeff[16]; |
83 | |
|
84 | 0 | svt_aom_hadamard_4x4((const int16_t*)src, src_stride, coeff); |
85 | |
|
86 | 0 | return (svt_aom_satd(coeff, 16) << 1) - coeff[0]; |
87 | 0 | } |
88 | | |
89 | 0 | static int highbd_psy_energy_8x8_c(const uint16_t* src, ptrdiff_t src_stride) { |
90 | 0 | int32_t coeff[64]; |
91 | |
|
92 | 0 | svt_aom_highbd_hadamard_8x8((const int16_t*)src, src_stride, coeff); |
93 | |
|
94 | 0 | return ((svt_aom_satd(coeff, 64) + 2) >> 2) - ((coeff[0] + 2) >> 2); |
95 | 0 | } |
96 | | |
97 | | /* High bit-depth version of "AC Bias" */ |
98 | | uint64_t svt_psy_distortion_hbd_c(const uint16_t* input, const uint32_t input_stride, const uint16_t* recon, |
99 | 0 | const uint32_t recon_stride, const uint32_t width, const uint32_t height) { |
100 | 0 | uint64_t energy_gap = 0; |
101 | |
|
102 | 0 | if (width >= 8 && height >= 8) { /* >8x8 */ |
103 | 0 | for (uint32_t j = 0; j < height; j += 8) { |
104 | 0 | for (uint32_t i = 0; i < width; i += 8) { |
105 | 0 | const int input_energy = highbd_psy_energy_8x8_c(input + j * input_stride + i, input_stride); |
106 | 0 | const int recon_energy = highbd_psy_energy_8x8_c(recon + j * recon_stride + i, recon_stride); |
107 | |
|
108 | 0 | energy_gap += abs(input_energy - recon_energy); |
109 | 0 | } |
110 | 0 | } |
111 | 0 | } else { |
112 | 0 | for (uint64_t j = 0; j < height; j += 4) { /* 4x4, 4x8, 4x16, 8x4, and 16x4 */ |
113 | 0 | for (uint64_t i = 0; i < width; i += 4) { |
114 | 0 | const int input_energy = highbd_psy_energy_4x4_c(input + j * input_stride + i, input_stride); |
115 | 0 | const int recon_energy = highbd_psy_energy_4x4_c(recon + j * recon_stride + i, recon_stride); |
116 | |
|
117 | 0 | energy_gap += abs(input_energy - recon_energy); |
118 | 0 | } |
119 | 0 | } |
120 | 0 | } |
121 | | |
122 | | // Energy is scaled to approximately match equivalent 8-bit strengths |
123 | 0 | return energy_gap << 2; |
124 | 0 | } |
125 | | #endif |
126 | | |
127 | | /* |
128 | | * Public function that mirrors the arguments of `spatial_full_dist_type_fun()` |
129 | | */ |
130 | | uint64_t get_svt_psy_full_dist(const void* s, const uint32_t so, const uint32_t sp, const void* r, const uint32_t ro, |
131 | | const uint32_t rp, const uint32_t w, const uint32_t h, const uint8_t is_hbd, |
132 | 0 | const double ac_bias) { |
133 | 0 | if (is_hbd) |
134 | 0 | #if CONFIG_ENABLE_HIGH_BIT_DEPTH |
135 | 0 | return llrint(svt_psy_distortion_hbd((const uint16_t*)s + so, sp, (uint16_t*)r + ro, rp, w, h) * ac_bias); |
136 | | #else |
137 | | return 0; |
138 | | #endif |
139 | 0 | else { |
140 | 0 | return llrint(svt_psy_distortion((const uint8_t*)s + so, sp, (const uint8_t*)r + ro, rp, w, h) * ac_bias); |
141 | 0 | } |
142 | 0 | } |
143 | | |
144 | | /* |
145 | | * Light version of "AC Bias", called by the Light-PD code paths |
146 | | * |
147 | | * Based on adjusting each block's rate so blocks with more energy (sum of AC coeffs) appear "cheaper" to the encoder, |
148 | | * thus making them more favorable to be picked by the RDO process. This tends to increase the image's total "energy" |
149 | | * (in contrast to `get_svt_psy_full_dist()` which tries to reduce the "energy gap" between source and recon) |
150 | | * |
151 | | * Much faster than `get_svt_psy_full_dist()` as it can re-use existing block coefficients instead of computing new |
152 | | * ones, but subjective visual quality benefits are significantly more modest |
153 | | */ |
154 | | uint64_t svt_psy_adjust_rate_light(const int32_t* coeff, uint64_t coeff_bits, const uint32_t width, |
155 | 0 | const uint32_t height, const double ac_bias) { |
156 | 0 | uint64_t energy = 0; |
157 | 0 | const int32_t* buf = coeff; |
158 | |
|
159 | 0 | for (uint32_t j = 0; j < height; j++) { |
160 | | // Skip the DC coefficient from the calculation |
161 | 0 | for (uint32_t i = j ? 0 : 1; i < width; i++) { |
162 | 0 | energy += (uint64_t)llabs((int64_t)buf[i]); |
163 | 0 | } |
164 | 0 | buf += width; |
165 | 0 | } |
166 | |
|
167 | 0 | if (energy > 0) { |
168 | 0 | uint64_t coeff_bits_adj = (int)(energy * ac_bias * 100); |
169 | | |
170 | | // When the adjustment rate is greater than the rate, keep rate (coeff_bits) positive |
171 | 0 | coeff_bits = (coeff_bits > coeff_bits_adj) ? (coeff_bits - coeff_bits_adj) : 1; |
172 | 0 | } |
173 | |
|
174 | 0 | return coeff_bits; |
175 | 0 | } |
176 | | |
177 | 901k | double get_effective_ac_bias(const double ac_bias, const bool is_islice, const uint8_t temporal_layer_index) { |
178 | 901k | if (is_islice) { |
179 | 901k | return ac_bias * 0.3; |
180 | 901k | } |
181 | 18.4E | switch (temporal_layer_index) { |
182 | 0 | case 0: |
183 | 0 | return ac_bias * 0.6; |
184 | 0 | case 1: |
185 | 0 | return ac_bias * 0.8; |
186 | 0 | case 2: |
187 | 0 | return ac_bias * 0.9; |
188 | 0 | default: |
189 | 0 | return ac_bias; |
190 | 18.4E | } |
191 | 18.4E | } |