vo_gpu: add --tone-mapping-mode

This merges the old desaturation control options into a single
enumeration, with the goal of both simplifying how these options work
and also making this list more extensible (including, notably, new
options only supported by vo_gpu_next).

For the hybrid option, I decided to port the (slightly tweaked) values
from libplacebo's pre-refactor defaults, rather than the old values we
had in mpv, to more visually match the look of the vo_gpu_next hybrid.
This commit is contained in:
Niklas Haas 2022-01-04 01:50:00 +01:00 committed by Niklas Haas
parent f3fccfc395
commit d09c73c7b2
6 changed files with 80 additions and 40 deletions

View File

@ -36,6 +36,8 @@ Interface changes
- add `--tone-mapping` options `auto`, `spline` and `bt.2446a`
- add `--inverse-tone-mapping`
- add `--gamut-mapping-mode`, replacing `--gamut-clipping` and `--gamut-warning`
- add `--tone-mapping-mode`, replacing `--tone-mapping-desaturate` and
`--tone-mapping-desaturate-exponent`.
--- mpv 0.34.0 ---
- deprecate selecting by card number with `--drm-connector`, add
`--drm-device` which can be used instead

View File

@ -6385,6 +6385,28 @@ them.
it too high will make dark scenes appear unnaturally bright. (``-vo=gpu``
only)
``--tone-mapping-mode``
Controls how the tone mapping function is applied to colors.
auto
Choose the best mode automatically. (Default)
rgb
Tone-map per-channel (RGB). Has a tendency to severely distort colors,
desaturate highlights, and is generally not very recommended. However,
this is the mode used in many displays and TVs (especially early ones),
and so sometimes it's needed to reproduce the artistic intent a film
was mastered with.
max
Tone-map on the brightest component in the video. Has a tendency to
lead to weirdly oversaturated colors, and loss of dark details.
hybrid
A hybrid approach that uses linear tone-mapping for midtones and
per-channel tone mapping for highlights.
luma
Luminance-based method from ITU-R BT.2446a, including fixed gamut
reductions to account for brightness-related perceptual nonuniformity.
(``--vo=gpu-next`` only)
``--gamut-mapping-mode``
Specifies the algorithm used for reducing the gamut of images for the
target display, after any tone mapping is done.
@ -6445,27 +6467,6 @@ them.
aggressive, up to the limit of the high threshold (at which point the
filter becomes instant).
``--tone-mapping-desaturate=<0.0..1.0>``
Apply desaturation for highlights (default: 0.75). The parameter controls
the strength of the desaturation curve. A value of 0.0 completely disables
it, while a value of 1.0 means that overly bright colors will tend towards
white. This is not always the case, especially not for highlights that are
near primary colors. (``--vo=gpu`` only)
Values in between apply progressively more/less aggressive desaturation.
This setting helps prevent unnaturally oversaturated colors for
super-highlights, by (smoothly) turning them into less saturated (per
channel tone mapped) colors instead. This makes images feel more natural,
at the cost of chromatic distortions for out-of-range colors. The default
value of 0.75 provides a good balance. Setting this to 0.0 preserves the
chromatic accuracy of the tone mapping process.
``--tone-mapping-desaturate-exponent=<0.0..20.0>``
This setting controls the exponent of the desaturation curve, which
controls how bright a color needs to be in order to start being
desaturated. The default of 1.5 provides a reasonable balance. Decreasing
this exponent makes the curve more aggressive.
``--use-embedded-icc-profile``
Load the embedded ICC profile contained in media files such as PNG images.
(Default: yes). Note that this option only works when also using a display

View File

