[FFmpeg-devel] [PATCH 09/17] swscale: add ICC intent enum and option

Niklas Haas ffmpeg at haasn.xyz
Thu Dec 5 13:30:18 EET 2024


From: Niklas Haas <git at haasn.dev>

This setting can be used to infuence the type of tone and gamut mapping used
internally when color space conversions are required. As discussed at VDD'24,
the default was set to relative colorimetric clipping, which is approximately
associative, surjective and idempotent. As such, it roundtrips well, although
it is strictly speaking not associative on out-of-gamut colors.
---
 doc/APIchanges       |  3 +++
 doc/filters.texi     | 30 ++++++++++++++++++++++++++++++
 libswscale/graph.c   | 11 ++++++-----
 libswscale/options.c |  6 ++++++
 libswscale/swscale.h | 13 +++++++++++++
 libswscale/version.h |  2 +-
 6 files changed, 59 insertions(+), 6 deletions(-)

diff --git a/doc/APIchanges b/doc/APIchanges
index b9af3de933..2f1101fc92 100644
--- a/doc/APIchanges
+++ b/doc/APIchanges
@@ -2,6 +2,9 @@ The last version increases of all libraries were on 2024-03-07
 
 API changes, most recent first:
 
+2024-12-xx - xxxxxxxxxx - lsws 8.13.100 - swscale.h
+  Add enum SwsIntent and SwsContext.intent.
+
 2024-12-05 - xxxxxxxxxx - lavu 59.49.100 - csp.h
   Add av_csp_itu_eotf() and av_csp_itu_eotf_inv().
 
diff --git a/doc/filters.texi b/doc/filters.texi
index 900baf2f45..cba1e9fc76 100644
--- a/doc/filters.texi
+++ b/doc/filters.texi
@@ -21071,7 +21071,37 @@ Set libswscale input parameters for scaling algorithms that need them. See
 complete documentation. If not explicitly specified the filter applies
 empty parameters.
 
+ at item intent
+Set the ICC rendering intent to use when transforming between different color
+spaces. It accepts the following values:
 
+ at table @samp
+ at item perceptual
+Use a perceptually guided tone and gamut mapping curve. The exact details of
+the mapping used may change at any time and should not be relied on as stable.
+This intent is recommended for final viewing of image/video content in typical
+viewing settings.
+
+ at item relative_colorimetric
+Statically clip out-of-gamut colors using a colorimetric clipping curve which
+attempts to find the colorimetrically least dissimilar in-gamut color. This
+intent performs white point adaptation and black point adaptation. This is
+the default. This intent is recommended wherever faithful color reproduction
+is of the utmost importance, even at the cost of clipping.
+
+ at item absolute_colorimetric
+Hard clip out-of-gamut colors with no attempt at white or black point
+reproduction. This intent will reproduce in-gamut colors 1:1 on the output
+display as they would appear on the reference display, assuming the output
+display is appropriately calibrated.
+
+ at item saturation
+Performs saturation mapping - that is, stretches the input color volume
+directly onto the output color volume, in non-linear fashion that preserves the
+original signal appearance as much as possible. This intent is recommended for
+signal content evaluation, as it will not lead to any clipping. It is roughly
+analogous to not performing any color mapping, although it still takes into
+account the mastering display primaries and any differences in encoding TRC.
 
 @item size, s
 Set the video size. For the syntax of this option, check the
diff --git a/libswscale/graph.c b/libswscale/graph.c
index 311e2d0287..34621c784b 100644
--- a/libswscale/graph.c
+++ b/libswscale/graph.c
@@ -584,15 +584,16 @@ void sws_graph_free(SwsGraph **pgraph)
 /* Tests only options relevant to SwsGraph */
 static int opts_equal(const SwsContext *c1, const SwsContext *c2)
 {
-    return c1->flags       == c2->flags       &&
-           c1->threads     == c2->threads     &&
-           c1->dither      == c2->dither      &&
-           c1->alpha_blend == c2->alpha_blend &&
-           c1->gamma_flag  == c2->gamma_flag  &&
+    return c1->flags         == c2->flags         &&
+           c1->threads       == c2->threads       &&
+           c1->dither        == c2->dither        &&
+           c1->alpha_blend   == c2->alpha_blend   &&
+           c1->gamma_flag    == c2->gamma_flag    &&
            c1->src_h_chr_pos == c2->src_h_chr_pos &&
            c1->src_v_chr_pos == c2->src_v_chr_pos &&
            c1->dst_h_chr_pos == c2->dst_h_chr_pos &&
            c1->dst_v_chr_pos == c2->dst_v_chr_pos &&
+           c1->intent        == c2->intent        &&
            !memcmp(c1->scaler_params, c2->scaler_params, sizeof(c1->scaler_params));
 
 }
