PR #25631 opened by Kilian (lusoris) URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/25631 Patch URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/25631.patch
The normalize filter scales its 8-bit black and white point options to the input bit depth by bit replication: the low bits repeat the high bits so that 255 maps to the full-scale value at every depth. The expression introduced in c7ece8e26f masks a single bit of the point value instead of replicating the high bits, which is wrong at both ends of the range. A requested white point of 255 becomes 256 at 8 bits and 65280 at 16 bits instead of 255 and 65535, and at 8-bit depth any odd point value is one too high. With `normalize=blackpt=white:whitept=black` on a black frame the output is 0 at 8 bits instead of 255, and 65280 at 16 bits instead of 65535. Ordinary input lands one off the requested range, for example 128..130 for a requested 127..129. The fix shifts by `16 - depth` so the 8-bit point is replicated into the high bits of the target depth: 255 maps to 255 at 8 bits and to 65535 at 16 bits. Two FATE tests are added for the two ends of the supported depth range (rgb24 and gbrp16le). Both fail on current master and pass with the fix. Measured on master before and after: - `normalize=blackpt=white:whitept=black` on black rgb24: 0 -> 255 - same on gbrp16le: 65280 -> 65535 - requested 127..129 on rgb24: 128..130 -> 127..129 >From 52d54a07b48b72e7fff9f817b1f945ebb0d15a0a Mon Sep 17 00:00:00 2001 From: lusoris <[email protected]> Date: Fri, 30 Oct 2026 23:50:54 +0100 Subject: [PATCH] avfilter/vf_normalize: fix white and black point scaling The 8-bit black and white point options are scaled to the input bit depth by bit replication, so that the low bits repeat the high bits and 255 maps to the full-scale value at every depth. The expression introduced in c7ece8e26f masks a single bit of the point value instead of replicating the high bits, which is wrong at both ends of the range: a white point of 255 becomes 256 at 8 bits and 65280 at 16 bits instead of 255 and 65535, and at 8-bit depth any odd point value is one too high. With blackpt=white and whitept=black on a black frame the output is 0 at 8 bits instead of 255, and 65280 at 16 bits instead of 65535. Ordinary input lands one off the requested range, for example 128..130 for a requested 127..129. Shift by 16 - depth so the 8-bit point is replicated into the high bits of the target depth: 255 maps to 255 at 8 bits and to 65535 at 16 bits. Add FATE tests for the two ends of the supported depth range. Signed-off-by: lusoris <[email protected]> --- libavfilter/vf_normalize.c | 4 ++-- tests/fate/filter-video.mak | 4 ++++ tests/ref/fate/filter-normalize-white-gbrp16le | 6 ++++++ tests/ref/fate/filter-normalize-white-rgb24 | 6 ++++++ 4 files changed, 18 insertions(+), 2 deletions(-) create mode 100644 tests/ref/fate/filter-normalize-white-gbrp16le create mode 100644 tests/ref/fate/filter-normalize-white-rgb24 diff --git a/libavfilter/vf_normalize.c b/libavfilter/vf_normalize.c index 6d32bc85fcb..937c802f77a 100644 --- a/libavfilter/vf_normalize.c +++ b/libavfilter/vf_normalize.c @@ -437,8 +437,8 @@ static int config_input(AVFilterLink *inlink) for (c = 0; c < 3; c++) { s->min[c].history = s->history_mem + (c*2) * s->history_len; s->max[c].history = s->history_mem + (c*2+1) * s->history_len; - s->sblackpt[c] = scale * s->blackpt[c] + (s->blackpt[c] & (1 << (s->depth - 8))); - s->swhitept[c] = scale * s->whitept[c] + (s->whitept[c] & (1 << (s->depth - 8))); + s->sblackpt[c] = scale * s->blackpt[c] + (s->blackpt[c] >> (16 - s->depth)); + s->swhitept[c] = scale * s->whitept[c] + (s->whitept[c] >> (16 - s->depth)); } planar = desc->flags & AV_PIX_FMT_FLAG_PLANAR; diff --git a/tests/fate/filter-video.mak b/tests/fate/filter-video.mak index 95254ff3fcf..9c32dd1149f 100644 --- a/tests/fate/filter-video.mak +++ b/tests/fate/filter-video.mak @@ -181,6 +181,10 @@ FATE_FILTER-$(call FILTERFRAMECRC, TESTSRC FORMAT CONCAT SCALE, LAVFI_INDEV FILE fate-filter-lavd-scalenorm: tests/data/filtergraphs/scalenorm fate-filter-lavd-scalenorm: CMD = framecrc -f lavfi -graph_file $(TARGET_PATH)/tests/data/filtergraphs/scalenorm -i dummy +FATE_FILTER-$(call FILTERFRAMECRC, COLOR FORMAT NORMALIZE) += $(addprefix fate-filter-normalize-white-, rgb24 gbrp16le) +fate-filter-normalize-white-rgb24: CMD = framecrc -flags bitexact -lavfi color=c=black:s=16x16:r=1:d=1,format=rgb24,normalize=blackpt=white:whitept=black +fate-filter-normalize-white-gbrp16le: CMD = framecrc -flags bitexact -lavfi color=c=black:s=16x16:r=1:d=1,format=gbrp16le,normalize=blackpt=white:whitept=black + FATE_FILTER-$(call FILTERFRAMECRC, COLOR FORMAT SCALE CROP) += fate-filter-scale-fast-bilinear-wide-edge fate-filter-scale-fast-bilinear-wide-edge: CMD = framecrc -flags bitexact -lavfi color=c=red:s=40000x1:r=1:d=1,format=yuv444p,scale=40032:1:flags=fast_bilinear,crop=1:1:40031:0 -frames:v 1 diff --git a/tests/ref/fate/filter-normalize-white-gbrp16le b/tests/ref/fate/filter-normalize-white-gbrp16le new file mode 100644 index 00000000000..9789e180f25 --- /dev/null +++ b/tests/ref/fate/filter-normalize-white-gbrp16le @@ -0,0 +1,6 @@ +#tb 0: 1/1 +#media_type 0: video +#codec_id 0: rawvideo +#dimensions 0: 16x16 +#sar 0: 1/1 +0, 0, 0, 1, 1536, 0x0a2efa4b diff --git a/tests/ref/fate/filter-normalize-white-rgb24 b/tests/ref/fate/filter-normalize-white-rgb24 new file mode 100644 index 00000000000..c6eadac969c --- /dev/null +++ b/tests/ref/fate/filter-normalize-white-rgb24 @@ -0,0 +1,6 @@ +#tb 0: 1/1 +#media_type 0: video +#codec_id 0: rawvideo +#dimensions 0: 16x16 +#sar 0: 1/1 +0, 0, 0, 1, 768, 0x41d3fd1e -- 2.54.0 _______________________________________________ ffmpeg-devel mailing list -- [email protected] To unsubscribe send an email to [email protected]
