/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 | 329k | YV12_BUFFER_CONFIG *dst_bc, int plane) { |
45 | 329k | switch (plane) { |
46 | 183k | case 0: aom_yv12_copy_y(src_bc, dst_bc, 0); break; |
47 | 73.0k | case 1: aom_yv12_copy_u(src_bc, dst_bc, 0); break; |
48 | 73.0k | case 2: aom_yv12_copy_v(src_bc, dst_bc, 0); break; |
49 | 0 | default: assert(plane >= 0 && plane <= 2); break; |
50 | 329k | } |
51 | 329k | } |
52 | | |
53 | 138k | static int get_max_filter_level(const AV1_COMP *cpi) { |
54 | 138k | 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 | 138k | } else { |
58 | 138k | return MAX_LOOP_FILTER; |
59 | 138k | } |
60 | 138k | } |
61 | | |
62 | | static int64_t try_filter_frame(const YV12_BUFFER_CONFIG *sd, |
63 | | AV1_COMP *const cpi, int filt_level, |
64 | 254k | int partial_frame, int plane, int dir) { |
65 | 254k | MultiThreadInfo *const mt_info = &cpi->mt_info; |
66 | 254k | int num_workers = mt_info->num_mod_workers[MOD_LPF]; |
67 | 254k | AV1_COMMON *const cm = &cpi->common; |
68 | 254k | int64_t filt_err; |
69 | | |
70 | 254k | assert(plane >= 0 && plane <= 2); |
71 | 254k | int filter_level[2] = { filt_level, filt_level }; |
72 | 254k | if (plane == 0 && dir == 0) filter_level[1] = cm->lf.filter_level[1]; |
73 | 254k | 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 | 254k | switch (plane) { |
78 | 141k | case 0: |
79 | 141k | cm->lf.filter_level[0] = filter_level[0]; |
80 | 141k | cm->lf.filter_level[1] = filter_level[1]; |
81 | 141k | break; |
82 | 56.5k | case 1: cm->lf.filter_level_u = filter_level[0]; break; |
83 | 56.5k | case 2: cm->lf.filter_level_v = filter_level[0]; break; |
84 | 254k | } |
85 | | |
86 | | // lpf_opt_level = 1 : Enables dual/quad loop-filtering. |
87 | 254k | int lpf_opt_level = is_inter_tx_size_search_level_one(&cpi->sf.tx_sf); |
88 | | |
89 | 254k | av1_loop_filter_frame_mt(&cm->cur_frame->buf, cm, &cpi->td.mb.e_mbd, plane, |
90 | 254k | plane + 1, partial_frame, mt_info->workers, |
91 | 254k | num_workers, &mt_info->lf_row_sync, lpf_opt_level); |
92 | | |
93 | 254k | filt_err = aom_get_sse_plane(sd, &cm->cur_frame->buf, plane, |
94 | 254k | cm->seq_params->use_highbitdepth); |
95 | | |
96 | | // Re-instate the unfiltered frame |
97 | 254k | yv12_copy_plane(&cpi->last_frame_uf, &cm->cur_frame->buf, plane); |
98 | | |
99 | 254k | return filt_err; |
100 | 254k | } |
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 | 74.9k | int dir, int64_t *best_filter_sse) { |
106 | 74.9k | const AV1_COMMON *const cm = &cpi->common; |
107 | 74.9k | const int min_filter_level = 0; |
108 | 74.9k | const int max_filter_level = get_max_filter_level(cpi); |
109 | 74.9k | int filt_direction = 0; |
110 | 74.9k | int64_t best_err; |
111 | 74.9k | int filt_best; |
112 | | |
113 | | // Start the search at the previous frame filter level unless it is now out of |
114 | | // range. |
115 | 74.9k | int lvl; |
116 | 74.9k | switch (plane) { |
117 | 41.9k | case 0: |
118 | 41.9k | switch (dir) { |
119 | 41.9k | case 2: |
120 | 41.9k | lvl = (last_frame_filter_level[0] + last_frame_filter_level[1] + 1) >> |
121 | 41.9k | 1; |
122 | 41.9k | 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 | 41.9k | } |
127 | 41.9k | break; |
128 | 41.9k | case 1: lvl = last_frame_filter_level[2]; break; |
129 | 16.5k | case 2: lvl = last_frame_filter_level[3]; break; |
130 | 0 | default: assert(plane >= 0 && plane <= 2); return 0; |
131 | 74.9k | } |
132 | 74.9k | int filt_mid = clamp(lvl, min_filter_level, max_filter_level); |
133 | 74.9k | int filter_step = filt_mid < 16 ? 4 : filt_mid / 4; |
134 | | // Sum squared error at each filter level |
135 | 74.9k | int64_t ss_err[MAX_LOOP_FILTER + 1]; |
136 | | |
137 | 74.9k | const int use_coarse_search = cpi->sf.lpf_sf.use_coarse_filter_level_search; |
138 | 74.9k | assert(use_coarse_search <= 1); |
139 | 74.9k | 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 | 74.9k | const int min_filter_step_thesh = min_filter_step_lookup[use_coarse_search]; |
143 | | |
144 | | // Set each entry to -1 |
145 | 74.9k | memset(ss_err, 0xFF, sizeof(ss_err)); |
146 | 74.9k | yv12_copy_plane(&cm->cur_frame->buf, &cpi->last_frame_uf, plane); |
147 | 74.9k | best_err = try_filter_frame(sd, cpi, filt_mid, partial_frame, plane, dir); |
148 | 74.9k | filt_best = filt_mid; |
149 | 74.9k | ss_err[filt_mid] = best_err; |
150 | | |
151 | 250k | while (filter_step > min_filter_step_thesh) { |
152 | 175k | const int filt_high = AOMMIN(filt_mid + filter_step, max_filter_level); |
153 | 175k | 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 | 175k | int64_t bias = (best_err >> (15 - (filt_mid / 8))) * filter_step; |
157 | | |
158 | 175k | 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 | 175k | if (cm->features.tx_mode != ONLY_4X4) bias >>= 1; |
164 | | |
165 | 175k | if (filt_direction <= 0 && filt_low != filt_mid) { |
166 | | // Get Low filter error score |
167 | 10.1k | if (ss_err[filt_low] < 0) { |
168 | 8.03k | ss_err[filt_low] = |
169 | 8.03k | try_filter_frame(sd, cpi, filt_low, partial_frame, plane, dir); |
170 | 8.03k | } |
171 | | // If value is close to the best so far then bias towards a lower loop |
172 | | // filter value. |
173 | 10.1k | if (ss_err[filt_low] < (best_err + bias)) { |
174 | | // Was it actually better than the previous best? |
175 | 2.87k | if (ss_err[filt_low] < best_err) { |
176 | 621 | best_err = ss_err[filt_low]; |
177 | 621 | } |
178 | 2.87k | filt_best = filt_low; |
179 | 2.87k | } |
180 | 10.1k | } |
181 | | |
182 | | // Now look at filt_high |
183 | 175k | if (filt_direction >= 0 && filt_high != filt_mid) { |
184 | 172k | if (ss_err[filt_high] < 0) { |
185 | 171k | ss_err[filt_high] = |
186 | 171k | try_filter_frame(sd, cpi, filt_high, partial_frame, plane, dir); |
187 | 171k | } |
188 | | // If value is significantly better than previous best, bias added against |
189 | | // raising filter value |
190 | 172k | if (ss_err[filt_high] < (best_err - bias)) { |
191 | 5.95k | best_err = ss_err[filt_high]; |
192 | 5.95k | filt_best = filt_high; |
193 | 5.95k | } |
194 | 172k | } |
195 | | |
196 | | // Half the step distance if the best filter value was the same as last time |
197 | 175k | if (filt_best == filt_mid) { |
198 | 166k | filter_step /= 2; |
199 | 166k | filt_direction = 0; |
200 | 166k | } else { |
201 | 8.64k | filt_direction = (filt_best < filt_mid) ? -1 : 1; |
202 | 8.64k | filt_mid = filt_best; |
203 | 8.64k | } |
204 | 175k | } |
205 | | |
206 | 74.9k | *best_filter_sse = ss_err[filt_best]; |
207 | | |
208 | 74.9k | return filt_best; |
209 | 74.9k | } |
210 | | |
211 | | void av1_pick_filter_level(const YV12_BUFFER_CONFIG *sd, AV1_COMP *cpi, |
212 | 105k | LPF_PICK_METHOD method) { |
213 | 105k | AV1_COMMON *const cm = &cpi->common; |
214 | 105k | const SequenceHeader *const seq_params = cm->seq_params; |
215 | 105k | const int num_planes = av1_num_planes(cm); |
216 | 105k | struct loopfilter *const lf = &cm->lf; |
217 | 105k | int disable_filter_rt_screen = 0; |
218 | 105k | (void)sd; |
219 | | |
220 | | // Enable loop filter sharpness only for allintra encoding mode, |
221 | | // as frames do not have to serve as references to others |
222 | 105k | lf->sharpness_level = |
223 | 105k | cpi->oxcf.mode == ALLINTRA ? cpi->oxcf.algo_cfg.sharpness : 0; |
224 | | |
225 | 105k | if (cpi->oxcf.algo_cfg.enable_adaptive_sharpness) { |
226 | | // Loop filter sharpness levels are highly nonlinear. Visually, lf sharpness |
227 | | // 1 is closer to 7 than it is to 0, so in practice adaptive sharpness is |
228 | | // written to pick levels 0, 1 and 7 to keep it simple. |
229 | 0 | int max_lf_sharpness; |
230 | |
|
231 | 0 | if (cm->quant_params.base_qindex <= 120) { |
232 | 0 | max_lf_sharpness = 7; |
233 | 0 | } else if (cm->quant_params.base_qindex <= 160) { |
234 | 0 | max_lf_sharpness = 1; |
235 | 0 | } else { |
236 | 0 | max_lf_sharpness = 0; |
237 | 0 | } |
238 | |
|
239 | 0 | lf->sharpness_level = AOMMIN(lf->sharpness_level, max_lf_sharpness); |
240 | 0 | } |
241 | | |
242 | 105k | if (cpi->oxcf.tune_cfg.content == AOM_CONTENT_SCREEN && |
243 | 0 | cpi->oxcf.q_cfg.aq_mode == CYCLIC_REFRESH_AQ && |
244 | 0 | cpi->sf.rt_sf.skip_lf_screen) |
245 | 0 | disable_filter_rt_screen = av1_cyclic_refresh_disable_lf_cdef(cpi); |
246 | | |
247 | 105k | if (disable_filter_rt_screen || |
248 | 105k | cpi->oxcf.algo_cfg.loopfilter_control == LOOPFILTER_NONE || |
249 | 105k | (cpi->oxcf.algo_cfg.loopfilter_control == LOOPFILTER_REFERENCE && |
250 | 0 | cpi->ppi->rtc_ref.non_reference_frame)) { |
251 | 0 | lf->filter_level[0] = 0; |
252 | 0 | lf->filter_level[1] = 0; |
253 | 0 | return; |
254 | 0 | } |
255 | | |
256 | 105k | if (method == LPF_PICK_MINIMAL_LPF) { |
257 | 0 | lf->filter_level[0] = 0; |
258 | 0 | lf->filter_level[1] = 0; |
259 | 105k | } else if (method >= LPF_PICK_FROM_Q) { |
260 | 63.4k | const int min_filter_level = 0; |
261 | 63.4k | const int max_filter_level = get_max_filter_level(cpi); |
262 | 63.4k | const int q = av1_ac_quant_QTX(cm->quant_params.base_qindex, 0, |
263 | 63.4k | seq_params->bit_depth); |
264 | | // based on tests result for rtc test set |
265 | | // 0.04590 boosted or 0.02295 non-booseted in 18-bit fixed point |
266 | 63.4k | const int strength_boost_q_treshold = 0; |
267 | 63.4k | int inter_frame_multiplier = |
268 | 63.4k | (q > strength_boost_q_treshold || |
269 | 0 | (cpi->sf.rt_sf.use_nonrd_pick_mode && |
270 | 0 | cpi->common.width * cpi->common.height > 352 * 288)) |
271 | 63.4k | ? 12034 |
272 | 63.4k | : 6017; |
273 | | // Increase strength on base TL0 for temporal layers, for low-resoln, |
274 | | // based on frame source_sad. |
275 | 63.4k | if (cpi->svc.number_temporal_layers > 1 && |
276 | 0 | cpi->svc.temporal_layer_id == 0 && |
277 | 0 | cpi->common.width * cpi->common.height <= 352 * 288 && |
278 | 0 | cpi->sf.rt_sf.use_nonrd_pick_mode) { |
279 | 0 | if (cpi->rc.frame_source_sad > 100000) |
280 | 0 | inter_frame_multiplier = inter_frame_multiplier << 1; |
281 | 0 | else if (cpi->rc.frame_source_sad > 50000) |
282 | 0 | inter_frame_multiplier = 3 * (inter_frame_multiplier >> 1); |
283 | 63.4k | } else if (cpi->sf.rt_sf.use_fast_fixed_part) { |
284 | 0 | inter_frame_multiplier = inter_frame_multiplier << 1; |
285 | 0 | } |
286 | | // These values were determined by linear fitting the result of the |
287 | | // searched level for 8 bit depth: |
288 | | // Keyframes: filt_guess = q * 0.06699 - 1.60817 |
289 | | // Other frames: filt_guess = q * inter_frame_multiplier + 2.48225 |
290 | | // |
291 | | // And high bit depth separately: |
292 | | // filt_guess = q * 0.316206 + 3.87252 |
293 | 63.4k | int filt_guess; |
294 | 63.4k | switch (seq_params->bit_depth) { |
295 | 46.9k | case AOM_BITS_8: |
296 | 46.9k | filt_guess = |
297 | 46.9k | (cm->current_frame.frame_type == KEY_FRAME) |
298 | 46.9k | ? ROUND_POWER_OF_TWO(q * 17563 - 421574, 18) |
299 | 46.9k | : ROUND_POWER_OF_TWO(q * inter_frame_multiplier + 650707, 18); |
300 | 46.9k | break; |
301 | 7.14k | case AOM_BITS_10: |
302 | 7.14k | filt_guess = ROUND_POWER_OF_TWO(q * 20723 + 4060632, 20); |
303 | 7.14k | break; |
304 | 9.32k | case AOM_BITS_12: |
305 | 9.32k | filt_guess = ROUND_POWER_OF_TWO(q * 20723 + 16242526, 22); |
306 | 9.32k | break; |
307 | 0 | default: |
308 | 0 | assert(0 && |
309 | 0 | "bit_depth should be AOM_BITS_8, AOM_BITS_10 " |
310 | 0 | "or AOM_BITS_12"); |
311 | 0 | return; |
312 | 63.4k | } |
313 | 63.4k | if (seq_params->bit_depth != AOM_BITS_8 && |
314 | 16.4k | cm->current_frame.frame_type == KEY_FRAME) |
315 | 14.3k | filt_guess -= 4; |
316 | | // TODO(chengchen): retrain the model for Y, U, V filter levels |
317 | 63.4k | lf->filter_level[0] = clamp(filt_guess, min_filter_level, max_filter_level); |
318 | 63.4k | lf->filter_level[1] = clamp(filt_guess, min_filter_level, max_filter_level); |
319 | 63.4k | lf->filter_level_u = clamp(filt_guess, min_filter_level, max_filter_level); |
320 | 63.4k | lf->filter_level_v = clamp(filt_guess, min_filter_level, max_filter_level); |
321 | 63.4k | if (cpi->oxcf.algo_cfg.loopfilter_control == LOOPFILTER_SELECTIVELY && |
322 | 0 | !frame_is_intra_only(cm) && !cpi->rc.high_source_sad) { |
323 | 0 | if (cpi->oxcf.tune_cfg.content == AOM_CONTENT_SCREEN) { |
324 | 0 | lf->filter_level[0] = 0; |
325 | 0 | lf->filter_level[1] = 0; |
326 | 0 | } else { |
327 | 0 | const int num4x4 = (cm->width >> 2) * (cm->height >> 2); |
328 | 0 | const int newmv_thresh = 7; |
329 | 0 | const int distance_since_key_thresh = 5; |
330 | 0 | if ((cpi->td.rd_counts.newmv_or_intra_blocks * 100 / num4x4) < |
331 | 0 | newmv_thresh && |
332 | 0 | cpi->rc.frames_since_key > distance_since_key_thresh) { |
333 | 0 | lf->filter_level[0] = 0; |
334 | 0 | lf->filter_level[1] = 0; |
335 | 0 | } |
336 | 0 | } |
337 | 0 | } |
338 | 63.4k | } else { |
339 | 41.9k | int last_frame_filter_level[4] = { 0 }; |
340 | 41.9k | if (!frame_is_intra_only(cm)) { |
341 | 16.7k | last_frame_filter_level[0] = cpi->ppi->filter_level[0]; |
342 | 16.7k | last_frame_filter_level[1] = cpi->ppi->filter_level[1]; |
343 | 16.7k | last_frame_filter_level[2] = cpi->ppi->filter_level_u; |
344 | 16.7k | last_frame_filter_level[3] = cpi->ppi->filter_level_v; |
345 | 16.7k | } |
346 | | // The frame buffer last_frame_uf is used to store the non-loop filtered |
347 | | // reconstructed frame in search_filter_level(). |
348 | 41.9k | if (aom_realloc_frame_buffer( |
349 | 41.9k | &cpi->last_frame_uf, cm->width, cm->height, |
350 | 41.9k | seq_params->subsampling_x, seq_params->subsampling_y, |
351 | 41.9k | seq_params->use_highbitdepth, cpi->oxcf.border_in_pixels, |
352 | 41.9k | cm->features.byte_alignment, NULL, NULL, NULL, false, 0)) |
353 | 0 | aom_internal_error(cm->error, AOM_CODEC_MEM_ERROR, |
354 | 0 | "Failed to allocate last frame buffer"); |
355 | | |
356 | 41.9k | int64_t zero_filter_sse[MAX_MB_PLANE] = { 0 }; |
357 | 41.9k | int64_t best_filter_sse[MAX_MB_PLANE] = { 0 }; |
358 | | |
359 | 41.9k | if (cpi->sf.lpf_sf.skip_loop_filter_using_filt_error >= 1) { |
360 | 0 | for (int plane = 0; plane < num_planes; plane++) { |
361 | 0 | zero_filter_sse[plane] = aom_get_sse_plane( |
362 | 0 | sd, &cm->cur_frame->buf, plane, cm->seq_params->use_highbitdepth); |
363 | 0 | } |
364 | 0 | } |
365 | | |
366 | 41.9k | lf->filter_level[0] = lf->filter_level[1] = |
367 | 41.9k | search_filter_level(sd, cpi, method == LPF_PICK_FROM_SUBIMAGE, |
368 | 41.9k | last_frame_filter_level, 0, 2, &best_filter_sse[0]); |
369 | 41.9k | if (method != LPF_PICK_FROM_FULL_IMAGE_NON_DUAL) { |
370 | 0 | lf->filter_level[0] = search_filter_level( |
371 | 0 | sd, cpi, method == LPF_PICK_FROM_SUBIMAGE, last_frame_filter_level, 0, |
372 | 0 | 0, &best_filter_sse[0]); |
373 | 0 | lf->filter_level[1] = search_filter_level( |
374 | 0 | sd, cpi, method == LPF_PICK_FROM_SUBIMAGE, last_frame_filter_level, 0, |
375 | 0 | 1, &best_filter_sse[0]); |
376 | 0 | } |
377 | | |
378 | 41.9k | if (num_planes > 1) { |
379 | 16.5k | lf->filter_level_u = search_filter_level( |
380 | 16.5k | sd, cpi, method == LPF_PICK_FROM_SUBIMAGE, last_frame_filter_level, 1, |
381 | 16.5k | 0, &best_filter_sse[1]); |
382 | 16.5k | lf->filter_level_v = search_filter_level( |
383 | 16.5k | sd, cpi, method == LPF_PICK_FROM_SUBIMAGE, last_frame_filter_level, 2, |
384 | 16.5k | 0, &best_filter_sse[2]); |
385 | 16.5k | } |
386 | | |
387 | 41.9k | lf->backup_filter_level[0] = lf->filter_level[0]; |
388 | 41.9k | lf->backup_filter_level[1] = lf->filter_level[1]; |
389 | 41.9k | lf->backup_filter_level_u = lf->filter_level_u; |
390 | 41.9k | lf->backup_filter_level_v = lf->filter_level_v; |
391 | | |
392 | 41.9k | if (cpi->sf.lpf_sf.adaptive_luma_loop_filter_skip >= 1) { |
393 | 0 | int32_t min_ref_filter_level[2] = { MAX_LOOP_FILTER, MAX_LOOP_FILTER }; |
394 | | // Find the minimum luma filter levels across all reference frames. |
395 | 0 | for (int ref = LAST_FRAME; ref <= ALTREF_FRAME; ++ref) { |
396 | 0 | const RefCntBuffer *const buf = get_ref_frame_buf(cm, ref); |
397 | 0 | if (buf == NULL) continue; |
398 | | |
399 | 0 | if (buf->filter_level[0] != -1) |
400 | 0 | min_ref_filter_level[0] = |
401 | 0 | AOMMIN(min_ref_filter_level[0], buf->filter_level[0]); |
402 | 0 | if (buf->filter_level[1] != -1) |
403 | 0 | min_ref_filter_level[1] = |
404 | 0 | AOMMIN(min_ref_filter_level[1], buf->filter_level[1]); |
405 | 0 | } |
406 | | |
407 | | // Reset luma filter levels to zero based on minimum filter levels of |
408 | | // reference frames and current frame's pyramid level. |
409 | 0 | unsigned int pyramid_level = cm->current_frame.pyramid_level; |
410 | 0 | if (pyramid_level > 1) { |
411 | 0 | int filter_threshold; |
412 | 0 | if (pyramid_level >= 5) |
413 | 0 | filter_threshold = 32; |
414 | 0 | else if (pyramid_level >= 4) |
415 | 0 | filter_threshold = 16; |
416 | 0 | else |
417 | 0 | filter_threshold = 8; |
418 | |
|
419 | 0 | const bool reset_filter_level_y = |
420 | 0 | lf->filter_level[0] < filter_threshold && |
421 | 0 | lf->filter_level[1] < filter_threshold && |
422 | 0 | lf->filter_level_u < filter_threshold && |
423 | 0 | lf->filter_level_v < filter_threshold && |
424 | 0 | min_ref_filter_level[0] == 0 && min_ref_filter_level[1] == 0; |
425 | 0 | if (reset_filter_level_y) { |
426 | 0 | lf->filter_level[0] = 0; |
427 | 0 | lf->filter_level[1] = 0; |
428 | 0 | } |
429 | 0 | } |
430 | 0 | } |
431 | | |
432 | 41.9k | if (lf->filter_level[0] != 0 && lf->filter_level[1] != 0 && |
433 | 2.97k | cpi->sf.lpf_sf.skip_loop_filter_using_filt_error >= 1) { |
434 | 0 | const double pct_improvement_thresh = 2.0; |
435 | 0 | bool reset_filter_level_y = true; |
436 | | |
437 | | // Calculate the percentage improvement in SSE for each plane. This |
438 | | // measures the relative reduction in error when applying the filter |
439 | | // compared to no filtering. |
440 | 0 | for (int plane = 0; plane < num_planes; plane++) { |
441 | 0 | const double pct_improvement_sse = |
442 | 0 | ((zero_filter_sse[plane] - best_filter_sse[plane]) * 100.0) / |
443 | 0 | zero_filter_sse[plane]; |
444 | 0 | reset_filter_level_y &= pct_improvement_sse < pct_improvement_thresh; |
445 | 0 | } |
446 | |
|
447 | 0 | if (reset_filter_level_y) { |
448 | 0 | lf->filter_level[0] = 0; |
449 | 0 | lf->filter_level[1] = 0; |
450 | 0 | } |
451 | 0 | } |
452 | | |
453 | | // Store the current frame's filter levels to be referenced |
454 | | // while determining the minimum filter level from reference frames. |
455 | 41.9k | cm->cur_frame->filter_level[0] = lf->filter_level[0]; |
456 | 41.9k | cm->cur_frame->filter_level[1] = lf->filter_level[1]; |
457 | 41.9k | } |
458 | 105k | } |