diff --git a/libswscale/options.c b/libswscale/options.c
index 5eef26de06..08d369e620 100644
--- a/libswscale/options.c
+++ b/libswscale/options.c
@@ -84,6 +84,12 @@ static const AVOption swscale_options[] = {
     { "threads",         "number of threads",             OFFSET(threads),   AV_OPT_TYPE_INT,   {.i64 = 1 }, .flags = VE, .unit = "threads", .max = INT_MAX },
         { "auto",        "automatic selection",           0,                 AV_OPT_TYPE_CONST, {.i64 = 0 }, .flags = VE, .unit = "threads" },
 
+    { "intent",          "color mapping intent",        OFFSET(intent), AV_OPT_TYPE_INT,    { .i64 = SWS_INTENT_RELATIVE_COLORIMETRIC }, .flags = VE, .unit = "intent", .max = SWS_INTENT_NB - 1 },
+        { "perceptual",            "perceptual tone mapping",        0, AV_OPT_TYPE_CONST,  { .i64 = SWS_INTENT_PERCEPTUAL            }, .flags = VE, .unit = "intent" },
+        { "relative_colorimetric", "relative colorimetric clipping", 0, AV_OPT_TYPE_CONST,  { .i64 = SWS_INTENT_RELATIVE_COLORIMETRIC }, .flags = VE, .unit = "intent" },
+        { "saturation",            "saturation mapping",             0, AV_OPT_TYPE_CONST,  { .i64 = SWS_INTENT_SATURATION            }, .flags = VE, .unit = "intent" },
+        { "absolute_colorimetric", "absolute colorimetric clipping", 0, AV_OPT_TYPE_CONST,  { .i64 = SWS_INTENT_ABSOLUTE_COLORIMETRIC }, .flags = VE, .unit = "intent" },
+
     { NULL }
 };
 
diff --git a/libswscale/swscale.h b/libswscale/swscale.h
index fa3a0f01ab..10b52c34e2 100644
--- a/libswscale/swscale.h
+++ b/libswscale/swscale.h
@@ -162,6 +162,14 @@ typedef enum SwsFlags {
     SWS_ERROR_DIFFUSION = 1 << 23, ///< Set `SwsContext.dither` instead
 } SwsFlags;
 
+typedef enum SwsIntent {
+    SWS_INTENT_PERCEPTUAL = 0,            ///< Perceptual tone mapping
+    SWS_INTENT_RELATIVE_COLORIMETRIC = 1, ///< Relative colorimetric clipping
+    SWS_INTENT_SATURATION = 2,            ///< Saturation mapping
+    SWS_INTENT_ABSOLUTE_COLORIMETRIC = 3, ///< Absolute colorimetric clipping
+    SWS_INTENT_NB, ///< not part of the ABI
+} SwsIntent;
+
 /***********************************
  * Context creation and management *
  ***********************************/
@@ -226,6 +234,11 @@ typedef struct SwsContext {
     int dst_v_chr_pos; ///< Destination vertical chroma position
     int dst_h_chr_pos; ///< Destination horizontal chroma position
 
+    /**
+     * Desired ICC intent for color space conversions.
+     */
+    int intent;
+
     /* Remember to add new fields to graph.c:opts_equal() */
 } SwsContext;
 
diff --git a/libswscale/version.h b/libswscale/version.h
index 419721948a..f84fcc160d 100644
--- a/libswscale/version.h
+++ b/libswscale/version.h
@@ -28,7 +28,7 @@
 
 #include "version_major.h"
 
-#define LIBSWSCALE_VERSION_MINOR  12
+#define LIBSWSCALE_VERSION_MINOR  13
 #define LIBSWSCALE_VERSION_MICRO 100
 
 #define LIBSWSCALE_VERSION_INT  AV_VERSION_INT(LIBSWSCALE_VERSION_MAJOR, \
-- 
2.47.0



More information about the ffmpeg-devel mailing list