From 8035ff1ebf0482ad654cbbc9c8f6bca78dede106 Mon Sep 17 00:00:00 2001 From: facelessuser Date: Fri, 20 Jan 2023 13:31:17 -0700 Subject: [PATCH] Add proper handling of gamma and black level lift Set lb to 0 and lw to 1000 as these are the minimums for each range. Gamma will calculate to about 1.2, but it'll make no difference as the black level lift will be calculated as zero for us anyways due to lb = 0 If we end up changing lb at some point, gamma will have an actual impact --- coloraide/spaces/rec2100_hlg.py | 60 +++++++++++++++++++++++++++------ 1 file changed, 50 insertions(+), 10 deletions(-) diff --git a/coloraide/spaces/rec2100_hlg.py b/coloraide/spaces/rec2100_hlg.py index bb39549b6..7cf58e227 100644 --- a/coloraide/spaces/rec2100_hlg.py +++ b/coloraide/spaces/rec2100_hlg.py @@ -19,29 +19,68 @@ from .srgb import sRGB from .. import algebra as alg from ..types import Vector +import math -A = 0.17883277 -B = 0.28466892 # `1 - 4 * A` -C = 0.55991073 # `0.5 - A * alg.nlog(4 * A)` +# Sets 18% grey card to ~38% (37.7...) in order to set diffused white to 75%. SCALE = 0.26496256042100724 -INV_SCALE = 1 / SCALE -def hlg_oetf(values: Vector) -> Vector: +class Environment: + """Class to calculate and contain any required environmental data.""" + + def __init__( + self, + lw: float, + lb: float, + scale: float + ): + """Initialize environmental data.""" + + self.a = 0.17883277 + self.b = 0.28466892 # `1 - 4 * a` + self.c = 0.55991073 # `0.5 - a * alg.nlog(4 * a)` + self.beta = hlg_black_level_lift(lw, lb) + self.scale = scale + self.inv_scale = 1 / scale + + +def hlg_gamma(lw: float = 1000.0) -> float: + """ + Return the reference HLG system gamma. + + Ranges should be `lw >= 1000 cd / m^2`. + """ + + return 1.2 + 0.42 * math.log(lw / 1000.0) + + +def hlg_black_level_lift(lw: float = 0.0, lb: float = 1000.0) -> float: + """ + Return beta (the black level lift) using the nominal peak level luminance and display luminance for black. + + Ranges should be `lw >= 1000 cd / m^2` and `lb <= 0.005 cd / m^2`. + """ + + return math.sqrt(3 * (lb / lw) ** (1 / hlg_gamma(lw))) + + +def hlg_oetf(values: Vector, env: Environment) -> Vector: """HLG OETF.""" adjusted = [] # type: Vector for e in values: - adjusted.append(alg.nth_root(3 * e, 2) if e <= 1 / 12 else A * alg.nlog(12 * e - B) + C) + e = alg.nth_root(3 * e, 2) if e <= 1 / 12 else env.a * alg.nlog(12 * e - env.b) + env.c + adjusted.append((e - env.beta) / (1 - env.beta)) return adjusted -def hlg_eotf(values: Vector) -> Vector: +def hlg_eotf(values: Vector, env: Environment) -> Vector: """HLG EOTF.""" adjusted = [] # type: Vector for e in values: - adjusted.append((e ** 2) / 3 if e <= 0.5 else (alg.nexp((e - C) / A) + B) / 12) + e = (1 - env.beta) * e + env.beta + adjusted.append((e ** 2) / 3 if e <= 0.5 else (alg.nexp((e - env.c) / env.a) + env.b) / 12) return adjusted @@ -53,13 +92,14 @@ class Rec2100HLG(sRGB): SERIALIZE = ('--rec2100-hlg',) WHITE = WHITES['2deg']['D65'] DYNAMIC_RANGE = 'hdr' + ENV = Environment(1000, 0, SCALE) def to_base(self, coords: Vector) -> Vector: """To base from Rec 2100 HLG.""" - return alg.multiply(hlg_eotf(coords), INV_SCALE, dims=alg.D1_SC) + return [c * self.ENV.inv_scale for c in hlg_eotf(coords, self.ENV)] def from_base(self, coords: Vector) -> Vector: """From base to Rec. 2100 HLG.""" - return hlg_oetf(alg.multiply(coords, SCALE, dims=alg.D1_SC)) + return hlg_oetf([c * self.ENV.scale for c in coords], self.ENV)