@ -329,8 +329,6 @@ static const struct gl_video_opts gl_video_opts_def = {
.decay_rate = 100.0,
.scene_threshold_low = 5.5,
.scene_threshold_high = 10.0,
.desat = 0.75,
.desat_exp = 1.5,
},
.early_flush = -1,
.hwdec_interop = "auto",
@ -394,9 +392,12 @@ const struct m_sub_options gl_video_conf = {
M_RANGE(0.0, 0.3)},
{"tone-mapping-max-boost", OPT_FLOAT(tone_map.max_boost),
M_RANGE(1.0, 10.0)},
{"tone-mapping-desaturate", OPT_FLOAT(tone_map.desat)},
{"tone-mapping-desaturate-exponent", OPT_FLOAT(tone_map.desat_exp),
M_RANGE(0.0, 20.0)},
{"tone-mapping-mode", OPT_CHOICE(tone_map.mode,
{"auto", TONE_MAP_MODE_AUTO},
{"rgb", TONE_MAP_MODE_RGB},
{"max", TONE_MAP_MODE_MAX},
{"hybrid", TONE_MAP_MODE_HYBRID},
{"luma", TONE_MAP_MODE_LUMA})},
{"gamut-mapping-mode", OPT_CHOICE(tone_map.gamut_mode,
{"auto", GAMUT_AUTO},
{"clip", GAMUT_CLIP},
@ -479,6 +480,8 @@ const struct m_sub_options gl_video_conf = {
"--linear-downscaling")},
{"gamut-warning", OPT_REMOVED("Replaced by --gamut-mapping-mode=warn")},
{"gamut-clipping", OPT_REMOVED("Replaced by --gamut-mapping-mode=desaturate")},
{"tone-mapping-desaturate", OPT_REMOVED("Replaced by --tone-mapping-mode")},
{"tone-mapping-desaturate-exponent", OPT_REMOVED("Replaced by --tone-mapping-mode")},
{0}
},
.size = sizeof(struct gl_video_opts),
@ -2644,6 +2647,18 @@ static void pass_colormanage(struct gl_video *p, struct mp_colorspace src,
break;
}
switch (p->opts.tone_map.mode) {
case TONE_MAP_MODE_AUTO:
case TONE_MAP_MODE_RGB:
case TONE_MAP_MODE_MAX:
case TONE_MAP_MODE_HYBRID:
break;
default:
MP_WARN(p, "Tone mapping mode unsupported by vo_gpu, falling back.\n");
p->opts.tone_map.mode = TONE_MAP_MODE_AUTO;
break;
}
switch (p->opts.tone_map.gamut_mode) {
case GAMUT_AUTO:
case GAMUT_WARN:

View File

@ -100,6 +100,14 @@ enum tone_mapping {
TONE_MAPPING_BT_2446A,
};
enum tone_mapping_mode {
TONE_MAP_MODE_AUTO,
TONE_MAP_MODE_RGB,
TONE_MAP_MODE_MAX,
TONE_MAP_MODE_HYBRID,
TONE_MAP_MODE_LUMA,
};
enum gamut_mode {
GAMUT_AUTO,
GAMUT_CLIP,
@ -114,12 +122,11 @@ struct gl_tone_map_opts {
float max_boost;
int inverse;
float crosstalk;
int mode;
int compute_peak;
float decay_rate;
float scene_threshold_low;
float scene_threshold_high;
float desat;
float desat_exp;
int gamut_mode;
};

View File

@ -809,18 +809,24 @@ static void pass_tone_map(struct gl_shader_cache *sc,
abort();
}
GLSL(vec3 sig_lin = color.rgb * (sig[sig_idx] / sig_orig);)
// Mix between the per-channel tone mapped and the linear tone mapped
// signal based on the desaturation strength
if (opts->desat > 0) {
float base = 0.18 * dst_scale;
GLSLF("float coeff = max(sig[sig_idx] - %f, 1e-6) / "
" max(sig[sig_idx], 1.0);\n", base);
GLSLF("coeff = %f * pow(coeff, %f);\n", opts->desat, opts->desat_exp);
GLSLF("color.rgb = mix(sig_lin, %f * sig, coeff);\n", dst_scale);
} else {
GLSL(color.rgb = sig_lin;)
switch (opts->mode) {
case TONE_MAP_MODE_RGB:
GLSL(color.rgb = sig;)
break;
case TONE_MAP_MODE_MAX:
GLSL(color.rgb *= sig[sig_idx] / sig_orig;)
break;
case TONE_MAP_MODE_AUTO:
case TONE_MAP_MODE_HYBRID:
GLSLF("float coeff = max(sig[sig_idx] - %f, 1e-6) / \n"
" max(sig[sig_idx], 1.0); \n"
"coeff = %f * pow(coeff / %f, %f); \n"
"color.rgb *= sig[sig_idx] / sig_orig; \n"
"color.rgb = mix(color.rgb, %f * sig, coeff); \n",
0.18 / dst_scale, 0.90, dst_scale, 0.20, dst_scale);
break;
default:
abort();
}
}

View File

@ -1406,12 +1406,21 @@ static void update_render_options(struct priv *p)
[GAMUT_DARKEN] = PL_GAMUT_DARKEN,
};
static const enum pl_tone_map_mode tone_map_modes[] = {
[TONE_MAP_MODE_AUTO] = PL_TONE_MAP_AUTO,
[TONE_MAP_MODE_RGB] = PL_TONE_MAP_RGB,
[TONE_MAP_MODE_MAX] = PL_TONE_MAP_MAX,
[TONE_MAP_MODE_HYBRID] = PL_TONE_MAP_HYBRID,
[TONE_MAP_MODE_LUMA] = PL_TONE_MAP_LUMA,
};
p->color_map = pl_color_map_default_params;
p->color_map.intent = opts->icc_opts->intent;
p->color_map.tone_mapping_function = tone_map_funs[opts->tone_map.curve];
p->color_map.tone_mapping_param = opts->tone_map.curve_param;
p->color_map.inverse_tone_mapping = opts->tone_map.inverse;
p->color_map.tone_mapping_crosstalk = opts->tone_map.crosstalk;
p->color_map.tone_mapping_mode = tone_map_modes[opts->tone_map.mode];
if (isnan(p->color_map.tone_mapping_param)) // vo_gpu compatibility
p->color_map.tone_mapping_param = 0.0;
if (opts->tone_map.gamut_mode != GAMUT_AUTO)