Commit 84b0dd1652 for aom
commit 84b0dd16528d804f546530d22d8f7f44f20fa6fc
Author: Lin Zheng <linzhen@google.com>
Date: Thu Sep 17 21:27:25 2026 +0000
Stabilize temporal filtering and TPL motion search for HBD
Favor zero motion vector (0,0) in stationary/near-stationary regions
during temporal filtering motion search and TPL mode estimation.
Disable sub-block motion estimation in temporal filtering and clamp
filter strength to 0 for low-contrast blocks when sharpness is 3.
Bug: 548701298
Change-Id: Id5f1a8ed67a1b609159a15a969064419b053beab
diff --git a/av1/encoder/temporal_filter.c b/av1/encoder/temporal_filter.c
index be7ff38d2f..97be8fc2b8 100644
--- a/av1/encoder/temporal_filter.c
+++ b/av1/encoder/temporal_filter.c
@@ -382,6 +382,27 @@ static void tf_motion_search(AV1_COMP *cpi, MACROBLOCK *mb,
*is_dc_diff_large = 50 * error < sse;
if (src_var <= 2 * (int64_t)distortion) *is_low_cntras = 1;
+#if CONFIG_AV1_HIGHBITDEPTH
+ const MACROBLOCKD *xd = &mb->e_mbd;
+ if (xd->bd > 8 && cpi->oxcf.algo_cfg.sharpness == 3) {
+ // Favor zero motion vector (0,0) for stationary/near-stationary regions
+ // when zero-motion error is close to the best found error or sufficiently
+ // small.
+ unsigned int zero_sse;
+ unsigned int zero_error = cpi->ppi->fn_ptr[block_size].vf(
+ ref_frame->y_buffer + y_offset, y_stride,
+ frame_to_filter->y_buffer + y_offset, y_stride, &zero_sse);
+ if (zero_error <= error + (error >> 4) || (zero_error / mb_pels) < 16) {
+ best_mv.as_mv = kZeroMv;
+ block_mv = kZeroMv;
+ *ref_mv = kZeroMv;
+ error = zero_error;
+ distortion = (int)zero_error;
+ block_mse = DIVIDE_AND_ROUND(error, mb_pels);
+ }
+ }
+#endif
+
// On 4 mid-blocks in the 64x64 tf block.
if (allow_me_for_sub_blks) {
// midblock_size is 32x32, which corresponds to each 32x32 block in the
@@ -1092,7 +1113,12 @@ void av1_tf_do_filtering_row(AV1_COMP *cpi, ThreadData *td, int mb_row) {
// partition only if the difference between max and min source variance of
// 4x4 blocks is greater than a threshold (which is derived empirically).
bool allow_me_for_sub_blks = true;
- if (cpi->sf.hl_sf.allow_sub_blk_me_in_tf) {
+ // In high bit-depth sharpness mode, disable sub-block motion estimation to
+ // avoid inconsistent filtering across sub-partitions and preserve
+ // sharpness.
+ if (mbd->bd > 8 && cpi->oxcf.algo_cfg.sharpness == 3) {
+ allow_me_for_sub_blks = false;
+ } else if (cpi->sf.hl_sf.allow_sub_blk_me_in_tf) {
const int is_hbd = is_frame_high_bitdepth(frame_to_filter);
// Initialize minimum variance to a large value and maximum variance to 0.
double blk_4x4_var_min = DBL_MAX;
@@ -1139,7 +1165,7 @@ void av1_tf_do_filtering_row(AV1_COMP *cpi, ThreadData *td, int mb_row) {
filter_strength = AOMMIN(filter_strength, 1);
if (cpi->oxcf.algo_cfg.sharpness == 3 && is_low_cntras)
- filter_strength = AOMMIN(filter_strength, 3);
+ filter_strength = AOMMIN(filter_strength, mbd->bd > 8 ? 0 : 3);
// Perform weighted averaging.
if (frame == filter_frame_idx) { // Frame to be filtered.
diff --git a/av1/encoder/tpl_model.c b/av1/encoder/tpl_model.c
index a22341bd6a..34cf2bfd9b 100644
--- a/av1/encoder/tpl_model.c
+++ b/av1/encoder/tpl_model.c
@@ -854,6 +854,20 @@ static inline void mode_estimation(AV1_COMP *cpi, TplTxfmStats *tpl_txfm_stats,
cpi, x, src_mb_buffer, ref_mb, src_stride, ref_stride, src_width,
ref_width, bsize, center_mvs[idx].mv.as_mv, &this_mv);
+#if CONFIG_AV1_HIGHBITDEPTH
+ if (xd->bd > 8 && cpi->oxcf.algo_cfg.sharpness == 3) {
+ // Bias towards (0,0) / short motion vectors in stationary background.
+ // Require a noticeable SAD improvement before replacing (0,0) with a
+ // non-zero MV.
+ if (bestsme != UINT32_MAX && best_rfidx_mv.as_int == 0 &&
+ this_mv.as_int != 0) {
+ if (thissme + (thissme >> 4) >= bestsme) {
+ continue;
+ }
+ }
+ }
+#endif
+
if (thissme < bestsme) {
bestsme = thissme;
best_rfidx_mv = this_mv;