/src/libavif/ext/aom/av1/encoder/picklpf.c
Line | Count | Source |
1 | | /* |
2 | | * Copyright (c) 2016, 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 <assert.h> |
13 | | #include <limits.h> |
14 | | |
15 | | #include "config/aom_scale_rtcd.h" |
16 | | |
17 | | #include "aom_dsp/aom_dsp_common.h" |
18 | | #include "aom_dsp/psnr.h" |
19 | | #include "aom_mem/aom_mem.h" |
20 | | #include "aom_ports/mem.h" |
21 | | |
22 | | #include "av1/common/av1_common_int.h" |
23 | | #include "av1/common/av1_loopfilter.h" |
24 | | #include "av1/common/quant_common.h" |
25 | | |
26 | | #include "av1/encoder/av1_quantize.h" |
27 | | #include "av1/encoder/encoder.h" |
28 | | #include "av1/encoder/picklpf.h" |
29 | | |
30 | | // AV1 loop filter applies to the whole frame according to mi_rows and mi_cols, |
31 | | // which are calculated based on aligned width and aligned height, |
32 | | // In addition, if super res is enabled, it copies the whole frame |
33 | | // according to the aligned width and height (av1_superres_upscale()). |
34 | | // So we need to copy the whole filtered region, instead of the cropped region. |
35 | | // For example, input image size is: 160x90. |
36 | | // Then src->y_crop_width = 160, src->y_crop_height = 90. |
37 | | // The aligned frame size is: src->y_width = 160, src->y_height = 96. |
38 | | // AV1 aligns frame size to a multiple of 8, if there is |
39 | | // chroma subsampling, it is able to ensure the chroma is also |
40 | | // an integer number of mi units. mi unit is 4x4, 8 = 4 * 2, and 2 luma mi |
41 | | // units correspond to 1 chroma mi unit if there is subsampling. |
42 | | // See: aom_realloc_frame_buffer() in yv12config.c. |
43 | | static void yv12_copy_plane(const YV12_BUFFER_CONFIG *src_bc, |
44 | 257k | YV12_BUFFER_CONFIG *dst_bc, int plane) { |
45 | 257k | switch (plane) { |
46 | 145k | case 0: aom_yv12_copy_y(src_bc, dst_bc, 0); break; |
47 | 56.0k | case 1: aom_yv12_copy_u(src_bc, dst_bc, 0); break; |
48 | 56.2k | case 2: aom_yv12_copy_v(src_bc, dst_bc, 0); break; |
49 | 0 | default: assert(plane >= 0 && plane <= 2); break; |
50 | 257k | } |
51 | 257k | } |
52 | | |
53 | 147k | static int get_max_filter_level(const AV1_COMP *cpi) { |
54 | 147k | if (is_stat_consumption_stage_twopass(cpi)) { |
55 | 0 | return cpi->ppi->twopass.section_intra_rating > 8 ? MAX_LOOP_FILTER * 3 / 4 |
56 | 0 | : MAX_LOOP_FILTER; |
57 | 147k | } else { |
58 | 147k | return MAX_LOOP_FILTER; |
59 | 147k | } |
60 | 147k | } |
61 | | |
62 | | static int64_t try_filter_frame(const YV12_BUFFER_CONFIG *sd, |
63 | | AV1_COMP *const cpi, int filt_level, |
64 | 196k | int partial_frame, int plane, int dir) { |
65 | 196k | MultiThreadInfo *const mt_info = &cpi->mt_info; |
66 | 196k | int num_workers = mt_info->num_mod_workers[MOD_LPF]; |
67 | 196k | AV1_COMMON *const cm = &cpi->common; |
68 | 196k | int64_t filt_err; |
69 | | |
70 | 196k | assert(plane >= 0 && plane <= 2); |
71 | 196k | int filter_level[2] = { filt_level, filt_level }; |
72 | 196k | if (plane == 0 && dir == 0) filter_level[1] = cm->lf.filter_level[1]; |
73 | 196k | if (plane == 0 && dir == 1) filter_level[0] = cm->lf.filter_level[0]; |
74 | | |
75 | | // set base filters for use of get_filter_level (av1_loopfilter.c) when in |
76 | | // DELTA_LF mode |
77 | 196k | switch (plane) { |
78 | 110k | case 0: |
79 | 110k | cm->lf.filter_level[0] = filter_level[0]; |
80 | 110k | cm->lf.filter_level[1] = filter_level[1]; |
81 | 110k | break; |
82 | 42.9k | case 1: cm->lf.filter_level_u = filter_level[0]; break; |
83 | 43.0k | case 2: cm->lf.filter_level_v = filter_level[0]; break; |
84 | 196k | } |
85 | | |
86 | | // lpf_opt_level = 1 : Enables dual/quad loop-filtering. |
87 | 196k | int lpf_opt_level = is_inter_tx_size_search_level_one(&cpi->sf.tx_sf); |
88 | | |
89 | 196k | av1_loop_filter_frame_mt(&cm->cur_frame->buf, cm, &cpi->td.mb.e_mbd, plane, |
90 | 196k | plane + 1, partial_frame, mt_info->workers, |
91 | 196k | num_workers, &mt_info->lf_row_sync, lpf_opt_level); |
92 | | |
93 | 196k | filt_err = aom_get_sse_plane(sd, &cm->cur_frame->buf, plane, |
94 | 196k | cm->seq_params->use_highbitdepth); |
95 | | |
96 | | // Re-instate the unfiltered frame |
97 | 196k | yv12_copy_plane(&cpi->last_frame_uf, &cm->cur_frame->buf, plane); |
98 | | |
99 | 196k | return filt_err; |
100 | 196k | } |
101 | | |
102 | | static int search_filter_level(const YV12_BUFFER_CONFIG *sd, AV1_COMP *cpi, |
103 | | int partial_frame, |
104 | | const int *last_frame_filter_level, int plane, |
105 | 60.8k | int dir, int64_t *best_filter_sse) { |
106 | 60.8k | const AV1_COMMON *const cm = &cpi->common; |
107 | 60.8k | const int min_filter_level = 0; |
108 | 60.8k | const int max_filter_level = get_max_filter_level(cpi); |
109 | 60.8k | int filt_direction = 0; |
110 | 60.8k | int64_t best_err; |
111 | 60.8k | int filt_best; |
112 | | |
113 | | // Start the search at the previous frame filter level unless it is now out of |
114 | | // range. |
115 | 60.8k | int lvl; |
116 | 60.8k | switch (plane) { |
117 | 34.5k | case 0: |
118 | 34.5k | switch (dir) { |
119 | 34.5k | case 2: |
120 | 34.5k | lvl = (last_frame_filter_level[0] + last_frame_filter_level[1] + 1) >> |
121 | 34.5k | 1; |
122 | 34.5k | break; |
123 | 0 | case 0: |
124 | 0 | case 1: lvl = last_frame_filter_level[dir]; break; |
125 | 0 | default: assert(dir >= 0 && dir <= 2); return 0; |
126 | 34.5k | } |
127 | 34.5k | break; |
128 | 34.5k | case 1: lvl = last_frame_filter_level[2]; break; |
129 | 13.1k | case 2: lvl = last_frame_filter_level[3]; break; |
130 | 0 | default: assert(plane >= 0 && plane <= 2); return 0; |
131 | 60.8k | } |
132 | 60.8k | int filt_mid = clamp(lvl, min_filter_level, max_filter_level); |
133 | 60.8k | int filter_step = filt_mid < 16 ? 4 : filt_mid / 4; |
134 | | // Sum squared error at each filter level |
135 | 60.8k | int64_t ss_err[MAX_LOOP_FILTER + 1]; |
136 | | |
137 | 60.8k | const int use_coarse_search = cpi->sf.lpf_sf.use_coarse_filter_level_search; |
138 | 60.8k | assert(use_coarse_search <= 1); |
139 | 60.8k | static const int min_filter_step_lookup[2] = { 0, 2 }; |
140 | | // min_filter_step_thesh determines the stopping criteria for the search. |
141 | | // The search is terminated when filter_step equals min_filter_step_thesh. |
142 | 60.8k | const int min_filter_step_thesh = min_filter_step_lookup[use_coarse_search]; |
143 | | |
144 | | // Set each entry to -1 |
145 | 60.8k | memset(ss_err, 0xFF, sizeof(ss_err)); |
146 | 60.8k | yv12_copy_plane(&cm->cur_frame->buf, &cpi->last_frame_uf, plane); |
147 | 60.8k | best_err = try_filter_frame(sd, cpi, filt_mid, partial_frame, plane, dir); |
148 | 60.8k | filt_best = filt_mid; |
149 | 60.8k | ss_err[filt_mid] = best_err; |
150 | | |
151 | 193k | while (filter_step > min_filter_step_thesh) { |
152 | 133k | const int filt_high = AOMMIN(filt_mid + filter_step, max_filter_level); |
153 | 133k | const int filt_low = AOMMAX(filt_mid - filter_step, min_filter_level); |
154 | | |
155 | | // Bias against raising loop filter in favor of lowering it. |
156 | 133k | int64_t bias = (best_err >> (15 - (filt_mid / 8))) * filter_step; |
157 | | |
158 | 133k | if ((is_stat_consumption_stage_twopass(cpi)) && |
159 | 0 | (cpi->ppi->twopass.section_intra_rating < 20)) |
160 | 0 | bias = (bias * cpi->ppi->twopass.section_intra_rating) / 20; |
161 | | |
162 | | // yx, bias less for large block size |
163 | 133k | if (cm->features.tx_mode != ONLY_4X4) bias >>= 1; |
164 | | |
165 | 133k | if (filt_direction <= 0 && filt_low != filt_mid) { |
166 | | // Get Low filter error score |
167 | 7.22k | if (ss_err[filt_low] < 0) { |
168 | 5.65k | ss_err[filt_low] = |
169 | 5.65k | try_filter_frame(sd, cpi, filt_low, partial_frame, plane, dir); |
170 | 5.65k | } |
171 | | // If value is close to the best so far then bias towards a lower loop |
172 | | // filter value. |
173 | 7.22k | if (ss_err[filt_low] < (best_err + bias)) { |
174 | | // Was it actually better than the previous best? |
175 | 2.77k | if (ss_err[filt_low] < best_err) { |
176 | 552 | best_err = ss_err[filt_low]; |
177 | 552 | } |
178 | 2.77k | filt_best = filt_low; |
179 | 2.77k | } |
180 | 7.22k | } |
181 | | |
182 | | // Now look at filt_high |
183 | 133k | if (filt_direction >= 0 && filt_high != filt_mid) { |
184 | 130k | if (ss_err[filt_high] < 0) { |
185 | 129k | ss_err[filt_high] = |
186 | 129k | try_filter_frame(sd, cpi, filt_high, partial_frame, plane, dir); |
187 | 129k | } |
188 | | // If value is significantly better than previous best, bias added against |
189 | | // raising filter value |
190 | 130k | if (ss_err[filt_high] < (best_err - bias)) { |
191 | 3.86k | best_err = ss_err[filt_high]; |
192 | 3.86k | filt_best = filt_high; |
193 | 3.86k | } |
194 | 130k | } |
195 | | |
196 | | // Half the step distance if the best filter value was the same as last time |
197 | 133k | if (filt_best == filt_mid) { |
198 | 126k | filter_step /= 2; |
199 | 126k | filt_direction = 0; |
200 | 126k | } else { |
201 | 6.44k | filt_direction = (filt_best < filt_mid) ? -1 : 1; |
202 | 6.44k | filt_mid = filt_best; |
203 | 6.44k | } |
204 | 133k | } |
205 | | |
206 | 60.8k | *best_filter_sse = ss_err[filt_best]; |
207 | | |
208 | 60.8k | return filt_best; |
209 | 60.8k | } |
210 | | |
211 | | void av1_pick_filter_level(const YV12_BUFFER_CONFIG *sd, AV1_COMP *cpi, |
212 | 121k | LPF_PICK_METHOD method) { |
213 | 121k | AV1_COMMON *const cm = &cpi->common; |
214 | 121k | const SequenceHeader *const seq_params = cm->seq_params; |
215 | 121k | const int num_planes = av1_num_planes(cm); |
216 | 121k | struct loopfilter *const lf = &cm->lf; |
217 | 121k | int disable_filter_rt_screen = 0; |
218 | 121k | (void)sd; |
219 | | |
220 | | // Enable loop filter sharpness only for all-intra encoding mode, |
221 | | // or tune IQ or SSIMULACRA2. This is because: |
222 | | // - All-intra: frames do not have to serve as references to others |
223 | | // - Tune IQ/SSIMULACRA2: enabling loop filter sharpness has been found to |
224 | | // be beneficial for sharpness perception |
225 | 121k | if (cpi->oxcf.mode == ALLINTRA || cpi->oxcf.tune_cfg.tuning == AOM_TUNE_IQ || |
226 | 67.7k | cpi->oxcf.tune_cfg.tuning == AOM_TUNE_SSIMULACRA2) { |
227 | 67.7k | lf->sharpness_level = cpi->oxcf.algo_cfg.sharpness; |
228 | 67.7k | } else { |
229 | 53.6k | lf->sharpness_level = 0; |
230 | 53.6k | } |
231 | | |
232 | 121k | if (cpi->oxcf.algo_cfg.enable_adaptive_sharpness) { |
233 | | // Loop filter sharpness levels are highly nonlinear. Visually, lf sharpness |
234 | | // 1 is closer to 7 than it is to 0, so in practice adaptive sharpness is |
235 | | // written to pick levels 0, 1 and 7 to keep it simple. |
236 | 51.8k | int max_lf_sharpness; |
237 | | |
238 | 51.8k | if (cm->quant_params.base_qindex <= 112) { |
239 | 16.4k | max_lf_sharpness = 7; |
240 | 35.4k | } else if (cm->quant_params.base_qindex <= 160) { |
241 | 10.6k | max_lf_sharpness = 1; |
242 | 24.7k | } else { |
243 | 24.7k | max_lf_sharpness = 0; |
244 | 24.7k | } |
245 | | |
246 | 51.8k | lf->sharpness_level = AOMMIN(lf->sharpness_level, max_lf_sharpness); |
247 | 51.8k | } |
248 | | |
249 | 121k | if (cpi->oxcf.tune_cfg.content == AOM_CONTENT_SCREEN && |
250 | 0 | cpi->oxcf.q_cfg.aq_mode == CYCLIC_REFRESH_AQ && |
251 | 0 | cpi->sf.rt_sf.skip_lf_screen) |
252 | 0 | disable_filter_rt_screen = av1_cyclic_refresh_disable_lf_cdef(cpi); |
253 | | |
254 | 121k | if (disable_filter_rt_screen || |
255 | 121k | cpi->oxcf.algo_cfg.loopfilter_control == LOOPFILTER_NONE || |
256 | 121k | (cpi->oxcf.algo_cfg.loopfilter_control == LOOPFILTER_REFERENCE && |
257 | 0 | cpi->ppi->rtc_ref.non_reference_frame)) { |
258 | 0 | lf->filter_level[0] = 0; |
259 | 0 | lf->filter_level[1] = 0; |
260 | 0 | return; |
261 | 0 | } |
262 | | |
263 | 121k | if (method == LPF_PICK_MINIMAL_LPF) { |
264 | 0 | lf->filter_level[0] = 0; |
265 | 0 | lf->filter_level[1] = 0; |
266 | 121k | } else if (method >= LPF_PICK_FROM_Q) { |
267 | 86.8k | const int min_filter_level = 0; |
268 | 86.8k | const int max_filter_level = get_max_filter_level(cpi); |
269 | 86.8k | const int q = av1_ac_quant_QTX(cm->quant_params.base_qindex, 0, |
270 | 86.8k | seq_params->bit_depth); |
271 | | // based on tests result for rtc test set |
272 | | // 0.04590 boosted or 0.02295 non-booseted in 18-bit fixed point |
273 | 86.8k | const int strength_boost_q_treshold = 0; |
274 | 86.8k | int inter_frame_multiplier = |
275 | 86.8k | (q > strength_boost_q_treshold || |
276 | 0 | (cpi->sf.rt_sf.use_nonrd_pick_mode && |
277 | 0 | cpi->common.width * cpi->common.height > 352 * 288)) |
278 | 86.8k | ? 12034 |
279 | 86.8k | : 6017; |
280 | | // Increase strength on base TL0 for temporal layers, for low-resoln, |
281 | | // based on frame source_sad. |
282 | 86.8k | if (cpi->svc.number_temporal_layers > 1 && |
283 | 0 | cpi->svc.temporal_layer_id == 0 && |
284 | 0 | cpi->common.width * cpi->common.height <= 352 * 288 && |
285 | 0 | cpi->sf.rt_sf.use_nonrd_pick_mode) { |
286 | 0 | if (cpi->rc.frame_source_sad > 100000) |
287 | 0 | inter_frame_multiplier = inter_frame_multiplier << 1; |
288 | 0 | else if (cpi->rc.frame_source_sad > 50000) |
289 | 0 | inter_frame_multiplier = 3 * (inter_frame_multiplier >> 1); |
290 | 86.8k | } else if (cpi->sf.rt_sf.use_fast_fixed_part) { |
291 | 0 | inter_frame_multiplier = inter_frame_multiplier << 1; |
292 | 0 | } |
293 | | // These values were determined by linear fitting the result of the |
294 | | // searched level for 8 bit depth: |
295 | | // Keyframes: filt_guess = q * 0.06699 - 1.60817 |
296 | | // Other frames: filt_guess = q * inter_frame_multiplier + 2.48225 |
297 | | // |
298 | | // 10 bit depth: |
299 | | // filt_guess = q * 0.01976 + 3.87252 |
300 | | // |
301 | | // 12 bit depth: |
302 | | // filt_guess = q * 0.00494 + 3.87252 |
303 | 86.8k | int filt_guess; |
304 | 86.8k | switch (seq_params->bit_depth) { |
305 | 61.7k | case AOM_BITS_8: |
306 | 61.7k | filt_guess = |
307 | 61.7k | (cm->current_frame.frame_type == KEY_FRAME) |
308 | 61.7k | ? ROUND_POWER_OF_TWO(q * 17563 - 421574, 18) |
309 | 61.7k | : ROUND_POWER_OF_TWO(q * inter_frame_multiplier + 650707, 18); |
310 | 61.7k | break; |
311 | 11.2k | case AOM_BITS_10: |
312 | 11.2k | filt_guess = ROUND_POWER_OF_TWO(q * 20723 + 4060632, 20); |
313 | 11.2k | break; |
314 | 13.9k | case AOM_BITS_12: |
315 | 13.9k | filt_guess = ROUND_POWER_OF_TWO(q * 20723 + 16242526, 22); |
316 | 13.9k | break; |
317 | 0 | default: |
318 | 0 | assert(0 && |
319 | 0 | "bit_depth should be AOM_BITS_8, AOM_BITS_10 " |
320 | 0 | "or AOM_BITS_12"); |
321 | 0 | return; |
322 | 86.8k | } |
323 | 86.8k | if (seq_params->bit_depth != AOM_BITS_8 && |
324 | 25.1k | cm->current_frame.frame_type == KEY_FRAME) |
325 | 23.1k | filt_guess -= 4; |
326 | | // TODO(chengchen): retrain the model for Y, U, V filter levels |
327 | 86.8k | lf->filter_level[0] = clamp(filt_guess, min_filter_level, max_filter_level); |
328 | 86.8k | lf->filter_level[1] = clamp(filt_guess, min_filter_level, max_filter_level); |
329 | 86.8k | lf->filter_level_u = clamp(filt_guess, min_filter_level, max_filter_level); |
330 | 86.8k | lf->filter_level_v = clamp(filt_guess, min_filter_level, max_filter_level); |
331 | 86.8k | if (cpi->oxcf.algo_cfg.loopfilter_control == LOOPFILTER_SELECTIVELY && |
332 | 0 | !frame_is_intra_only(cm) && !cpi->rc.high_source_sad) { |
333 | 0 | if (cpi->oxcf.tune_cfg.content == AOM_CONTENT_SCREEN) { |
334 | 0 | lf->filter_level[0] = 0; |
335 | 0 | lf->filter_level[1] = 0; |
336 | 0 | } else { |
337 | 0 | const int num4x4 = (cm->width >> 2) * (cm->height >> 2); |
338 | 0 | const int newmv_thresh = 7; |
339 | 0 | const int distance_since_key_thresh = 5; |
340 | 0 | if ((cpi->td.rd_counts.newmv_or_intra_blocks * 100 / num4x4) < |
341 | 0 | newmv_thresh && |
342 | 0 | cpi->rc.frames_since_key > distance_since_key_thresh) { |
343 | 0 | lf->filter_level[0] = 0; |
344 | 0 | lf->filter_level[1] = 0; |
345 | 0 | } |
346 | 0 | } |
347 | 0 | } |
348 | 86.8k | } else { |
349 | 34.5k | int last_frame_filter_level[4] = { 0 }; |
350 | 34.5k | if (!frame_is_intra_only(cm)) { |
351 | 16.1k | last_frame_filter_level[0] = cpi->ppi->filter_level[0]; |
352 | 16.1k | last_frame_filter_level[1] = cpi->ppi->filter_level[1]; |
353 | 16.1k | last_frame_filter_level[2] = cpi->ppi->filter_level_u; |
354 | 16.1k | last_frame_filter_level[3] = cpi->ppi->filter_level_v; |
355 | 16.1k | } |
356 | | // The frame buffer last_frame_uf is used to store the non-loop filtered |
357 | | // reconstructed frame in search_filter_level(). |
358 | 34.5k | if (aom_realloc_frame_buffer( |
359 | 34.5k | &cpi->last_frame_uf, cm->width, cm->height, |
360 | 34.5k | seq_params->subsampling_x, seq_params->subsampling_y, |
361 | 34.5k | seq_params->use_highbitdepth, cpi->oxcf.border_in_pixels, |
362 | 34.5k | cm->features.byte_alignment, NULL, NULL, NULL, false, 0)) |
363 | 0 | aom_internal_error(cm->error, AOM_CODEC_MEM_ERROR, |
364 | 0 | "Failed to allocate last frame buffer"); |
365 | | |
366 | 34.5k | int64_t zero_filter_sse[MAX_MB_PLANE] = { 0 }; |
367 | 34.5k | int64_t best_filter_sse[MAX_MB_PLANE] = { 0 }; |
368 | | |
369 | 34.5k | if (cpi->sf.lpf_sf.skip_loop_filter_using_filt_error >= 1) { |
370 | 0 | for (int plane = 0; plane < num_planes; plane++) { |
371 | 0 | zero_filter_sse[plane] = aom_get_sse_plane( |
372 | 0 | sd, &cm->cur_frame->buf, plane, cm->seq_params->use_highbitdepth); |
373 | 0 | } |
374 | 0 | } |
375 | | |
376 | 34.5k | lf->filter_level[0] = lf->filter_level[1] = |
377 | 34.5k | search_filter_level(sd, cpi, method == LPF_PICK_FROM_SUBIMAGE, |
378 | 34.5k | last_frame_filter_level, 0, 2, &best_filter_sse[0]); |
379 | 34.5k | if (method != LPF_PICK_FROM_FULL_IMAGE_NON_DUAL) { |
380 | 0 | lf->filter_level[0] = search_filter_level( |
381 | 0 | sd, cpi, method == LPF_PICK_FROM_SUBIMAGE, last_frame_filter_level, 0, |
382 | 0 | 0, &best_filter_sse[0]); |
383 | 0 | lf->filter_level[1] = search_filter_level( |
384 | 0 | sd, cpi, method == LPF_PICK_FROM_SUBIMAGE, last_frame_filter_level, 0, |
385 | 0 | 1, &best_filter_sse[0]); |
386 | 0 | } |
387 | | |
388 | 34.5k | if (num_planes > 1) { |
389 | 13.1k | lf->filter_level_u = search_filter_level( |
390 | 13.1k | sd, cpi, method == LPF_PICK_FROM_SUBIMAGE, last_frame_filter_level, 1, |
391 | 13.1k | 0, &best_filter_sse[1]); |
392 | 13.1k | lf->filter_level_v = search_filter_level( |
393 | 13.1k | sd, cpi, method == LPF_PICK_FROM_SUBIMAGE, last_frame_filter_level, 2, |
394 | 13.1k | 0, &best_filter_sse[2]); |
395 | 13.1k | } |
396 | | |
397 | 34.5k | lf->backup_filter_level[0] = lf->filter_level[0]; |
398 | 34.5k | lf->backup_filter_level[1] = lf->filter_level[1]; |
399 | 34.5k | lf->backup_filter_level_u = lf->filter_level_u; |
400 | 34.5k | lf->backup_filter_level_v = lf->filter_level_v; |
401 | | |
402 | 34.5k | if (cpi->sf.lpf_sf.adaptive_luma_loop_filter_skip >= 1) { |
403 | 0 | int32_t min_ref_filter_level[2] = { MAX_LOOP_FILTER, MAX_LOOP_FILTER }; |
404 | | // Find the minimum luma filter levels across all reference frames. |
405 | 0 | for (int ref = LAST_FRAME; ref <= ALTREF_FRAME; ++ref) { |
406 | 0 | const RefCntBuffer *const buf = get_ref_frame_buf(cm, ref); |
407 | 0 | if (buf == NULL) continue; |
408 | | |
409 | 0 | if (buf->filter_level[0] != -1) |
410 | 0 | min_ref_filter_level[0] = |
411 | 0 | AOMMIN(min_ref_filter_level[0], buf->filter_level[0]); |
412 | 0 | if (buf->filter_level[1] != -1) |
413 | 0 | min_ref_filter_level[1] = |
414 | 0 | AOMMIN(min_ref_filter_level[1], buf->filter_level[1]); |
415 | 0 | } |
416 | | |
417 | | // Reset luma filter levels to zero based on minimum filter levels of |
418 | | // reference frames and current frame's pyramid level. |
419 | 0 | unsigned int pyramid_level = cm->current_frame.pyramid_level; |
420 | 0 | if (pyramid_level > 1) { |
421 | 0 | int filter_threshold; |
422 | 0 | if (pyramid_level >= 4) |
423 | 0 | filter_threshold = 16; |
424 | 0 | else |
425 | 0 | filter_threshold = 8; |
426 | |
|
427 | 0 | const bool reset_filter_level_y = |
428 | 0 | lf->filter_level[0] < filter_threshold && |
429 | 0 | lf->filter_level[1] < filter_threshold && |
430 | 0 | lf->filter_level_u < filter_threshold && |
431 | 0 | lf->filter_level_v < filter_threshold && |
432 | 0 | min_ref_filter_level[0] == 0 && min_ref_filter_level[1] == 0; |
433 | 0 | if (reset_filter_level_y) { |
434 | 0 | lf->filter_level[0] = 0; |
435 | 0 | lf->filter_level[1] = 0; |
436 | 0 | } |
437 | 0 | } |
438 | 0 | } |
439 | | |
440 | 34.5k | if (lf->filter_level[0] != 0 && lf->filter_level[1] != 0 && |
441 | 1.90k | cpi->sf.lpf_sf.skip_loop_filter_using_filt_error >= 1) { |
442 | 0 | const double pct_improvement_thresh = 2.0; |
443 | 0 | bool reset_filter_level_y = true; |
444 | | |
445 | | // Calculate the percentage improvement in SSE for each plane. This |
446 | | // measures the relative reduction in error when applying the filter |
447 | | // compared to no filtering. |
448 | 0 | for (int plane = 0; plane < num_planes; plane++) { |
449 | 0 | const double pct_improvement_sse = |
450 | 0 | ((zero_filter_sse[plane] - best_filter_sse[plane]) * 100.0) / |
451 | 0 | zero_filter_sse[plane]; |
452 | 0 | reset_filter_level_y &= pct_improvement_sse < pct_improvement_thresh; |
453 | 0 | } |
454 | |
|
455 | 0 | if (reset_filter_level_y) { |
456 | 0 | lf->filter_level[0] = 0; |
457 | 0 | lf->filter_level[1] = 0; |
458 | 0 | } |
459 | 0 | } |
460 | | |
461 | | // Store the current frame's filter levels to be referenced |
462 | | // while determining the minimum filter level from reference frames. |
463 | 34.5k | cm->cur_frame->filter_level[0] = lf->filter_level[0]; |
464 | 34.5k | cm->cur_frame->filter_level[1] = lf->filter_level[1]; |
465 | 34.5k | } |
466 | 121k | } |