summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorNiklas Haas <git@nand.wakku.to>2016-05-03 21:58:03 +0200
committerwm4 <wm4@nowhere>2016-05-04 12:10:45 +0200
commit9054460bbaec267f7c08bf8115f50aba830bdc70 (patch)
tree8bcc99e886a481678d5ec77e7667a2a8d2807794
parent05c398fb6c3a48c6a72b6b0706ea0156c46b146d (diff)
downloadmpv-9054460bbaec267f7c08bf8115f50aba830bdc70.tar.bz2
mpv-9054460bbaec267f7c08bf8115f50aba830bdc70.tar.xz
lcms: improve black point handling (especially BT.1886)
First of all, black point compensation is now on by default. This is really rather harmless and only improves the result (where "improvement" means "less black clipping"). Second, this adds an option to limit the ICC profile's contrast, which helps for untagged matrix profiles that are implicitly black scaled even in colorimetric intent. (Note that this relies on BPC being enabled to work properly, which is why the two changes are tied together) Third, this uses the LittleCMS built in black point estimator instead of relying on the presence of accurate A2B tables. This also checks tags and does some amounts of noise elimination. If the option is unspecified and the profile is missing black point information, print a warning instructing the user to set the option, and fall back to 1000 otherwise.
-rw-r--r--DOCS/man/vo.rst7
-rw-r--r--video/out/opengl/lcms.c54
-rw-r--r--video/out/opengl/lcms.h1
3 files changed, 50 insertions, 12 deletions
diff --git a/DOCS/man/vo.rst b/DOCS/man/vo.rst
index bf3fb2e8fd..0e091591e2 100644
--- a/DOCS/man/vo.rst
+++ b/DOCS/man/vo.rst
@@ -951,6 +951,13 @@ Available video output drivers are:
Default is 128x256x64.
Sizes must be a power of two, and 512 at most.
+ ``icc-contrast=<0-100000>``
+ Specifies an upper limit on the target device's contrast ratio.
+ This is detected automatically from the profile if possible, but for
+ some profiles it might be missing, causing the contrast to be assumed
+ as infinite. As a result, video may appear darker than intended. This
+ only affects BT.1886 content. The default of 0 means no limit.
+
``blend-subtitles=<yes|video|no>``
Blend subtitles directly onto upscaled video frames, before
interpolation and/or color management (default: no). Enabling this
diff --git a/video/out/opengl/lcms.c b/video/out/opengl/lcms.c
index a2030d3792..20ebf6fd57 100644
--- a/video/out/opengl/lcms.c
+++ b/video/out/opengl/lcms.c
@@ -81,6 +81,7 @@ const struct m_sub_options mp_icc_conf = {
OPT_FLAG("icc-profile-auto", profile_auto, 0),
OPT_STRING("icc-cache-dir", cache_dir, 0),
OPT_INT("icc-intent", intent, 0),
+ OPT_INTRANGE("icc-contrast", contrast, 0, 0, 100000),
OPT_STRING_VALIDATE("3dlut-size", size_str, 0, validate_3dlut_size_opt),
OPT_REMOVED("icc-cache", "see icc-cache-dir"),
@@ -181,7 +182,8 @@ bool gl_lcms_has_changed(struct gl_lcms *p, enum mp_csp_prim prim,
return change;
}
-static cmsHPROFILE get_vid_profile(cmsContext cms, cmsHPROFILE disp_profile,
+static cmsHPROFILE get_vid_profile(struct gl_lcms *p, cmsContext cms,
+ cmsHPROFILE disp_profile,
enum mp_csp_prim prim, enum mp_csp_trc trc)
{
// The input profile for the transformation is dependent on the video
@@ -214,21 +216,47 @@ static cmsHPROFILE get_vid_profile(cmsContext cms, cmsHPROFILE disp_profile,
case MP_CSP_TRC_BT_1886: {
// To build an appropriate BT.1886 transformation we need access to
- // the display's black point, so we use the reverse mappings
+ // the display's black point, so we LittleCMS' detection function.
+ // Relative colorimetric is used since we want to approximate the
+ // BT.1886 to the target device's actual black point even in e.g.
+ // perceptual mode
+ const int intent = MP_INTENT_RELATIVE_COLORIMETRIC;
+ cmsCIEXYZ bp_XYZ;
+ if (!cmsDetectBlackPoint(&bp_XYZ, disp_profile, intent, 0))
+ return false;
+
+ // Map this XYZ value back into the (linear) source space
cmsToneCurve *linear = cmsBuildGamma(cms, 1.0);
cmsHPROFILE rev_profile = cmsCreateRGBProfileTHR(cms, &wp_xyY, &prim_xyY,
(cmsToneCurve*[3]){linear, linear, linear});
- cmsHTRANSFORM disp2src = cmsCreateTransformTHR(cms,
- disp_profile, TYPE_RGB_16, rev_profile, TYPE_RGB_DBL,
- INTENT_RELATIVE_COLORIMETRIC, 0);
+ cmsHPROFILE xyz_profile = cmsCreateXYZProfile();
+ cmsHTRANSFORM xyz2src = cmsCreateTransformTHR(cms,
+ xyz_profile, TYPE_XYZ_DBL, rev_profile, TYPE_RGB_DBL,
+ intent, 0);
cmsFreeToneCurve(linear);
cmsCloseProfile(rev_profile);
- if (!disp2src)
+ cmsCloseProfile(xyz_profile);
+ if (!xyz2src)
return false;
- uint64_t disp_black[3] = {0};
double src_black[3];
- cmsDoTransform(disp2src, disp_black, src_black, 1);
+ cmsDoTransform(xyz2src, &bp_XYZ, src_black, 1);
+ cmsDeleteTransform(xyz2src);
+
+ // Contrast limiting
+ if (p->opts.contrast > 0) {
+ for (int i = 0; i < 3; i++)
+ src_black[i] = MPMAX(src_black[i], 1.0 / p->opts.contrast);
+ }
+
+ // Built-in contrast failsafe
+ double contrast = 3.0 / (src_black[0] + src_black[1] + src_black[2]);
+ if (contrast > 100000) {
+ MP_WARN(p, "ICC profile detected contrast very high (>100000),"
+ " falling back to contrast 1000 for sanity. Set the"
+ " icc-contrast option to silence this warning.\n");
+ src_black[0] = src_black[1] = src_black[2] = 1.0 / 1000;
+ }
// Build the parametric BT.1886 transfer curve, one per channel
for (int i = 0; i < 3; i++) {
@@ -283,8 +311,9 @@ bool gl_lcms_get_lut3d(struct gl_lcms *p, struct lut3d **result_lut3d,
// because we may change the parameter in the future or make it
// customizable, same for the primaries.
char *cache_info = talloc_asprintf(tmp,
- "ver=1.3, intent=%d, size=%dx%dx%d, prim=%d, trc=%d\n",
- p->opts.intent, s_r, s_g, s_b, prim, trc);
+ "ver=1.3, intent=%d, size=%dx%dx%d, prim=%d, trc=%d, "
+ "contrast=%d\n",
+ p->opts.intent, s_r, s_g, s_b, prim, trc, p->opts.contrast);
uint8_t hash[32];
struct AVSHA *sha = av_sha_alloc();
@@ -328,7 +357,7 @@ bool gl_lcms_get_lut3d(struct gl_lcms *p, struct lut3d **result_lut3d,
if (!profile)
goto error_exit;
- cmsHPROFILE vid_profile = get_vid_profile(cms, profile, prim, trc);
+ cmsHPROFILE vid_profile = get_vid_profile(p, cms, profile, prim, trc);
if (!vid_profile) {
cmsCloseProfile(profile);
goto error_exit;
@@ -337,7 +366,8 @@ bool gl_lcms_get_lut3d(struct gl_lcms *p, struct lut3d **result_lut3d,
cmsHTRANSFORM trafo = cmsCreateTransformTHR(cms, vid_profile, TYPE_RGB_16,
profile, TYPE_RGB_16,
p->opts.intent,
- cmsFLAGS_HIGHRESPRECALC);
+ cmsFLAGS_HIGHRESPRECALC |
+ cmsFLAGS_BLACKPOINTCOMPENSATION);
cmsCloseProfile(profile);
cmsCloseProfile(vid_profile);
diff --git a/video/out/opengl/lcms.h b/video/out/opengl/lcms.h
index ee2a48b59c..4e0a18c3d2 100644
--- a/video/out/opengl/lcms.h
+++ b/video/out/opengl/lcms.h
@@ -13,6 +13,7 @@ struct mp_icc_opts {
char *cache_dir;
char *size_str;
int intent;
+ int contrast;
};
struct lut3d;