Git Inbox Mirror of the ffmpeg-devel mailing list - see https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
 help / color / mirror / Atom feed
From: Niklas Haas <ffmpeg@haasn.xyz>
To: ffmpeg-devel@ffmpeg.org
Cc: Niklas Haas <git@haasn.dev>
Subject: Re: [FFmpeg-devel] [PATCH 1/4] lavfi/vf_libplacebo: switch to new gamut mapping API
Date: Mon, 22 May 2023 10:35:43 +0200
Message-ID: <20230522103543.GF56755@haasn.xyz> (raw)
In-Reply-To: <20230521122438.78375-1-ffmpeg@haasn.xyz>

On Sun, 21 May 2023 14:24:35 +0200 Niklas Haas <ffmpeg@haasn.xyz> wrote:
> From: Niklas Haas <git@haasn.dev>
> 
> Upstream deprecated the old ad-hoc, enum/intent-based gamut mapping API
> and added a new API based on colorimetrically accurate gamut mapping
> functions.
> 
> The relevant change for us is the addition of several new modes, as well
> as deprecation of the old options. Update the documentation accordingly.
> ---
>  doc/filters.texi            | 47 ++++++++++----------
>  libavfilter/vf_libplacebo.c | 86 +++++++++++++++++++++++++++++--------
>  2 files changed, 92 insertions(+), 41 deletions(-)
> 
> diff --git a/doc/filters.texi b/doc/filters.texi
> index ddc8529bbe9..97023d5f2e8 100644
> --- a/doc/filters.texi
> +++ b/doc/filters.texi
> @@ -16353,37 +16353,36 @@ gamut-mapping when dealing with mismatches between wide-gamut or HDR content.
>  In general, libplacebo relies on accurate source tagging and mastering display
>  gamut information to produce the best results.
>  @table @option
> -@item intent
> -Rendering intent to use when adapting between different primary color gamuts
> -(after tone-mapping).
> -@table @samp
> -@item perceptual
> -Perceptual gamut mapping. Currently equivalent to relative colorimetric.
> -@item relative
> -Relative colorimetric. This is the default.
> -@item absolute
> -Absolute colorimetric.
> -@item saturation
> -Saturation mapping. Forcibly stretches the source gamut to the target gamut.
> -@end table
> -
>  @item gamut_mode
>  How to handle out-of-gamut colors that can occur as a result of colorimetric
>  gamut mapping.
>  @table @samp
>  @item clip
> -Do nothing, simply clip out-of-range colors to the RGB volume. This is the
> -default.
> -@item warn
> -Highlight out-of-gamut pixels (by coloring them pink).
> -@item darken
> -Linearly reduces content brightness to preserves saturated details, followed by
> -clipping the remaining out-of-gamut colors. As the name implies, this makes
> -everything darker, but provides a good balance between preserving details and
> -colors.
> +Do nothing, simply clip out-of-range colors to the RGB volume. Low quality but
> +extremely fast.
> +@item perceptual
> +Perceptually soft-clip colors to the gamut volume. This is the default.
> +@item relative
> +Relative colorimetric hard-clip. Similar to @code{perceptual} but without
> +the soft knee.
> +@item saturation
> +Saturation mapping, maps primaries directly to primaries in RGB space.
> +Not recommended except for artificial computer graphics for which a bright,
> +saturated display is desired.
> +@item absolute
> +Absolute colorimetric hard-clip. Performs no adjustment of the white point.
>  @item desaturate
>  Hard-desaturates out-of-gamut colors towards white, while preserving the
> -luminance. Has a tendency to shift colors.
> +luminance. Has a tendency to distort the visual appearance of bright objects.
> +@item darken
> +Linearly reduces content brightness to preserves saturated details, followed by
> +clipping the remaining out-of-gamut colors.
> +@item warn
> +Highlight out-of-gamut pixels (by inverting/marking them).
> +@item linear
> +Linearly reduces chromaticity of the entire image to make it fit within the
> +target color volume. Be careful when using this on BT.2020 sources without
> +proper mastering metadata, as doing so will lead to excessive desaturation.
>  @end table
>  
>  @item tonemapping
> diff --git a/libavfilter/vf_libplacebo.c b/libavfilter/vf_libplacebo.c
> index f26d0126beb..09bb3dfac86 100644
> --- a/libavfilter/vf_libplacebo.c
> +++ b/libavfilter/vf_libplacebo.c
> @@ -56,6 +56,19 @@ enum {
>      TONE_MAP_COUNT,
>  };
>  
> +enum {
> +    GAMUT_MAP_CLIP,
> +    GAMUT_MAP_PERCEPTUAL,
> +    GAMUT_MAP_RELATIVE,
> +    GAMUT_MAP_SATURATION,
> +    GAMUT_MAP_ABSOLUTE,
> +    GAMUT_MAP_DESATURATE,
> +    GAMUT_MAP_DARKEN,
> +    GAMUT_MAP_HIGHLIGHT,
> +    GAMUT_MAP_LINEAR,
> +    GAMUT_MAP_COUNT,
> +};
> +
>  static const char *const var_names[] = {
>      "in_w", "iw",   ///< width  of the input video frame
>      "in_h", "ih",   ///< height of the input video frame
> @@ -186,7 +199,6 @@ typedef struct LibplaceboContext {
>  
>      /* pl_color_map_params */
>      struct pl_color_map_params color_map_params;
> -    int intent;
>      int gamut_mode;
>      int tonemapping;
>      float tonemapping_param;
> @@ -202,6 +214,7 @@ typedef struct LibplaceboContext {
>      int gamut_warning;
>      int gamut_clipping;
>      int force_icc_lut;
> +    int intent;
>  #endif
>  
>      /* pl_dither_params */
> @@ -272,6 +285,34 @@ static const struct pl_tone_map_function *pl_get_tonemapping_func(int tm) {
>      }
>  }
>  
> +static void set_gamut_mode(struct pl_color_map_params *p, int gamut_mode)
> +{
> +    switch (gamut_mode) {
> +#if PL_API_VER >= 269
> +    case GAMUT_MAP_CLIP:       p->gamut_mapping = &pl_gamut_map_clip; return;
> +    case GAMUT_MAP_PERCEPTUAL: p->gamut_mapping = &pl_gamut_map_perceptual; return;
> +    case GAMUT_MAP_RELATIVE:   p->gamut_mapping = &pl_gamut_map_relative; return;
> +    case GAMUT_MAP_SATURATION: p->gamut_mapping = &pl_gamut_map_saturation; return;
> +    case GAMUT_MAP_ABSOLUTE:   p->gamut_mapping = &pl_gamut_map_absolute; return;
> +    case GAMUT_MAP_DESATURATE: p->gamut_mapping = &pl_gamut_map_desaturate; return;
> +    case GAMUT_MAP_DARKEN:     p->gamut_mapping = &pl_gamut_map_darken; return;
> +    case GAMUT_MAP_HIGHLIGHT:  p->gamut_mapping = &pl_gamut_map_highlight; return;
> +    case GAMUT_MAP_LINEAR:     p->gamut_mapping = &pl_gamut_map_linear; return;
> +#else
> +    case GAMUT_MAP_RELATIVE:   p->intent = PL_INTENT_RELATIVE_COLORIMETRIC; return;
> +    case GAMUT_MAP_SATURATION: p->intent = PL_INTENT_SATURATION; return;
> +    case GAMUT_MAP_ABSOLUTE:   p->intent = PL_INTENT_ABSOLUTE_COLORIMETRIC; return;
> +    case GAMUT_MAP_DESATURATE: p->gamut_mode = PL_GAMUT_DESATURATE; return;
> +    case GAMUT_MAP_DARKEN:     p->gamut_mode = PL_GAMUT_DARKEN; return;
> +    case GAMUT_MAP_HIGHLIGHT:  p->gamut_mode = PL_GAMUT_WARN; return;
> +    /* Use defaults for all other cases */
> +    default: return;
> +#endif
> +    }
> +
> +    av_assert0(0);
> +};
> +
>  static int parse_shader(AVFilterContext *avctx, const void *shader, size_t len)
>  {
>      LibplaceboContext *s = avctx->priv;
> @@ -317,7 +358,7 @@ static int update_settings(AVFilterContext *ctx)
>      int err = 0;
>      LibplaceboContext *s = ctx->priv;
>      enum pl_tone_map_mode tonemapping_mode = s->tonemapping_mode;
> -    enum pl_gamut_mode gamut_mode = s->gamut_mode;
> +    int gamut_mode = s->gamut_mode;
>      uint8_t color_rgba[4];
>  
>      RET(av_parse_color(color_rgba, s->fillcolor, -1, s));
> @@ -336,10 +377,16 @@ static int update_settings(AVFilterContext *ctx)
>          }
>      }
>  
> +    switch (s->intent) {
> +    case PL_INTENT_SATURATION:            gamut_mode = GAMUT_MAP_SATURATION; break;
> +    case PL_INTENT_RELATIVE_COLORIMETRIC: gamut_mode = GAMUT_MAP_RELATIVE; break;
> +    case PL_INTENT_ABSOLUTE_COLORIMETRIC: gamut_mode = GAMUT_MAP_ABSOLUTE; break;
> +    }
> +
>      if (s->gamut_warning)
> -        gamut_mode = PL_GAMUT_WARN;
> +        gamut_mode = GAMUT_MAP_HIGHLIGHT;
>      if (s->gamut_clipping)
> -        gamut_mode = PL_GAMUT_DESATURATE;
> +        gamut_mode = GAMUT_MAP_DESATURATE;
>  #endif
>  
>      s->deband_params = *pl_deband_params(
> @@ -366,8 +413,6 @@ static int update_settings(AVFilterContext *ctx)
>      );
>  
>      s->color_map_params = *pl_color_map_params(
> -        .intent = s->intent,
> -        .gamut_mode = gamut_mode,
>          .tone_mapping_function = pl_get_tonemapping_func(s->tonemapping),
>          .tone_mapping_param = s->tonemapping_param,
>          .tone_mapping_mode = tonemapping_mode,
> @@ -376,6 +421,8 @@ static int update_settings(AVFilterContext *ctx)
>          .lut_size = s->tonemapping_lut_size,
>      );
>  
> +    set_gamut_mode(&s->color_map_params, gamut_mode);
> +
>      s->dither_params = *pl_dither_params(
>          .method = s->dithering,
>          .lut_size = s->dither_lut_size,
> @@ -1143,16 +1190,16 @@ static const AVOption libplacebo_options[] = {
>      { "scene_threshold_high", "Scene change high threshold", OFFSET(scene_high), AV_OPT_TYPE_FLOAT, {.dbl = 10.0}, -1.0, 100.0, DYNAMIC },
>      { "overshoot", "Tone-mapping overshoot margin", OFFSET(overshoot), AV_OPT_TYPE_FLOAT, {.dbl = 0.05}, 0.0, 1.0, DYNAMIC },
>  
> -    { "intent", "Rendering intent", OFFSET(intent), AV_OPT_TYPE_INT, {.i64 = PL_INTENT_RELATIVE_COLORIMETRIC}, 0, 3, DYNAMIC, "intent" },
> -        { "perceptual", "Perceptual", 0, AV_OPT_TYPE_CONST, {.i64 = PL_INTENT_PERCEPTUAL}, 0, 0, STATIC, "intent" },
> -        { "relative", "Relative colorimetric", 0, AV_OPT_TYPE_CONST, {.i64 = PL_INTENT_RELATIVE_COLORIMETRIC}, 0, 0, STATIC, "intent" },
> -        { "absolute", "Absolute colorimetric", 0, AV_OPT_TYPE_CONST, {.i64 = PL_INTENT_ABSOLUTE_COLORIMETRIC}, 0, 0, STATIC, "intent" },
> -        { "saturation", "Saturation mapping", 0, AV_OPT_TYPE_CONST, {.i64 = PL_INTENT_SATURATION}, 0, 0, STATIC, "intent" },
> -    { "gamut_mode", "Gamut-mapping mode", OFFSET(gamut_mode), AV_OPT_TYPE_INT, {.i64 = PL_GAMUT_CLIP}, 0, PL_GAMUT_MODE_COUNT - 1, DYNAMIC, "gamut_mode" },
> -        { "clip", "Hard-clip gamut boundary", 0, AV_OPT_TYPE_CONST, {.i64 = PL_GAMUT_CLIP}, 0, 0, STATIC, "gamut_mode" },
> -        { "warn", "Highlight out-of-gamut colors", 0, AV_OPT_TYPE_CONST, {.i64 = PL_GAMUT_WARN}, 0, 0, STATIC, "gamut_mode" },
> -        { "darken", "Darken image to fit gamut", 0, AV_OPT_TYPE_CONST, {.i64 = PL_GAMUT_DARKEN}, 0, 0, STATIC, "gamut_mode" },
> -        { "desaturate", "Colorimetrically desaturate colors", 0, AV_OPT_TYPE_CONST, {.i64 = PL_GAMUT_DESATURATE}, 0, 0, STATIC, "gamut_mode" },
> +    { "gamut_mode", "Gamut-mapping mode", OFFSET(gamut_mode), AV_OPT_TYPE_INT, {.i64 = GAMUT_MAP_PERCEPTUAL}, 0, GAMUT_MAP_COUNT - 1, DYNAMIC, "gamut_mode" },
> +        { "clip", "Hard-clip (RGB per-channel)", 0, AV_OPT_TYPE_CONST, {.i64 = GAMUT_MAP_CLIP}, 0, 0, STATIC, "gamut_mode" },
> +        { "perceptual", "Colorimetric soft clipping", 0, AV_OPT_TYPE_CONST, {.i64 = GAMUT_MAP_PERCEPTUAL}, 0, 0, STATIC, "gamut_mode" },
> +        { "relative", "Relative colorimetric clipping", 0, AV_OPT_TYPE_CONST, {.i64 = GAMUT_MAP_RELATIVE}, 0, 0, STATIC, "gamut_mode" },
> +        { "saturation", "Saturation mapping (RGB -> RGB)", 0, AV_OPT_TYPE_CONST, {.i64 = GAMUT_MAP_SATURATION}, 0, 0, STATIC, "gamut_mode" },
> +        { "absolute", "Absolute colorimetric clipping", 0, AV_OPT_TYPE_CONST, {.i64 = GAMUT_MAP_ABSOLUTE}, 0, 0, STATIC, "gamut_mode" },
> +        { "desaturate", "Colorimetrically desaturate colors towards white", 0, AV_OPT_TYPE_CONST, {.i64 = GAMUT_MAP_DESATURATE}, 0, 0, STATIC, "gamut_mode" },
> +        { "darken", "Colorimetric clip with bias towards darkening image to fit gamut", 0, AV_OPT_TYPE_CONST, {.i64 = GAMUT_MAP_DARKEN}, 0, 0, STATIC, "gamut_mode" },
> +        { "warn", "Highlight out-of-gamut colors", 0, AV_OPT_TYPE_CONST, {.i64 = GAMUT_MAP_HIGHLIGHT}, 0, 0, STATIC, "gamut_mode" },
> +        { "linear", "Linearly reduce chromaticity to fit gamut", 0, AV_OPT_TYPE_CONST, {.i64 = GAMUT_MAP_LINEAR}, 0, 0, STATIC, "gamut_mode" },
>      { "tonemapping", "Tone-mapping algorithm", OFFSET(tonemapping), AV_OPT_TYPE_INT, {.i64 = TONE_MAP_AUTO}, 0, TONE_MAP_COUNT - 1, DYNAMIC, "tonemap" },
>          { "auto", "Automatic selection", 0, AV_OPT_TYPE_CONST, {.i64 = TONE_MAP_AUTO}, 0, 0, STATIC, "tonemap" },
>          { "clip", "No tone mapping (clip", 0, AV_OPT_TYPE_CONST, {.i64 = TONE_MAP_CLIP}, 0, 0, STATIC, "tonemap" },
> @@ -1184,7 +1231,12 @@ static const AVOption libplacebo_options[] = {
>      { "desaturation_strength", "Desaturation strength", OFFSET(desat_str), AV_OPT_TYPE_FLOAT, {.dbl = -1.0}, -1.0, 1.0, DYNAMIC | AV_OPT_FLAG_DEPRECATED },
>      { "desaturation_exponent", "Desaturation exponent", OFFSET(desat_exp), AV_OPT_TYPE_FLOAT, {.dbl = -1.0}, -1.0, 10.0, DYNAMIC | AV_OPT_FLAG_DEPRECATED },
>      { "gamut_warning", "Highlight out-of-gamut colors", OFFSET(gamut_warning), AV_OPT_TYPE_BOOL, {.i64 = 0}, 0, 1, DYNAMIC | AV_OPT_FLAG_DEPRECATED },
> -    { "gamut_clipping", "Enable colorimetric gamut clipping", OFFSET(gamut_clipping), AV_OPT_TYPE_BOOL, {.i64 = 0}, 0, 1, DYNAMIC | AV_OPT_FLAG_DEPRECATED },
> +    { "gamut_clipping", "Enable desaturating colorimetric gamut clipping", OFFSET(gamut_clipping), AV_OPT_TYPE_BOOL, {.i64 = 0}, 0, 1, DYNAMIC | AV_OPT_FLAG_DEPRECATED },
> +    { "intent", "Rendering intent", OFFSET(intent), AV_OPT_TYPE_INT, {.i64 = PL_INTENT_PERCEPTUAL}, 0, 3, DYNAMIC | AV_OPT_FLAG_DEPRECATED, "intent" },
> +        { "perceptual", "Perceptual", 0, AV_OPT_TYPE_CONST, {.i64 = PL_INTENT_PERCEPTUAL}, 0, 0, STATIC, "intent" },
> +        { "relative", "Relative colorimetric", 0, AV_OPT_TYPE_CONST, {.i64 = PL_INTENT_RELATIVE_COLORIMETRIC}, 0, 0, STATIC, "intent" },
> +        { "absolute", "Absolute colorimetric", 0, AV_OPT_TYPE_CONST, {.i64 = PL_INTENT_ABSOLUTE_COLORIMETRIC}, 0, 0, STATIC, "intent" },
> +        { "saturation", "Saturation mapping", 0, AV_OPT_TYPE_CONST, {.i64 = PL_INTENT_SATURATION}, 0, 0, STATIC, "intent" },
>  #endif
>  
>      { "dithering", "Dither method to use", OFFSET(dithering), AV_OPT_TYPE_INT, {.i64 = PL_DITHER_BLUE_NOISE}, -1, PL_DITHER_METHOD_COUNT - 1, DYNAMIC, "dither" },
> -- 
> 2.40.1
> 

Merged as d637f20f057586a0a3...877ccaf776c92866e
_______________________________________________
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".

      parent reply	other threads:[~2023-05-22  8:35 UTC|newest]

Thread overview: 5+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2023-05-21 12:24 Niklas Haas
2023-05-21 12:24 ` [FFmpeg-devel] [PATCH 2/4] lavfi/vf_libplacebo: update for new tone " Niklas Haas
2023-05-21 12:24 ` [FFmpeg-devel] [PATCH 3/4] lavfi/vf_libplacebo: update peak detection options Niklas Haas
2023-05-21 12:24 ` [FFmpeg-devel] [PATCH 4/4] lavfi/vf_libplacebo: don't intrude on pl_ namespace Niklas Haas
2023-05-22  8:35 ` Niklas Haas [this message]

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20230522103543.GF56755@haasn.xyz \
    --to=ffmpeg@haasn.xyz \
    --cc=ffmpeg-devel@ffmpeg.org \
    --cc=git@haasn.dev \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link

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