// AV1 loop filter applies to the whole frame according to mi_rows and mi_cols, // which are calculated based on aligned width and aligned height, // In addition, if super res is enabled, it copies the whole frame // according to the aligned width and height (av1_superres_upscale()). // So we need to copy the whole filtered region, instead of the cropped region. // For example, input image size is: 160x90. // Then src->y_crop_width = 160, src->y_crop_height = 90. // The aligned frame size is: src->y_width = 160, src->y_height = 96. // AV1 aligns frame size to a multiple of 8, if there is // chroma subsampling, it is able to ensure the chroma is also // an integer number of mi units. mi unit is 4x4, 8 = 4 * 2, and 2 luma mi // units correspond to 1 chroma mi unit if there is subsampling. // See: aom_realloc_frame_buffer() in yv12config.c. staticvoid yv12_copy_plane(const YV12_BUFFER_CONFIG *src_bc,
YV12_BUFFER_CONFIG *dst_bc, int plane) { switch (plane) { case0: aom_yv12_copy_y(src_bc, dst_bc, 0); break; case1: aom_yv12_copy_u(src_bc, dst_bc, 0); break; case2: aom_yv12_copy_v(src_bc, dst_bc, 0); break; default: assert(plane >= 0 && plane <= 2); break;
}
}
// Re-instate the unfiltered frame
yv12_copy_plane(&cpi->last_frame_uf, &cm->cur_frame->buf, plane);
return filt_err;
}
static int search_filter_level(const YV12_BUFFER_CONFIG *sd, AV1_COMP *cpi,
int partial_frame, const int *last_frame_filter_level, int plane,
int dir, int64_t *best_filter_sse) { const AV1_COMMON *const cm = &cpi->common; const int min_filter_level = 0; const int max_filter_level = get_max_filter_level(cpi);
int filt_direction = 0;
int64_t best_err;
int filt_best;
// Start the search at the previous frame filter level unless it is now out of // range.
int lvl; switch (plane) { case0: switch (dir) { case2:
lvl = (last_frame_filter_level[0] + last_frame_filter_level[1] + 1) >> 1; break; case0: case1: lvl = last_frame_filter_level[dir]; break; default: assert(dir >= 0 && dir <= 2); return0;
} break; case1: lvl = last_frame_filter_level[2]; break; case2: lvl = last_frame_filter_level[3]; break; default: assert(plane >= 0 && plane <= 2); return0;
}
int filt_mid = clamp(lvl, min_filter_level, max_filter_level);
int filter_step = filt_mid < 16 ? 4 : filt_mid / 4; // Sum squared error at each filter level
int64_t ss_err[MAX_LOOP_FILTER + 1];
const int use_coarse_search = cpi->sf.lpf_sf.use_coarse_filter_level_search;
assert(use_coarse_search <= 1); staticconst int min_filter_step_lookup[2] = { 0, 2 }; // min_filter_step_thesh determines the stopping criteria for the search. // The search is terminated when filter_step equals min_filter_step_thesh. const int min_filter_step_thesh = min_filter_step_lookup[use_coarse_search];
// Set each entry to -1
memset(ss_err, 0xFF, sizeof(ss_err));
yv12_copy_plane(&cm->cur_frame->buf, &cpi->last_frame_uf, plane);
best_err = try_filter_frame(sd, cpi, filt_mid, partial_frame, plane, dir);
filt_best = filt_mid;
ss_err[filt_mid] = best_err;
while (filter_step > min_filter_step_thesh) { const int filt_high = AOMMIN(filt_mid + filter_step, max_filter_level); const int filt_low = AOMMAX(filt_mid - filter_step, min_filter_level);
// Bias against raising loop filter in favor of lowering it.
int64_t bias = (best_err >> (15 - (filt_mid / 8))) * filter_step;
// yx, bias less for large block size
if (cm->features.tx_mode != ONLY_4X4) bias >>= 1;
if (filt_direction <= 0 && filt_low != filt_mid) { // Get Low filter error score
if (ss_err[filt_low] < 0) {
ss_err[filt_low] =
try_filter_frame(sd, cpi, filt_low, partial_frame, plane, dir);
} // If value is close to the best so far then bias towards a lower loop // filter value.
if (ss_err[filt_low] < (best_err + bias)) { // Was it actually better than the previous best?
if (ss_err[filt_low] < best_err) {
best_err = ss_err[filt_low];
}
filt_best = filt_low;
}
}
// Now look at filt_high
if (filt_direction >= 0 && filt_high != filt_mid) {
if (ss_err[filt_high] < 0) {
ss_err[filt_high] =
try_filter_frame(sd, cpi, filt_high, partial_frame, plane, dir);
} // If value is significantly better than previous best, bias added against // raising filter value
if (ss_err[filt_high] < (best_err - bias)) {
best_err = ss_err[filt_high];
filt_best = filt_high;
}
}
// Half the step distance if the best filter value was the same as last time
if (filt_best == filt_mid) {
filter_step /= 2;
filt_direction = 0;
} else {
filt_direction = (filt_best < filt_mid) ? -1 : 1;
filt_mid = filt_best;
}
}
// Enable loop filter sharpness only for all-intra encoding mode, // or tune IQ or SSIMULACRA2. This is because: // - All-intra: frames do not have to serve as references to others // - Tune IQ/SSIMULACRA2: enabling loop filter sharpness has been found to // be beneficial for sharpness perception
if (cpi->oxcf.mode == ALLINTRA || cpi->oxcf.tune_cfg.tuning == AOM_TUNE_IQ ||
cpi->oxcf.tune_cfg.tuning == AOM_TUNE_SSIMULACRA2) {
lf->sharpness_level = cpi->oxcf.algo_cfg.sharpness;
} else {
lf->sharpness_level = 0;
}
if (cpi->oxcf.algo_cfg.enable_adaptive_sharpness) { // Loop filter sharpness levels are highly nonlinear. Visually, lf sharpness // 1 is closer to 7 than it is to 0, so in practice adaptive sharpness is // written to pick levels 0, 1 and 7 to keep it simple.
int max_lf_sharpness;
if (cpi->sf.lpf_sf.adaptive_luma_loop_filter_skip >= 1) {
int32_t min_ref_filter_level[2] = { MAX_LOOP_FILTER, MAX_LOOP_FILTER }; // Find the minimum luma filter levels across all reference frames.
for (int ref = LAST_FRAME; ref <= ALTREF_FRAME; ++ref) { const RefCntBuffer *const buf = get_ref_frame_buf(cm, ref);
if (buf == NULL) continue;
if (buf->filter_level[0] != -1)
min_ref_filter_level[0] =
AOMMIN(min_ref_filter_level[0], buf->filter_level[0]);
if (buf->filter_level[1] != -1)
min_ref_filter_level[1] =
AOMMIN(min_ref_filter_level[1], buf->filter_level[1]);
}
// Reset luma filter levels to zero based on minimum filter levels of // reference frames and current frame's pyramid level. unsigned int pyramid_level = cm->current_frame.pyramid_level;
if (pyramid_level > 1) {
int filter_threshold;
if (pyramid_level >= 4)
filter_threshold = 16; else
filter_threshold = 8;
// Calculate the percentage improvement in SSE for each plane. This // measures the relative reduction in error when applying the filter // compared to no filtering.
for (int plane = 0; plane < num_planes; plane++) { constdouble pct_improvement_sse =
((zero_filter_sse[plane] - best_filter_sse[plane]) * 100.0) /
zero_filter_sse[plane];
reset_filter_level_y &= pct_improvement_sse < pct_improvement_thresh;
}
// Store the current frame's filter levels to be referenced // while determining the minimum filter level from reference frames.
cm->cur_frame->filter_level[0] = lf->filter_level[0];
cm->cur_frame->filter_level[1] = lf->filter_level[1];
}
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.