* [FFmpeg-devel] [PATCH] vf_colorspace: Add an option to clamp trc LUT output [not found] <347789> @ 2025-08-18 15:55 ` Drew Dunne via ffmpeg-devel 2025-08-20 18:56 ` Ronald S. Bultje via ffmpeg-devel 0 siblings, 1 reply; 4+ messages in thread From: Drew Dunne via ffmpeg-devel @ 2025-08-18 15:55 UTC (permalink / raw) To: ffmpeg-devel; +Cc: Drew Dunne Add a new flag to the vf_colorspace filter which provides the user an option to clamp the linear and delinear transfer characteristics LUT values to the [0, 1] represented range. This helps constrain the potential value range when converting between colorspaces. Signed-off-by: Drew Dunne <asdunne@google.com> --- doc/filters.texi | 3 +++ libavfilter/vf_colorspace.c | 14 ++++++++++++-- 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/doc/filters.texi b/doc/filters.texi index 908c98a3cf..cb09d73f62 100644 --- a/doc/filters.texi +++ b/doc/filters.texi @@ -10403,6 +10403,9 @@ von Kries whitepoint adaptation identity whitepoint adaptation (i.e. no whitepoint adaptation) @end table +@item clamptrc +Clamps the linear and delinear transfer characteristics LUT values to the [0, 1] represented range. + @item iall Override all input properties at once. Same accepted values as @ref{all}. diff --git a/libavfilter/vf_colorspace.c b/libavfilter/vf_colorspace.c index e1f4725f63..15b4858b24 100644 --- a/libavfilter/vf_colorspace.c +++ b/libavfilter/vf_colorspace.c @@ -123,6 +123,7 @@ typedef struct ColorSpaceContext { int fast_mode; enum DitherMode dither; enum WhitepointAdaptation wp_adapt; + int clamp_trc; int16_t *rgb[3]; ptrdiff_t rgb_stride; @@ -215,7 +216,9 @@ static int fill_gamma_table(ColorSpaceContext *s) } else { d = out_alpha * pow(v, out_gamma) - (out_alpha - 1.0); } - s->delin_lut[n] = av_clip_int16(lrint(d * 28672.0)); + long d_rounded = lrint(d * 28672.0); + s->delin_lut[n] = s->clamp_trc ? av_clip64(d_rounded, 0, 28672) + : av_clip_int16(d_rounded); // linearize if (v <= -in_beta * in_delta) { @@ -225,7 +228,9 @@ static int fill_gamma_table(ColorSpaceContext *s) } else { l = pow((v + in_alpha - 1.0) * in_ialpha, in_igamma); } - s->lin_lut[n] = av_clip_int16(lrint(l * 28672.0)); + long l_rounded = lrint(l * 28672.0); + s->lin_lut[n] = s->clamp_trc ? av_clip64(l_rounded, 0, 28672) + : av_clip_int16(l_rounded); } return 0; @@ -1000,6 +1005,11 @@ static const AVOption colorspace_options[] = { ENUM("vonkries", WP_ADAPT_VON_KRIES, "wpadapt"), ENUM("identity", WP_ADAPT_IDENTITY, "wpadapt"), + { "clamptrc", + "Clamps the linear and delinear LUT output values to the range [0, 1].", + OFFSET(clamp_trc), AV_OPT_TYPE_BOOL, { .i64 = 0 }, + 0, 1, FLAGS }, + { "iall", "Set all input color properties together", OFFSET(user_iall), AV_OPT_TYPE_INT, { .i64 = CS_UNSPECIFIED }, CS_UNSPECIFIED, CS_NB - 1, FLAGS, .unit = "all" }, -- 2.51.0.rc1.163.g2494970778-goog _______________________________________________ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org https://ffmpeg.org/mailman/listinfo/ffmpeg-devel To unsubscribe, visit link above, or email ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe". ^ permalink raw reply [flat|nested] 4+ messages in thread
* Re: [FFmpeg-devel] [PATCH] vf_colorspace: Add an option to clamp trc LUT output 2025-08-18 15:55 ` [FFmpeg-devel] [PATCH] vf_colorspace: Add an option to clamp trc LUT output Drew Dunne via ffmpeg-devel @ 2025-08-20 18:56 ` Ronald S. Bultje via ffmpeg-devel 2025-08-25 14:49 ` Drew Dunne via ffmpeg-devel 0 siblings, 1 reply; 4+ messages in thread From: Ronald S. Bultje via ffmpeg-devel @ 2025-08-20 18:56 UTC (permalink / raw) To: FFmpeg development discussions and patches; +Cc: Ronald S. Bultje, Drew Dunne Hi Drew, On Mon, Aug 18, 2025 at 11:55 AM Drew Dunne via ffmpeg-devel < ffmpeg-devel@ffmpeg.org> wrote: > Add a new flag to the vf_colorspace filter which provides the user an > option to clamp the linear and delinear transfer characteristics LUT > values to the [0, 1] represented range. This helps constrain the > potential value range when converting between colorspaces. > > Signed-off-by: Drew Dunne <asdunne@google.com> > --- > doc/filters.texi | 3 +++ > libavfilter/vf_colorspace.c | 14 ++++++++++++-- > 2 files changed, 15 insertions(+), 2 deletions(-) > > diff --git a/doc/filters.texi b/doc/filters.texi > index 908c98a3cf..cb09d73f62 100644 > --- a/doc/filters.texi > +++ b/doc/filters.texi > @@ -10403,6 +10403,9 @@ von Kries whitepoint adaptation > identity whitepoint adaptation (i.e. no whitepoint adaptation) > @end table > > +@item clamptrc > +Clamps the linear and delinear transfer characteristics LUT values to the > [0, 1] represented range. > + > @item iall > Override all input properties at once. Same accepted values as @ref{all}. > > diff --git a/libavfilter/vf_colorspace.c b/libavfilter/vf_colorspace.c > index e1f4725f63..15b4858b24 100644 > --- a/libavfilter/vf_colorspace.c > +++ b/libavfilter/vf_colorspace.c > @@ -123,6 +123,7 @@ typedef struct ColorSpaceContext { > int fast_mode; > enum DitherMode dither; > enum WhitepointAdaptation wp_adapt; > + int clamp_trc; > > int16_t *rgb[3]; > ptrdiff_t rgb_stride; > @@ -215,7 +216,9 @@ static int fill_gamma_table(ColorSpaceContext *s) > } else { > d = out_alpha * pow(v, out_gamma) - (out_alpha - 1.0); > } > - s->delin_lut[n] = av_clip_int16(lrint(d * 28672.0)); > + long d_rounded = lrint(d * 28672.0); > + s->delin_lut[n] = s->clamp_trc ? av_clip64(d_rounded, 0, 28672) > + : av_clip_int16(d_rounded); > > // linearize > if (v <= -in_beta * in_delta) { > @@ -225,7 +228,9 @@ static int fill_gamma_table(ColorSpaceContext *s) > } else { > l = pow((v + in_alpha - 1.0) * in_ialpha, in_igamma); > } > - s->lin_lut[n] = av_clip_int16(lrint(l * 28672.0)); > + long l_rounded = lrint(l * 28672.0); > + s->lin_lut[n] = s->clamp_trc ? av_clip64(l_rounded, 0, 28672) > + : av_clip_int16(l_rounded); > } > > return 0; > @@ -1000,6 +1005,11 @@ static const AVOption colorspace_options[] = { > ENUM("vonkries", WP_ADAPT_VON_KRIES, "wpadapt"), > ENUM("identity", WP_ADAPT_IDENTITY, "wpadapt"), > > + { "clamptrc", > + "Clamps the linear and delinear LUT output values to the range [0, > 1].", > + OFFSET(clamp_trc), AV_OPT_TYPE_BOOL, { .i64 = 0 }, > + 0, 1, FLAGS }, > + > { "iall", "Set all input color properties together", > OFFSET(user_iall), AV_OPT_TYPE_INT, { .i64 = CS_UNSPECIFIED }, > CS_UNSPECIFIED, CS_NB - 1, FLAGS, .unit = "all" }, > -- > 2.51.0.rc1.163.g2494970778-goog Can you provide an example where this is relevant? I'm trying to understand the three components here: changing from int to long, clipping in 64bit instead of 16bit, and adding positive+negative range clamp. I'm wondering which of these you've observed, and what type of minimal fix would be sufficient. (Part of the question here is that we might not want this to be an AVOption, but to be always-on.) Thanks, Ronald _______________________________________________ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org https://ffmpeg.org/mailman/listinfo/ffmpeg-devel To unsubscribe, visit link above, or email ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe". ^ permalink raw reply [flat|nested] 4+ messages in thread
* [FFmpeg-devel] [PATCH] vf_colorspace: Add an option to clamp trc LUT output 2025-08-20 18:56 ` Ronald S. Bultje via ffmpeg-devel @ 2025-08-25 14:49 ` Drew Dunne via ffmpeg-devel 2025-08-26 18:33 ` [FFmpeg-devel] " Ronald S. Bultje via ffmpeg-devel 0 siblings, 1 reply; 4+ messages in thread From: Drew Dunne via ffmpeg-devel @ 2025-08-25 14:49 UTC (permalink / raw) To: ffmpeg-devel; +Cc: Drew Dunne Add a new flag to the vf_colorspace filter which provides the user an option to clamp the linear and delinear transfer characteristics LUT values to the [0, 1] represented range. This helps constrain the potential value range when converting between colorspaces. Signed-off-by: Drew Dunne <asdunne@google.com> --- doc/filters.texi | 3 +++ libavfilter/vf_colorspace.c | 14 ++++++++++++-- 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/doc/filters.texi b/doc/filters.texi index 908c98a3cf..cb09d73f62 100644 --- a/doc/filters.texi +++ b/doc/filters.texi @@ -10403,6 +10403,9 @@ von Kries whitepoint adaptation identity whitepoint adaptation (i.e. no whitepoint adaptation) @end table +@item clamptrc +Clamps the linear and delinear transfer characteristics LUT values to the [0, 1] represented range. + @item iall Override all input properties at once. Same accepted values as @ref{all}. diff --git a/libavfilter/vf_colorspace.c b/libavfilter/vf_colorspace.c index e1f4725f63..ad8596918f 100644 --- a/libavfilter/vf_colorspace.c +++ b/libavfilter/vf_colorspace.c @@ -123,6 +123,7 @@ typedef struct ColorSpaceContext { int fast_mode; enum DitherMode dither; enum WhitepointAdaptation wp_adapt; + int clamp_trc; int16_t *rgb[3]; ptrdiff_t rgb_stride; @@ -215,7 +216,9 @@ static int fill_gamma_table(ColorSpaceContext *s) } else { d = out_alpha * pow(v, out_gamma) - (out_alpha - 1.0); } - s->delin_lut[n] = av_clip_int16(lrint(d * 28672.0)); + int d_rounded = lrint(d * 28672.0); + s->delin_lut[n] = s->clamp_trc ? av_clip(d_rounded, 0, 28672) + : av_clip_int16(d_rounded); // linearize if (v <= -in_beta * in_delta) { @@ -225,7 +228,9 @@ static int fill_gamma_table(ColorSpaceContext *s) } else { l = pow((v + in_alpha - 1.0) * in_ialpha, in_igamma); } - s->lin_lut[n] = av_clip_int16(lrint(l * 28672.0)); + int l_rounded = lrint(l * 28672.0); + s->lin_lut[n] = s->clamp_trc ? av_clip(l_rounded, 0, 28672) + : av_clip_int16(l_rounded); } return 0; @@ -1000,6 +1005,11 @@ static const AVOption colorspace_options[] = { ENUM("vonkries", WP_ADAPT_VON_KRIES, "wpadapt"), ENUM("identity", WP_ADAPT_IDENTITY, "wpadapt"), + { "clamptrc", + "Clamps the linear and delinear LUT output values to the range [0, 1].", + OFFSET(clamp_trc), AV_OPT_TYPE_BOOL, { .i64 = 0 }, + 0, 1, FLAGS }, + { "iall", "Set all input color properties together", OFFSET(user_iall), AV_OPT_TYPE_INT, { .i64 = CS_UNSPECIFIED }, CS_UNSPECIFIED, CS_NB - 1, FLAGS, .unit = "all" }, -- 2.51.0.rc2.233.g662b1ed5c5-goog Certain colors when going through the conversion can result in out of gamut colors after the rotation. The colorspace filter allows that with the extended range. The added clamping just keeps the colors within the [0, 1) range rather than using that extended range. I'm not enough of a color scientist to say which is correct, but there are certain situations where we would prefer to keep the colors in gamut. The example I have is: A solid color image of 8-bit YUV: Y=157, U=164, V=98. Specify the input as: Input range: MPEG In color matrix: BT470BG In color primaries: BT470M In color transfer characteristics: Gamma 28 Output as: Out color range: JPEG Out color matrix: BT.709 Out color primaries: BT.709 Out color transfer characteristics: BT.709 During the calculation you get: Input YUV: y=157, u=164, v-98 Post-yuv2rgb BT.470BG: r=0.456055, g=0.684152, b=0.928606 Post-apply gamma28 linear LUT: r=0.110979, g=0.345494, b=0.812709 Post-color rotation BT.470M to BT.709: r=-0.04161, g=0.384626, b=0.852400 Post-apply Rec.709 delinear LUT: r=-0.16382, g=0.615932, b=0.923793 Post-rgb2yuv Rec.709 matrix: y=120, u=190, v=25 Where with this change, the delinear LUT output would be clamped to 0, so the result would be: r=0.000000, g=0.612390, b=0.918807 and a final output of y=129, u=185, v=46 As for the long and av_clip64, this was just because lrint returned a long, so I left it as that and then used av_clip64 to the [0,1) range to avoid overflow. But re-reading, it looks like av_clip_int16 would downcast that long to int anyway so the possibility of overflow already existed there. I've put it back to int just to match the existing behavior. _______________________________________________ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org https://ffmpeg.org/mailman/listinfo/ffmpeg-devel To unsubscribe, visit link above, or email ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe". ^ permalink raw reply [flat|nested] 4+ messages in thread
* [FFmpeg-devel] Re: [PATCH] vf_colorspace: Add an option to clamp trc LUT output 2025-08-25 14:49 ` Drew Dunne via ffmpeg-devel @ 2025-08-26 18:33 ` Ronald S. Bultje via ffmpeg-devel 0 siblings, 0 replies; 4+ messages in thread From: Ronald S. Bultje via ffmpeg-devel @ 2025-08-26 18:33 UTC (permalink / raw) To: FFmpeg development discussions and patches; +Cc: Drew Dunne, Ronald S. Bultje Hi Drew, Thanks for the bug report! On Mon, Aug 25, 2025 at 10:50 AM Drew Dunne via ffmpeg-devel < ffmpeg-devel@ffmpeg.org> wrote: > A solid color image of 8-bit YUV: Y=157, U=164, V=98. > > Specify the input as: > > Input range: MPEG > In color matrix: BT470BG > In color primaries: BT470M > In color transfer characteristics: Gamma 28 > > Output as: > Out color range: JPEG > Out color matrix: BT.709 > Out color primaries: BT.709 > Out color transfer characteristics: BT.709 > > During the calculation you get: > > Input YUV: y=157, u=164, v-98 > Post-yuv2rgb BT.470BG: r=0.456055, g=0.684152, b=0.928606 > Post-apply gamma28 linear LUT: r=0.110979, g=0.345494, b=0.812709 > Post-color rotation BT.470M to BT.709: r=-0.04161, g=0.384626, b=0.852400 > Post-apply Rec.709 delinear LUT: r=-0.16382, g=0.615932, b=0.923793 > Post-rgb2yuv Rec.709 matrix: y=120, u=190, v=25 > > Where with this change, the delinear LUT output would be clamped to 0, > so the result would be: > r=0.000000, g=0.612390, b=0.918807 and a final output of > y=129, u=185, v=46 So for those of us playing along, a repro looks like this: $ hexdump /tmp/in.yuv 0000000 9d9d 9d9d 62a4 $ git diff diff --git a/libavfilter/vf_colorspace.c b/libavfilter/vf_colorspace.c index e1f4725f635..512eb620fcf 100644 --- a/libavfilter/vf_colorspace.c +++ b/libavfilter/vf_colorspace.c @@ -348,11 +348,20 @@ static int convert(AVFilterContext *ctx, void *data, int job_nr, int n_jobs) */ s->yuv2rgb(rgb, s->rgb_stride, in_data, td->in_linesize, w, h, s->yuv2rgb_coeffs, s->yuv_offset[0]); + printf("post-yuv2rgb - R: %d, G: %d, B: %d\n", + rgb[0][0], rgb[1][0], rgb[2][0]); if (!s->rgb2rgb_passthrough) { apply_lut(rgb, s->rgb_stride, w, h, s->lin_lut); - if (!s->lrgb2lrgb_passthrough) + printf("post-linearize - R: %d, G: %d, B: %d\n", + rgb[0][0], rgb[1][0], rgb[2][0]); + if (!s->lrgb2lrgb_passthrough) { s->dsp.multiply3x3(rgb, s->rgb_stride, w, h, s->lrgb2lrgb_coeffs); + printf("post-rgb2rgb - R: %d, G: %d, B: %d\n", + rgb[0][0], rgb[1][0], rgb[2][0]); + } apply_lut(rgb, s->rgb_stride, w, h, s->delin_lut); + printf("post-delinearize - R: %d, G: %d, B: %d\n", + rgb[0][0], rgb[1][0], rgb[2][0]); } if (s->dither == DITHER_FSB) { s->rgb2yuv_fsb(out_data, td->out_linesize, rgb, s->rgb_stride, w, h, $ make -j4 && ./ffmpeg -s 2x2 -pix_fmt yuv420p -f rawvideo -c:v rawvideo -i /tmp/in.yuv -vf colorspace=ispace=bt470bg:iprimaries=bt470m:itrc=gamma28:irange=mpeg:range=jpeg:all=bt709 -y /tmp/out.yuv [..] post-yuv2rgb - R: 13076, G: 19616, B: 26625 post-linearize - R: 3182, G: 9906, B: 23302 post-rgb2rgb - R: -1193, G: 11028, B: 24440 post-delinearize - R: -4697, G: 17660, B: 26487 Patch looks OK. Drew, would it be possible to submit the patch at code.ffmpeg.org so I can merge it using our new system? Thanks, Ronald _______________________________________________ ffmpeg-devel mailing list -- ffmpeg-devel@ffmpeg.org To unsubscribe send an email to ffmpeg-devel-leave@ffmpeg.org ^ permalink raw reply [flat|nested] 4+ messages in thread
end of thread, other threads:[~2025-08-26 18:34 UTC | newest] Thread overview: 4+ messages (download: mbox.gz / follow: Atom feed) -- links below jump to the message on this page -- [not found] <347789> 2025-08-18 15:55 ` [FFmpeg-devel] [PATCH] vf_colorspace: Add an option to clamp trc LUT output Drew Dunne via ffmpeg-devel 2025-08-20 18:56 ` Ronald S. Bultje via ffmpeg-devel 2025-08-25 14:49 ` Drew Dunne via ffmpeg-devel 2025-08-26 18:33 ` [FFmpeg-devel] " Ronald S. Bultje via ffmpeg-devel
Git Inbox Mirror of the ffmpeg-devel mailing list - see https://ffmpeg.org/mailman/listinfo/ffmpeg-devel This inbox may be cloned and mirrored by anyone: git clone --mirror https://master.gitmailbox.com/ffmpegdev/0 ffmpegdev/git/0.git # If you have public-inbox 1.1+ installed, you may # initialize and index your mirror using the following commands: public-inbox-init -V2 ffmpegdev ffmpegdev/ https://master.gitmailbox.com/ffmpegdev \ ffmpegdev@gitmailbox.com public-inbox-index ffmpegdev Example config snippet for mirrors. AGPL code for this site: git clone https://public-inbox.org/public-inbox.git