[FFmpeg-devel] [PATCH] vf_colorspace: Add an option to clamp trc LUT output

2025-08-14 Thread Drew Dunne via ffmpeg-devel
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 
---
 libavfilter/vf_colorspace.c | 14 --
 1 file changed, 12 insertions(+), 2 deletions(-)

diff --git a/libavfilter/vf_colorspace.c b/libavfilter/vf_colorspace.c
index e1f4725f63..48aa267c07 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 clamplutoutput;
 
 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_int = av_clip_int16(lrint(d * 28672.0));
+s->delin_lut[n] =
+s->clamplutoutput ? FFMIN(FFMAX(0, d_int), 28672) : d_int; 
 
 // 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_int = av_clip_int16(lrint(l * 28672.0));
+s->lin_lut[n] =
+s->clamplutoutput ? FFMIN(FFMAX(0, l_int), 28672) : l_int; 
 }
 
 return 0;
@@ -1000,6 +1005,11 @@ static const AVOption colorspace_options[] = {
 ENUM("vonkries", WP_ADAPT_VON_KRIES, "wpadapt"),
 ENUM("identity", WP_ADAPT_IDENTITY, "wpadapt"),
 
+{ "clamplutoutput", 
+  "Clamps the linear and delinear LUT output values to the range [0, 1].",
+  OFFSET(clamplutoutput), 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.167.g924127e9c0-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-requ...@ffmpeg.org with subject "unsubscribe".


[FFmpeg-devel] [PATCH] vf_colorspace: Add an option to clamp trc LUT output

2025-08-18 Thread Drew Dunne via ffmpeg-devel
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 
---
 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-requ...@ffmpeg.org with subject "unsubscribe".


[FFmpeg-devel] [PATCH] vf_colorspace: Add an option to clamp trc LUT output

2025-08-19 Thread Drew Dunne via ffmpeg-devel
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 
---
 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.167.g924127e9c0-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-requ...@ffmpeg.org with subject "unsubscribe".


[FFmpeg-devel] [PATCH] vf_colorspace: Add an option to clamp trc LUT output

2025-08-19 Thread Drew Dunne via ffmpeg-devel
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.

Sorry about sending this multiple times, my comments outside my commit
message kept being removed from the patch for some reason. I was trying
to respond to the comment on confirming no additional time added.

I ran "time" with a command running just this filter on YUV frames and
confirmed that no significant time was adding with this patch. They both
averaged to the same time to the hundredth second.

Signed-off-by: Drew Dunne 
---
 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.167.g924127e9c0-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-requ...@ffmpeg.org with subject "unsubscribe".


[FFmpeg-devel] [PATCH] vf_colorspace: Add an option to clamp trc LUT output

2025-08-25 Thread Drew Dunne via ffmpeg-devel
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 
---
 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.00, 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-requ...@ffmpeg.org with subject "unsubscribe".


[FFmpeg-devel] Re: [PATCH] vf_colorspace: Add an option to clamp trc LUT output

2025-09-02 Thread Drew Dunne via ffmpeg-devel
Sure, I can submit it there. Is there documentation on how to submit a
patch there? Do I just submit it as a pull request?

On Tue, Aug 26, 2025 at 2:33 PM Ronald S. Bultje  wrote:

> 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.00, 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
> 000 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
>


-- 
Drew Dunne
asdu...@google.com
___
ffmpeg-devel mailing list -- ffmpeg-devel@ffmpeg.org
To unsubscribe send an email to ffmpeg-devel-le...@ffmpeg.org


[FFmpeg-devel] Re: [PATCH] vf_colorspace: Add an option to clamp trc LUT output

2025-09-04 Thread Drew Dunne via ffmpeg-devel
Ronald,

Thanks! Created a PR at https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/20438

- Drew

On Wed, Sep 3, 2025 at 8:31 AM Ronald S. Bultje  wrote:

> Hi Drew,
>
> I believe you create an account (if you don't already have one), fork the
> main FFmpeg repo (so you get a copy into your own personal account), push
> the patch into a branch on your personal repo, and submit a PR to merge
> that personal branch back into the main FFmpeg repo. It's similar to gitlab
> or github, if that helps. If emails feels a bit tardish, you can ask for
> help on IRC if you're not sure about any of these steps.
>
> Thanks!
> Ronald
>
> On Tue, Sep 2, 2025 at 3:49 PM Drew Dunne  wrote:
>
>> Sure, I can submit it there. Is there documentation on how to submit a
>> patch there? Do I just submit it as a pull request?
>>
>> On Tue, Aug 26, 2025 at 2:33 PM Ronald S. Bultje 
>> wrote:
>>
>>> 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.00, 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
>>> 000 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
>>>
>>
>>
>> --
>> Drew Dunne
>> asdu...@google.com
>>
>>

-- 
Drew Dunne
asdu...@google.com
___
ffmpeg-devel mailing list -- ffmpeg-devel@ffmpeg.org
To unsubscribe send an email to ffmpeg-devel-le...@ffmpeg.org


[FFmpeg-devel] [PATCH] vf_colorspace: Add an option to clamp trc LUT output (PR #20438)

2025-09-04 Thread Drew Dunne via ffmpeg-devel
PR #20438 opened by Drew Dunne (doctor3w)
URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/20438
Patch URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/20438.patch

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.

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.00, 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.


>From c288cff23a065fb2d3ff1ff999abc3b33c76296f Mon Sep 17 00:00:00 2001
From: Drew Dunne 
Date: Thu, 4 Sep 2025 17:01:56 +
Subject: [PATCH] vf_colorspace: Add an option to clamp trc LUT output

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.

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.00, 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.
---
 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 1b5665d36e..e212bd0970 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 valu