Exemple #1
0
    def test_from_range_1(self):
        """
        Tests :func:`colour.utilities.common.from_range_1` definition.
        """

        with domain_range_scale('Reference'):
            self.assertEqual(from_range_1(1), 1)

        with domain_range_scale('1'):
            self.assertEqual(from_range_1(1), 1)

        with domain_range_scale('100'):
            self.assertEqual(from_range_1(1), 100)

        with domain_range_scale('100'):
            self.assertEqual(from_range_1(1, np.pi), 1 * np.pi)
def oetf_reverse_ARIBSTDB67(E_p, r=0.5, constants=ARIBSTDB67_CONSTANTS):
    """
    Defines *ARIB STD-B67 (Hybrid Log-Gamma)* reverse opto-electrical transfer
    function (OETF / OECF).

    Parameters
    ----------
    E_p : numeric or array_like
        Non-linear signal :math:`E'`.
    r : numeric, optional
        Video level corresponding to reference white level.
    constants : Structure, optional
        *ARIB STD-B67 (Hybrid Log-Gamma)* constants.

    Returns
    -------
    numeric or ndarray
        Voltage :math:`E` normalised by the reference white level and
        proportional to the implicit light intensity that would be detected
        with a reference camera color channel R, G, B.

    Notes
    -----

    +------------+-----------------------+---------------+
    | **Domain** | **Scale - Reference** | **Scale - 1** |
    +============+=======================+===============+
    | ``E_p``    | [0, 1]                | [0, 1]        |
    +------------+-----------------------+---------------+

    +------------+-----------------------+---------------+
    | **Range**  | **Scale - Reference** | **Scale - 1** |
    +============+=======================+===============+
    | ``E``      | [0, 1]                | [0, 1]        |
    +------------+-----------------------+---------------+

    References
    ----------
    :cite:`AssociationofRadioIndustriesandBusinesses2015a`

    Examples
    --------
    >>> oetf_reverse_ARIBSTDB67(0.212132034355964)  # doctest: +ELLIPSIS
    0.1799999...
    """

    E_p = to_domain_1(E_p)

    a = constants.a
    b = constants.b
    c = constants.c

    with domain_range_scale('ignore'):
        E = np.where(
            E_p <= oetf_ARIBSTDB67(1),
            (E_p / r) ** 2,
            np.exp((E_p - c) / a) + b,
        )

    return as_float(from_range_1(E))
Exemple #3
0
def XYZ_to_xyY(
        XYZ,
        illuminant=ILLUMINANTS['CIE 1931 2 Degree Standard Observer']['D65']):
    """
    Converts from *CIE XYZ* tristimulus values to *CIE xyY* colourspace and
    reference *illuminant*.

    Parameters
    ----------
    XYZ : array_like
        *CIE XYZ* tristimulus values.
    illuminant : array_like, optional
        Reference *illuminant* chromaticity coordinates.

    Returns
    -------
    ndarray
        *CIE xyY* colourspace array.

    Notes
    -----

    +------------+-----------------------+---------------+
    | **Domain** | **Scale - Reference** | **Scale - 1** |
    +============+=======================+===============+
    | ``XYZ``    | [0, 1]                | [0, 1]        |
    +------------+-----------------------+---------------+

    +------------+-----------------------+---------------+
    | **Range**  | **Scale - Reference** | **Scale - 1** |
    +============+=======================+===============+
    | ``xyY``    | [0, 1]                | [0, 1]        |
    +------------+-----------------------+---------------+

    References
    ----------
    :cite:`Lindbloom2003e`, :cite:`Wikipedia2005`

    Examples
    --------
    >>> XYZ = np.array([0.20654008, 0.12197225, 0.05136952])
    >>> XYZ_to_xyY(XYZ)  # doctest: +ELLIPSIS
    array([ 0.5436955...,  0.3210794...,  0.1219722...])
    """

    XYZ = to_domain_1(XYZ)
    X, Y, Z = tsplit(XYZ)
    xy_w = as_float_array(illuminant)

    XYZ_n = np.zeros(XYZ.shape)
    XYZ_n[..., 0:2] = xy_w

    xyY = np.where(
        np.all(XYZ == 0, axis=-1)[..., np.newaxis],
        XYZ_n,
        tstack([X / (X + Y + Z), Y / (X + Y + Z),
                from_range_1(Y)]),
    )

    return xyY
Exemple #4
0
def uv_to_Luv(
        uv,
        illuminant=ILLUMINANTS['CIE 1931 2 Degree Standard Observer']['D65'],
        Y=1):
    """
    Returns the *CIE L\\*u\\*v\\** colourspace array from given :math:`uv^p`
    chromaticity coordinates by extending the array last dimension with given
    :math:`L` *Lightness*.

    Parameters
    ----------
    uv : array_like
        :math:`uv^p` chromaticity coordinates.
    illuminant : array_like, optional
        Reference *illuminant* *xy* chromaticity coordinates or *CIE xyY*
        colourspace array.
    Y : numeric, optional
        Optional :math:`Y` *luminance* value used to construct the intermediate
        *CIE XYZ* colourspace array, the default :math:`Y` *luminance* value is
        1.

    Returns
    -------
    ndarray
        *CIE L\\*u\\*v\\** colourspace array.

    Notes
    -----

    +----------------+-----------------------+-----------------+
    | **Range**      | **Scale - Reference** | **Scale - 1**   |
    +================+=======================+=================+
    | ``Luv``        | ``L`` : [0, 100]      | ``L`` : [0, 1]  |
    |                |                       |                 |
    |                | ``u`` : [-100, 100]   | ``u`` : [-1, 1] |
    |                |                       |                 |
    |                | ``v`` : [-100, 100]   | ``v`` : [-1, 1] |
    +----------------+-----------------------+-----------------+
    | ``illuminant`` | [0, 1]                | [0, 1]          |
    +----------------+-----------------------+-----------------+

    References
    ----------
    :cite:`CIETC1-482004j`

    Examples
    --------
    >>> uv = np.array([0.37720213, 0.50120264])
    >>> uv_to_Luv(uv)  # doctest: +ELLIPSIS
    array([ 100.        ,  233.1837603...,   42.7474385...])
    """

    u, v = tsplit(uv)
    Y = to_domain_1(Y)

    X = 9 * u / (4 * v)
    Z = (-5 * Y * v - 3 * u / 4 + 3) / v
    Y = np.full(u.shape, Y, DEFAULT_FLOAT_DTYPE)

    return XYZ_to_Luv(from_range_1(tstack([X, Y, Z])), illuminant)
Exemple #5
0
def log_encoding_ALEXALogC(x,
                           firmware='SUP 3.x',
                           method='Linear Scene Exposure Factor',
                           EI=800):
    """
    Defines the *ALEXA Log C* log encoding curve / opto-electronic transfer
    function.

    Parameters
    ----------
    x : numeric or array_like
        Linear data :math:`x`.
    firmware : unicode, optional
        **{'SUP 3.x', 'SUP 2.x'}**,
        Alexa firmware version.
    method : unicode, optional
        **{'Linear Scene Exposure Factor', 'Normalised Sensor Signal'}**,
        Conversion method.
    EI : int,  optional
        Ei.

    Returns
    -------
    numeric or ndarray
        *ALEXA Log C* encoded data :math:`t`.

    References
    ----------
    :cite:`ARRI2012a`

    Notes
    -----

    +------------+-----------------------+---------------+
    | **Domain** | **Scale - Reference** | **Scale - 1** |
    +============+=======================+===============+
    | ``x``      | [0, 1]                | [0, 1]        |
    +------------+-----------------------+---------------+

    +------------+-----------------------+---------------+
    | **Range**  | **Scale - Reference** | **Scale - 1** |
    +============+=======================+===============+
    | ``t``      | [0, 1]                | [0, 1]        |
    +------------+-----------------------+---------------+

    Examples
    --------
    >>> log_encoding_ALEXALogC(0.18)  # doctest: +ELLIPSIS
    0.3910068...
    """

    x = to_domain_1(x)

    cut, a, b, c, d, e, f, _e_cut_f = (
        ALEXA_LOG_C_CURVE_CONVERSION_DATA[firmware][method][EI])

    t = np.where(x > cut, c * np.log10(a * x + b) + d, e * x + f)

    return as_float(from_range_1(t))
Exemple #6
0
def log_decoding_ALEXALogC(t,
                           firmware='SUP 3.x',
                           method='Linear Scene Exposure Factor',
                           EI=800):
    """
    Defines the *ALEXA Log C* log decoding curve / electro-optical transfer
    function.

    Parameters
    ----------
    t : numeric or array_like
        *ALEXA Log C* encoded data :math:`t`.
    firmware : unicode, optional
        **{'SUP 3.x', 'SUP 2.x'}**,
        Alexa firmware version.
    method : unicode,  optional
        **{'Linear Scene Exposure Factor', 'Normalised Sensor Signal'}**,
        Conversion method.
    EI : int,  optional
        Ei.

    Returns
    -------
    numeric or ndarray
        Linear data :math:`x`.

    Notes
    -----

    +------------+-----------------------+---------------+
    | **Domain** | **Scale - Reference** | **Scale - 1** |
    +============+=======================+===============+
    | ``t``      | [0, 1]                | [0, 1]        |
    +------------+-----------------------+---------------+

    +------------+-----------------------+---------------+
    | **Range**  | **Scale - Reference** | **Scale - 1** |
    +============+=======================+===============+
    | ``x``      | [0, 1]                | [0, 1]        |
    +------------+-----------------------+---------------+

    References
    ----------
    :cite:`ARRI2012a`

    Examples
    --------
    >>> log_decoding_ALEXALogC(0.391006832034084)  # doctest: +ELLIPSIS
    0.18...
    """

    t = to_domain_1(t)

    cut, a, b, c, d, e, f, _e_cut_f = (
        ALEXA_LOG_C_CURVE_CONVERSION_DATA[firmware][method][EI])

    x = np.where(t > e * cut + f, (10 ** ((t - d) / c) - b) / a, (t - f) / e)

    return as_float(from_range_1(x))
def eotf_BT1886(V, L_B=0, L_W=1):
    """
    Defines *Recommendation ITU-R BT.1886* electro-optical transfer function
    (EOTF / EOCF).

    Parameters
    ----------
    V : numeric or array_like
        Input video signal level (normalised, black at :math:`V = 0`, to white
        at :math:`V = 1`. For content mastered per
        *Recommendation ITU-R BT.709*, 10-bit digital code values :math:`D` map
        into values of :math:`V` per the following equation:
        :math:`V = (D-64)/876`
    L_B : numeric, optional
        Screen luminance for black.
    L_W : numeric, optional
        Screen luminance for white.

    Returns
    -------
    numeric or ndarray
        Screen luminance in :math:`cd/m^2`.

    Notes
    -----

    +------------+-----------------------+---------------+
    | **Domain** | **Scale - Reference** | **Scale - 1** |
    +============+=======================+===============+
    | ``V``      | [0, 1]                | [0, 1]        |
    +------------+-----------------------+---------------+

    +------------+-----------------------+---------------+
    | **Range**  | **Scale - Reference** | **Scale - 1** |
    +============+=======================+===============+
    | ``L``      | [0, 1]                | [0, 1]        |
    +------------+-----------------------+---------------+

    References
    ----------
    :cite:`InternationalTelecommunicationUnion2011h`

    Examples
    --------
    >>> eotf_BT1886(0.409007728864150)  # doctest: +ELLIPSIS
    0.1169918...
    """

    V = to_domain_1(V)

    gamma = 2.40
    gamma_d = 1 / gamma

    n = L_W ** gamma_d - L_B ** gamma_d
    a = n ** gamma
    b = L_B ** gamma_d / n
    L = a * np.maximum(V + b, 0) ** gamma

    return from_range_1(L)
Exemple #8
0
def CMY_to_CMYK(CMY):
    """
    Converts from *CMY* colourspace to *CMYK* colourspace.

    Parameters
    ----------
    CMY : array_like
        *CMY* colourspace array.

    Returns
    -------
    ndarray
        *CMYK* array.

    Notes
    -----

    +------------+-----------------------+---------------+
    | **Domain** | **Scale - Reference** | **Scale - 1** |
    +============+=======================+===============+
    | ``CMY``    | [0, 1]                | [0, 1]        |
    +------------+-----------------------+---------------+

    +------------+-----------------------+---------------+
    | **Range**  | **Scale - Reference** | **Scale - 1** |
    +============+=======================+===============+
    | ``CMYK``   | [0, 1]                | [0, 1]        |
    +------------+-----------------------+---------------+

    References
    ----------
    :cite:`EasyRGBo`

    Examples
    --------
    >>> CMY = np.array([0.54379481, 0.96918929, 0.95908048])
    >>> CMY_to_CMYK(CMY)  # doctest: +ELLIPSIS
    array([ 0.        ,  0.9324630...,  0.9103045...,  0.5437948...])
    """

    C, M, Y = tsplit(to_domain_1(CMY))

    K = np.ones(C.shape)
    K = np.where(C < K, C, K)
    K = np.where(M < K, M, K)
    K = np.where(Y < K, Y, K)

    C = as_float_array((C - K) / (1 - K))
    M = as_float_array((M - K) / (1 - K))
    Y = as_float_array((Y - K) / (1 - K))

    C[np.asarray(K == 1)] = 0
    M[np.asarray(K == 1)] = 0
    Y[np.asarray(K == 1)] = 0

    CMYK = tstack([C, M, Y, K])

    return from_range_1(CMYK)
Exemple #9
0
def log_encoding_PivotedLog(x,
                            log_reference=445,
                            linear_reference=0.18,
                            negative_gamma=0.6,
                            density_per_code_value=0.002):
    """
    Defines the *Josh Pines* style *Pivoted Log* log encoding curve /
    opto-electronic transfer function.

    Parameters
    ----------
    x : numeric or array_like
        Linear data :math:`x`.
    log_reference : numeric or array_like
        Log reference.
    linear_reference : numeric or array_like
        Linear reference.
    negative_gamma : numeric or array_like
        Negative gamma.
    density_per_code_value : numeric or array_like
        Density per code value.

    Returns
    -------
    numeric or ndarray
        Non-linear data :math:`y`.

    Notes
    -----

    +------------+-----------------------+---------------+
    | **Domain** | **Scale - Reference** | **Scale - 1** |
    +============+=======================+===============+
    | ``x``      | [0, 1]                | [0, 1]        |
    +------------+-----------------------+---------------+

    +------------+-----------------------+---------------+
    | **Range**  | **Scale - Reference** | **Scale - 1** |
    +============+=======================+===============+
    | ``y``      | [0, 1]                | [0, 1]        |
    +------------+-----------------------+---------------+

    References
    ----------
    :cite:`SonyImageworks2012a`

    Examples
    --------
    >>> log_encoding_PivotedLog(0.18)  # doctest: +ELLIPSIS
    0.4349951...
    """

    x = to_domain_1(x)

    y = ((log_reference + np.log10(x / linear_reference) /
          (density_per_code_value / negative_gamma)) / 1023)

    return from_range_1(y)
Exemple #10
0
def log_decoding_PivotedLog(y,
                            log_reference=445,
                            linear_reference=0.18,
                            negative_gamma=0.6,
                            density_per_code_value=0.002):
    """
    Defines the *Josh Pines* style *Pivoted Log* log decoding curve /
    electro-optical transfer function.

    Parameters
    ----------
    y : numeric or array_like
        Non-linear data :math:`y`.
    log_reference : numeric or array_like
        Log reference.
    linear_reference : numeric or array_like
        Linear reference.
    negative_gamma : numeric or array_like
        Negative gamma.
    density_per_code_value : numeric or array_like
        Density per code value.

    Returns
    -------
    numeric or ndarray
        Linear data :math:`x`.

    Notes
    -----

    +------------+-----------------------+---------------+
    | **Domain** | **Scale - Reference** | **Scale - 1** |
    +============+=======================+===============+
    | ``y``      | [0, 1]                | [0, 1]        |
    +------------+-----------------------+---------------+

    +------------+-----------------------+---------------+
    | **Range**  | **Scale - Reference** | **Scale - 1** |
    +============+=======================+===============+
    | ``x``      | [0, 1]                | [0, 1]        |
    +------------+-----------------------+---------------+

    References
    ----------
    :cite:`SonyImageworks2012a`

    Examples
    --------
    >>> log_decoding_PivotedLog(0.434995112414467)  # doctest: +ELLIPSIS
    0.1...
    """

    y = to_domain_1(y)

    x = (10 ** ((y * 1023 - log_reference) *
                (density_per_code_value / negative_gamma)) * linear_reference)

    return from_range_1(x)
Exemple #11
0
def eotf_reverse_BT1886(L, L_B=0, L_W=1):
    """
    Defines *Recommendation ITU-R BT.1886* reverse electro-optical transfer
    function (EOTF / EOCF).

    Parameters
    ----------
    L : numeric or array_like
        Screen luminance in :math:`cd/m^2`.
    L_B : numeric, optional
        Screen luminance for black.
    L_W : numeric, optional
        Screen luminance for white.

    Returns
    -------
    numeric or ndarray
        Input video signal level (normalised, black at :math:`V = 0`, to white
        at :math:`V = 1`.

    Notes
    -----

    +------------+-----------------------+---------------+
    | **Domain** | **Scale - Reference** | **Scale - 1** |
    +============+=======================+===============+
    | ``L``      | [0, 1]                | [0, 1]        |
    +------------+-----------------------+---------------+

    +------------+-----------------------+---------------+
    | **Range**  | **Scale - Reference** | **Scale - 1** |
    +============+=======================+===============+
    | ``V``      | [0, 1]                | [0, 1]        |
    +------------+-----------------------+---------------+

    References
    ----------
    :cite:`InternationalTelecommunicationUnion2011h`

    Examples
    --------
    >>> eotf_reverse_BT1886(0.11699185725296059)  # doctest: +ELLIPSIS
    0.4090077...
    """

    L = to_domain_1(L)

    gamma = 2.40
    gamma_d = 1 / gamma

    n = L_W ** gamma_d - L_B ** gamma_d
    a = n ** gamma
    b = L_B ** gamma_d / n

    V = (L / a) ** gamma_d - b

    return from_range_1(V)
Exemple #12
0
def luminance_Fairchild2010(L_hdr, epsilon=1.836):
    """
    Computes *luminance* :math:`Y` of given *Lightness* :math:`L_{hdr}` using
    *Fairchild and Wyble (2010)* method according to *Michealis-Menten*
    kinetics.

    Parameters
    ----------
    L_hdr : array_like
        *Lightness* :math:`L_{hdr}`.
    epsilon : numeric or array_like, optional
        :math:`\\epsilon` exponent.

    Returns
    -------
    array_like
        *luminance* :math:`Y`.

    Warning
    -------
    The output range of that definition is non standard!

    Notes
    -----

    +------------+-----------------------+---------------+
    | **Domain** | **Scale - Reference** | **Scale - 1** |
    +============+=======================+===============+
    | ``L_hdr``  | [0, 100]              | [0, 1]        |
    +------------+-----------------------+---------------+

    +------------+-----------------------+---------------+
    | **Range**  | **Scale - Reference** | **Scale - 1** |
    +============+=======================+===============+
    | ``Y``      | [0, 1]                | [0, 1]        |
    +------------+-----------------------+---------------+

    References
    ----------
    :cite:`Fairchild2010`

    Examples
    --------
    >>> luminance_Fairchild2010(31.996390226262736, 1.836)
    ... # doctest: +ELLIPSIS
    0.1219722...
    """

    L_hdr = to_domain_100(L_hdr)

    Y = np.exp(
        np.log(
            substrate_concentration_MichealisMenten(L_hdr - 0.02, 100, 0.184 **
                                                    epsilon)) / epsilon)

    return from_range_1(Y)
Exemple #13
0
def eotf_reverse_DCDM(XYZ, out_int=False):
    """
    Defines the *DCDM* reverse electro-optical transfer function (EOTF / EOCF).

    Parameters
    ----------
    XYZ : numeric or array_like
        *CIE XYZ* tristimulus values.
    out_int : bool, optional
        Whether to return value as integer code value or float equivalent of a
        code value at a given bit depth.

    Returns
    -------
    numeric or ndarray
        Non-linear *CIE XYZ'* tristimulus values.

    Notes
    -----

    +----------------+-----------------------+---------------+
    | **Domain \\***  | **Scale - Reference** | **Scale - 1** |
    +================+=======================+===============+
    | ``XYZ``        | [0, 1]                | [0, 1]        |
    +----------------+-----------------------+---------------+

    +----------------+-----------------------+---------------+
    | **Range \\***   | **Scale - Reference** | **Scale - 1** |
    +================+=======================+===============+
    | ``XYZ_p``      | [0, 1]                | [0, 1]        |
    +----------------+-----------------------+---------------+

    -   \\* This definition has an output integer switch, thus the domain-range
        scale information is only given for the floating point mode.

    References
    ----------
    :cite:`DigitalCinemaInitiatives2007b`

    Examples
    --------
    >>> eotf_reverse_DCDM(0.18)  # doctest: +ELLIPSIS
    0.1128186...
    >>> eotf_reverse_DCDM(0.18, out_int=True)
    462
    """

    XYZ = to_domain_1(XYZ)

    XYZ_p = spow(XYZ / 52.37, 1 / 2.6)

    if out_int:
        return np.round(4095 * XYZ_p).astype(DEFAULT_INT_DTYPE)
    else:
        return from_range_1(XYZ_p)
Exemple #14
0
def eotf_DCDM(XYZ_p, in_int=False):
    """
    Defines the *DCDM* electro-optical transfer function (EOTF / EOCF).

    Parameters
    ----------
    XYZ_p : numeric or array_like
        Non-linear *CIE XYZ'* tristimulus values.
    in_int : bool, optional
        Whether to treat the input value as integer code value or float
        equivalent of a code value at a given bit depth.

    Returns
    -------
    numeric or ndarray
        *CIE XYZ* tristimulus values.

    Notes
    -----

    +----------------+-----------------------+---------------+
    | **Domain \\***  | **Scale - Reference** | **Scale - 1** |
    +================+=======================+===============+
    | ``XYZ_p``      | [0, 1]                | [0, 1]        |
    +----------------+-----------------------+---------------+

    +----------------+-----------------------+---------------+
    | **Range \\***   | **Scale - Reference** | **Scale - 1** |
    +================+=======================+===============+
    | ``XYZ``        | [0, 1]                | [0, 1]        |
    +----------------+-----------------------+---------------+

    -   \\* This definition has an input integer switch, thus the domain-range
        scale information is only given for the floating point mode.

    References
    ----------
    :cite:`DigitalCinemaInitiatives2007b`

    Examples
    --------
    >>> eotf_DCDM(0.11281860951766724)  # doctest: +ELLIPSIS
    0.18...
    >>> eotf_DCDM(462, in_int=True)  # doctest: +ELLIPSIS
    0.18...
    """

    XYZ_p = to_domain_1(XYZ_p)

    if in_int:
        XYZ_p = XYZ_p / 4095

    XYZ = 52.37 * spow(XYZ_p, 2.6)

    return from_range_1(XYZ)
Exemple #15
0
def oetf_ARIBSTDB67(E, r=0.5, constants=ARIBSTDB67_CONSTANTS):
    """
    Defines *ARIB STD-B67 (Hybrid Log-Gamma)* opto-electrical transfer
    function (OETF / OECF).

    Parameters
    ----------
    E : numeric or array_like
        Voltage normalised by the reference white level and proportional to
        the implicit light intensity that would be detected with a reference
        camera color channel R, G, B.
    r : numeric, optional
        Video level corresponding to reference white level.
    constants : Structure, optional
        *ARIB STD-B67 (Hybrid Log-Gamma)* constants.

    Returns
    -------
    numeric or ndarray
        Resulting non-linear signal :math:`E'`.

    Notes
    -----

    +------------+-----------------------+---------------+
    | **Domain** | **Scale - Reference** | **Scale - 1** |
    +============+=======================+===============+
    | ``E``      | [0, 1]                | [0, 1]        |
    +------------+-----------------------+---------------+

    +------------+-----------------------+---------------+
    | **Range**  | **Scale - Reference** | **Scale - 1** |
    +============+=======================+===============+
    | ``E_p``    | [0, 1]                | [0, 1]        |
    +------------+-----------------------+---------------+

    References
    ----------
    :cite:`AssociationofRadioIndustriesandBusinesses2015a`

    Examples
    --------
    >>> oetf_ARIBSTDB67(0.18)  # doctest: +ELLIPSIS
    0.2121320...
    """

    E = to_domain_1(E)

    a = constants.a
    b = constants.b
    c = constants.c

    E_p = np.where(E <= 1, r * np.sqrt(E), a * np.log(E - b) + c)

    return as_float(from_range_1(E_p))
Exemple #16
0
def log_encoding_FilmicPro6(t):
    """
    Defines the *FiLMiC Pro 6* log encoding curve / opto-electronic transfer
    function.

    Parameters
    ----------
    t : numeric or array_like
        Linear data :math:`t`.

    Returns
    -------
    numeric or ndarray
        Non-linear data :math:`y`.

    Notes
    -----

    +------------+-----------------------+---------------+
    | **Domain** | **Scale - Reference** | **Scale - 1** |
    +============+=======================+===============+
    | ``t``      | [0, 1]                | [0, 1]        |
    +------------+-----------------------+---------------+

    +------------+-----------------------+---------------+
    | **Range**  | **Scale - Reference** | **Scale - 1** |
    +============+=======================+===============+
    | ``y``      | [0, 1]                | [0, 1]        |
    +------------+-----------------------+---------------+

    -   The *FiLMiC Pro 6* log encoding curve / opto-electronic transfer
        function is only defined for domain (0, 1].

    References
    ----------
    :cite:`FiLMiCInc2017`

    Warnings
    --------
    The *FiLMiC Pro 6* log encoding curve / opto-electronic transfer function
    was fitted with poor precision and has :math:`Y=1.000000819999999` value
    for :math:`t=1`. It also has no linear segment near zero and will thus be
    undefined for :math:`t=0` when computing its logarithm.

    Examples
    --------
    >>> log_encoding_FilmicPro6(0.18)  # doctest: +ELLIPSIS
    0.6066345...
    """

    t = to_domain_1(t)

    y = 0.371 * (np.sqrt(t) + 0.28257 * np.log(t) + 1.69542)

    return from_range_1(y)
Exemple #17
0
def XYZ_to_IPT(XYZ):
    """
    Converts from *CIE XYZ* tristimulus values to *IPT* colourspace.

    Parameters
    ----------
    XYZ : array_like
        *CIE XYZ* tristimulus values.

    Returns
    -------
    ndarray
        *IPT* colourspace array.

    Notes
    -----

    +------------+-----------------------+-----------------+
    | **Domain** | **Scale - Reference** | **Scale - 1**   |
    +============+=======================+=================+
    | ``XYZ``    | [0, 1]                | [0, 1]          |
    +------------+-----------------------+-----------------+

    +------------+-----------------------+-----------------+
    | **Range**  | **Scale - Reference** | **Scale - 1**   |
    +============+=======================+=================+
    | ``IPT``    | ``I`` : [0, 1]        | ``I`` : [0, 1]  |
    |            |                       |                 |
    |            | ``P`` : [-1, 1]       | ``P`` : [-1, 1] |
    |            |                       |                 |
    |            | ``T`` : [-1, 1]       | ``T`` : [-1, 1] |
    +------------+-----------------------+-----------------+

    -   Input *CIE XYZ* tristimulus values needs to be adapted for
        *CIE Standard Illuminant D Series* *D65*.

    References
    ----------
    :cite:`Fairchild2013y`

    Examples
    --------
    >>> XYZ = np.array([0.20654008, 0.12197225, 0.05136952])
    >>> XYZ_to_IPT(XYZ)  # doctest: +ELLIPSIS
    array([ 0.3842619...,  0.3848730...,  0.1888683...])
    """

    XYZ = to_domain_1(XYZ)

    LMS = dot_vector(IPT_XYZ_TO_LMS_MATRIX, XYZ)
    LMS_prime = spow(LMS, 0.43)
    IPT = dot_vector(IPT_LMS_TO_IPT_MATRIX, LMS_prime)

    return from_range_1(IPT)
Exemple #18
0
def log_decoding_Log3G10(y, legacy_curve=False):
    """
    Defines the *Log3G10* log decoding curve / electro-optical transfer
    function.

    Parameters
    ----------
    y : numeric or array_like
        Non-linear data :math:`y`.
    legacy_curve : bool, optional
        Whether to use the v1 *Log3G10* log encoding curve. Default is *False*.

    Returns
    -------
    numeric or ndarray
        Linear data :math:`x`.

    Notes
    -----

    +------------+-----------------------+---------------+
    | **Domain** | **Scale - Reference** | **Scale - 1** |
    +============+=======================+===============+
    | ``y``      | [0, 1]                | [0, 1]        |
    +------------+-----------------------+---------------+

    +------------+-----------------------+---------------+
    | **Range**  | **Scale - Reference** | **Scale - 1** |
    +============+=======================+===============+
    | ``x``      | [0, 1]                | [0, 1]        |
    +------------+-----------------------+---------------+

    References
    ----------
    :cite:`Nattress2016a`

    Examples
    --------
    >>> log_decoding_Log3G10(1.0 / 3, legacy_curve=True)  # doctest: +ELLIPSIS
    0.1799994...
    >>> log_decoding_Log3G10(1.0)  # doctest: +ELLIPSIS
    184.3223476...
    """

    y = to_domain_1(y)

    if legacy_curve:
        x = (np.sign(y) * (10.0 ** (np.abs(y) / 0.222497) - 1) / 169.379333)
    else:
        x = (np.sign(y) * (10.0 **
                           (np.abs(y) / 0.224282) - 1) / 155.975327) - 0.01

    return from_range_1(x)
Exemple #19
0
def log_decoding_FilmicPro6(y):
    """
    Defines the *FiLMiC Pro 6* log decoding curve / electro-optical transfer
    function.

    Parameters
    ----------
    y : numeric or array_like
        Non-linear data :math:`y`.

    Returns
    -------
    numeric or ndarray
        Linear data :math:`t`.

    Notes
    -----

    +------------+-----------------------+---------------+
    | **Domain** | **Scale - Reference** | **Scale - 1** |
    +============+=======================+===============+
    | ``y``      | [0, 1]                | [0, 1]        |
    +------------+-----------------------+---------------+

    +------------+-----------------------+---------------+
    | **Range**  | **Scale - Reference** | **Scale - 1** |
    +============+=======================+===============+
    | ``t``      | [0, 1]                | [0, 1]        |
    +------------+-----------------------+---------------+

    -   The *FiLMiC Pro 6* log decoding curve / electro-optical transfer
        function is only defined for domain (0, 1].

    References
    ----------
    :cite:`FiLMiCInc2017`

    Warnings
    --------
    The *FiLMiC Pro 6* log encoding curve / opto-electronic transfer function
    has no inverse in :math:`R`, we thus use a *LUT* based inversion.

    Examples
    --------
    >>> log_decoding_FilmicPro6(0.6066345199247033)  # doctest: +ELLIPSIS
    0.1800000...
    """

    y = to_domain_1(y)

    t = _log_decoding_FilmicPro6_interpolator()(y)

    return from_range_1(t)
Exemple #20
0
def eotf_SMPTE240M(V_r):
    """
    Defines *SMPTE 240M* electro-optical transfer function (EOTF / EOCF).

    Parameters
    ----------
    V_r : numeric or array_like
        Video signal level :math:`V_r` driving the reference reproducer
        normalised to the system reference white.

    Returns
    -------
    numeric or ndarray
         Light output :math:`L_r` from the reference reproducer normalised to
         the system reference white.

    Notes
    -----

    +------------+-----------------------+---------------+
    | **Domain** | **Scale - Reference** | **Scale - 1** |
    +============+=======================+===============+
    | ``V_c``    | [0, 1]                | [0, 1]        |
    +------------+-----------------------+---------------+

    +------------+-----------------------+---------------+
    | **Range**  | **Scale - Reference** | **Scale - 1** |
    +============+=======================+===============+
    | ``L_c``    | [0, 1]                | [0, 1]        |
    +------------+-----------------------+---------------+

    References
    ----------
    :cite:`SocietyofMotionPictureandTelevisionEngineers1999b`

    Examples
    --------
    >>> eotf_SMPTE240M(0.402285796753870)  # doctest: +ELLIPSIS
    0.1...
    """

    V_r = to_domain_1(V_r)

    with domain_range_scale('ignore'):
        L_r = np.where(
            V_r < oetf_SMPTE240M(0.0228),
            V_r / 4,
            spow((V_r + 0.1115) / 1.1115, 1 / 0.45),
        )

    return as_float(from_range_1(L_r))
Exemple #21
0
def oetf_reverse_sRGB(V):
    """
    Defines the *sRGB* colourspace reverse opto-electronic transfer function
    (OETF / OECF).

    Parameters
    ----------
    V : numeric or array_like
        Electrical signal :math:`V`.

    Returns
    -------
    numeric or ndarray
        Corresponding *luminance* :math:`L` of the image.

    Notes
    -----

    +------------+-----------------------+---------------+
    | **Domain** | **Scale - Reference** | **Scale - 1** |
    +============+=======================+===============+
    | ``V``      | [0, 1]                | [0, 1]        |
    +------------+-----------------------+---------------+

    +------------+-----------------------+---------------+
    | **Range**  | **Scale - Reference** | **Scale - 1** |
    +============+=======================+===============+
    | ``L``      | [0, 1]                | [0, 1]        |
    +------------+-----------------------+---------------+

    References
    ----------
    :cite:`InternationalElectrotechnicalCommission1999a`,
    :cite:`InternationalTelecommunicationUnion2015i`

    Examples
    --------
    >>> oetf_reverse_sRGB(0.461356129500442)  # doctest: +ELLIPSIS
    0.1...
    """

    V = to_domain_1(V)

    with domain_range_scale('ignore'):
        L = np.where(
            V <= oetf_sRGB(0.0031308),
            V / 12.92,
            spow((V + 0.055) / 1.055, 2.4),
        )

    return as_float(from_range_1(L))
Exemple #22
0
def IPT_to_XYZ(IPT):
    """
    Converts from *IPT* colourspace to *CIE XYZ* tristimulus values.

    Parameters
    ----------
    IPT : array_like
        *IPT* colourspace array.

    Returns
    -------
    ndarray
        *CIE XYZ* tristimulus values.

    Notes
    -----

    +------------+-----------------------+-----------------+
    | **Domain** | **Scale - Reference** | **Scale - 1**   |
    +============+=======================+=================+
    | ``IPT``    | ``I`` : [0, 1]        | ``I`` : [0, 1]  |
    |            |                       |                 |
    |            | ``P`` : [-1, 1]       | ``P`` : [-1, 1] |
    |            |                       |                 |
    |            | ``T`` : [-1, 1]       | ``T`` : [-1, 1] |
    +------------+-----------------------+-----------------+

    +------------+-----------------------+-----------------+
    | **Range**  | **Scale - Reference** | **Scale - 1**   |
    +============+=======================+=================+
    | ``XYZ``    | [0, 1]                | [0, 1]          |
    +------------+-----------------------+-----------------+

    References
    ----------
    :cite:`Fairchild2013y`

    Examples
    --------
    >>> IPT = np.array([0.38426191, 0.38487306, 0.18886838])
    >>> IPT_to_XYZ(IPT)  # doctest: +ELLIPSIS
    array([ 0.2065400...,  0.1219722...,  0.0513695...])
    """

    IPT = to_domain_1(IPT)

    LMS = dot_vector(IPT_IPT_TO_LMS_MATRIX, IPT)
    LMS_prime = spow(LMS, 1 / 0.43)
    XYZ = dot_vector(IPT_LMS_TO_XYZ_MATRIX, LMS_prime)

    return from_range_1(XYZ)
Exemple #23
0
def xyY_to_XYZ(xyY):
    """
    Converts from *CIE xyY* colourspace to *CIE XYZ* tristimulus values.

    Parameters
    ----------
    xyY : array_like
        *CIE xyY* colourspace array.

    Returns
    -------
    ndarray
        *CIE XYZ* tristimulus values.

    Notes
    -----

    +------------+-----------------------+---------------+
    | **Domain** | **Scale - Reference** | **Scale - 1** |
    +============+=======================+===============+
    | ``xyY``    | [0, 1]                | [0, 1]        |
    +------------+-----------------------+---------------+

    +------------+-----------------------+---------------+
    | **Range**  | **Scale - Reference** | **Scale - 1** |
    +============+=======================+===============+
    | ``XYZ``    | [0, 1]                | [0, 1]        |
    +------------+-----------------------+---------------+

    References
    ----------
    :cite:`Lindbloom2009d`, :cite:`Wikipedia2005`

    Examples
    --------
    >>> xyY = np.array([0.54369557, 0.32107944, 0.12197225])
    >>> xyY_to_XYZ(xyY)  # doctest: +ELLIPSIS
    array([ 0.2065400...,  0.1219722...,  0.0513695...])
    """

    x, y, Y = tsplit(xyY)
    Y = to_domain_1(Y)

    XYZ = np.where(
        (y == 0)[..., np.newaxis],
        tstack([y, y, y]),
        tstack([x * Y / y, Y, (1 - x - y) * Y / y]),
    )

    return from_range_1(XYZ)
Exemple #24
0
def HEX_to_RGB(HEX):
    """
    Converts from hexadecimal triplet representation to *RGB* colourspace.

    Parameters
    ----------
    HEX : unicode or array_like
        Hexadecimal triplet representation.

    Returns
    -------
    ndarray
        *RGB* colourspace array.

    Notes
    -----

    Notes
    -----

    +-----------+-----------------------+---------------+
    | **Range** | **Scale - Reference** | **Scale - 1** |
    +===========+=======================+===============+
    | ``RGB``   | [0, 1]                | [0, 1]        |
    +-----------+-----------------------+---------------+

    Examples
    --------
    >>> HEX = '#aaddff'
    >>> HEX_to_RGB(HEX)  # doctest: +ELLIPSIS
    array([ 0.6666666...,  0.8666666...,  1.        ])
    """

    HEX = np.core.defchararray.lstrip(HEX, '#')

    def to_RGB(x):
        """
        Converts given hexadecimal representation to *RGB*.
        """

        l_x = len(x)
        return [int(x[i:i + l_x // 3], 16) for i in range(0, l_x, l_x // 3)]

    to_RGB_v = np.vectorize(to_RGB, otypes=[np.ndarray])

    RGB = np.asarray(to_RGB_v(HEX).tolist()) / 255

    return from_range_1(RGB)
Exemple #25
0
def log_decoding_Cineon(y, black_offset=10 ** ((95 - 685) / 300)):
    """
    Defines the *Cineon* log decoding curve / electro-optical transfer
    function.

    Parameters
    ----------
    y : numeric or array_like
        Non-linear data :math:`y`.
    black_offset : numeric or array_like
        Black offset.

    Returns
    -------
    numeric or ndarray
        Linear data :math:`x`.

    Notes
    -----

    +------------+-----------------------+---------------+
    | **Domain** | **Scale - Reference** | **Scale - 1** |
    +============+=======================+===============+
    | ``y``      | [0, 1]                | [0, 1]        |
    +------------+-----------------------+---------------+

    +------------+-----------------------+---------------+
    | **Range**  | **Scale - Reference** | **Scale - 1** |
    +============+=======================+===============+
    | ``x``      | [0, 1]                | [0, 1]        |
    +------------+-----------------------+---------------+

    References
    ----------
    :cite:`SonyImageworks2012a`

    Examples
    --------
    >>> log_decoding_Cineon(0.457319613085418)  # doctest: +ELLIPSIS
    0.1799999...
    """

    y = to_domain_1(y)

    x = ((10 ** ((1023 * y - 685) / 300) - black_offset) / (1 - black_offset))

    return from_range_1(x)
Exemple #26
0
def log_encoding_REDLog(x, black_offset=10 ** ((0 - 1023) / 511)):
    """
    Defines the *REDLog* log encoding curve / opto-electronic transfer
    function.

    Parameters
    ----------
    x : numeric or array_like
        Linear data :math:`x`.
    black_offset : numeric or array_like
        Black offset.

    Returns
    -------
    numeric or ndarray
        Non-linear data :math:`y`.

    Notes
    -----

    +------------+-----------------------+---------------+
    | **Domain** | **Scale - Reference** | **Scale - 1** |
    +============+=======================+===============+
    | ``x``      | [0, 1]                | [0, 1]        |
    +------------+-----------------------+---------------+

    +------------+-----------------------+---------------+
    | **Range**  | **Scale - Reference** | **Scale - 1** |
    +============+=======================+===============+
    | ``y``      | [0, 1]                | [0, 1]        |
    +------------+-----------------------+---------------+

    References
    ----------
    :cite:`SonyImageworks2012a`

    Examples
    --------
    >>> log_encoding_REDLog(0.18)  # doctest: +ELLIPSIS
    0.6376218...
    """

    x = to_domain_1(x)

    y = (1023 + 511 * np.log10(x * (1 - black_offset) + black_offset)) / 1023

    return from_range_1(y)
Exemple #27
0
def log_decoding_REDLog(y, black_offset=10 ** ((0 - 1023) / 511)):
    """
    Defines the *REDLog* log decoding curve / electro-optical transfer
    function.

    Parameters
    ----------
    y : numeric or array_like
        Non-linear data :math:`y`.
    black_offset : numeric or array_like
        Black offset.

    Returns
    -------
    numeric or ndarray
        Linear data :math:`x`.

    Notes
    -----

    +------------+-----------------------+---------------+
    | **Domain** | **Scale - Reference** | **Scale - 1** |
    +============+=======================+===============+
    | ``y``      | [0, 1]                | [0, 1]        |
    +------------+-----------------------+---------------+

    +------------+-----------------------+---------------+
    | **Range**  | **Scale - Reference** | **Scale - 1** |
    +============+=======================+===============+
    | ``x``      | [0, 1]                | [0, 1]        |
    +------------+-----------------------+---------------+

    References
    ----------
    :cite:`SonyImageworks2012a`

    Examples
    --------
    >>> log_decoding_REDLog(0.637621845988175)  # doctest: +ELLIPSIS
    0.1...
    """

    y = to_domain_1(y)

    x = ((10 ** ((1023 * y - 1023) / 511)) - black_offset) / (1 - black_offset)

    return from_range_1(x)
Exemple #28
0
def oetf_sRGB(L):
    """
    Defines the *sRGB* colourspace opto-electronic transfer function
    (OETF / OECF).

    Parameters
    ----------
    L : numeric or array_like
        *Luminance* :math:`L` of the image.

    Returns
    -------
    numeric or ndarray
        Corresponding electrical signal :math:`V`.

    Notes
    -----

    +------------+-----------------------+---------------+
    | **Domain** | **Scale - Reference** | **Scale - 1** |
    +============+=======================+===============+
    | ``L``      | [0, 1]                | [0, 1]        |
    +------------+-----------------------+---------------+

    +------------+-----------------------+---------------+
    | **Range**  | **Scale - Reference** | **Scale - 1** |
    +============+=======================+===============+
    | ``V``      | [0, 1]                | [0, 1]        |
    +------------+-----------------------+---------------+

    References
    ----------
    :cite:`InternationalElectrotechnicalCommission1999a`,
    :cite:`InternationalTelecommunicationUnion2015i`

    Examples
    --------
    >>> oetf_sRGB(0.18)  # doctest: +ELLIPSIS
    0.4613561...
    """

    L = to_domain_1(L)

    V = np.where(L <= 0.0031308, L * 12.92, 1.055 * spow(L, 1 / 2.4) - 0.055)

    return as_float(from_range_1(V))
Exemple #29
0
def oetf_SMPTE240M(L_c):
    """
    Defines *SMPTE 240M* opto-electrical transfer function (OETF / OECF).

    Parameters
    ----------
    L_c : numeric or array_like
        Light input :math:`L_c` to the reference camera normalised to the
        system reference white.

    Returns
    -------
    numeric or ndarray
        Video signal output :math:`V_c` of the reference camera normalised to
        the system reference white.

    Notes
    -----

    +------------+-----------------------+---------------+
    | **Domain** | **Scale - Reference** | **Scale - 1** |
    +============+=======================+===============+
    | ``L_c``    | [0, 1]                | [0, 1]        |
    +------------+-----------------------+---------------+

    +------------+-----------------------+---------------+
    | **Range**  | **Scale - Reference** | **Scale - 1** |
    +============+=======================+===============+
    | ``V_c``    | [0, 1]                | [0, 1]        |
    +------------+-----------------------+---------------+

    References
    ----------
    :cite:`SocietyofMotionPictureandTelevisionEngineers1999b`

    Examples
    --------
    >>> oetf_SMPTE240M(0.18)  # doctest: +ELLIPSIS
    0.4022857...
    """

    L_c = to_domain_1(L_c)

    V_c = np.where(L_c < 0.0228, 4 * L_c, 1.1115 * spow(L_c, 0.45) - 0.1115)

    return as_float(from_range_1(V_c))
Exemple #30
0
def log_decoding_Log3G12(y):
    """
    Defines the *Log3G12* log decoding curve / electro-optical transfer
    function.

    Parameters
    ----------
    y : numeric or array_like
        Non-linear data :math:`y`.

    Returns
    -------
    numeric or ndarray
        Linear data :math:`x`.

    Notes
    -----

    +------------+-----------------------+---------------+
    | **Domain** | **Scale - Reference** | **Scale - 1** |
    +============+=======================+===============+
    | ``y``      | [0, 1]                | [0, 1]        |
    +------------+-----------------------+---------------+

    +------------+-----------------------+---------------+
    | **Range**  | **Scale - Reference** | **Scale - 1** |
    +============+=======================+===============+
    | ``x``      | [0, 1]                | [0, 1]        |
    +------------+-----------------------+---------------+

    References
    ----------
    :cite:`Nattress2016a`

    Examples
    --------
    >>> log_decoding_Log3G12(1.0 / 3)  # doctest: +ELLIPSIS
    0.1800015...
    """

    y = to_domain_1(y)

    x = np.sign(y) * (10.0 ** (np.abs(y) / 0.184904) - 1) / 347.189667

    return from_range_1(x)
Exemple #31
0
def uv_to_UCS(uv, V=1):
    """
    Returns the *CIE 1960 UCS* colourspace array from given *uv* chromaticity
    coordinates.

    Parameters
    ----------
    uv : array_like
        *uv* chromaticity coordinates.
    V : numeric, optional
        Optional :math:`V` *luminance* value used to construct the
        *CIE 1960 UCS* colourspace array, the default :math:`V` *luminance* is
        set to 1.

    Returns
    -------
    ndarray
        *CIE 1960 UCS* colourspace array.

    References
    ----------
    :cite:`Wikipedia2008c`

    Examples
    --------
    >>> uv = np.array([0.37720213, 0.33413508])
    >>> uv_to_UCS(uv)  # doctest: +ELLIPSIS
    array([ 1.1288911...,  1.        ,  0.8639104...])
    """

    u, v = tsplit(uv)
    V = np.full(u.shape, V, DEFAULT_FLOAT_DTYPE)

    U = V * u / v
    W = -V * (u + v - 1) / v

    UVW = tstack([U, V, W])

    return from_range_1(UVW)
Exemple #32
0
def uv_to_UCS(uv: ArrayLike, V: Floating = 1) -> NDArray:
    """
    Return the *CIE 1960 UCS* colourspace array from given *uv* chromaticity
    coordinates.

    Parameters
    ----------
    uv
        *uv* chromaticity coordinates.
    V
        Optional :math:`V` *luminance* value used to construct the
        *CIE 1960 UCS* colourspace array, the default :math:`V` *luminance* is
        set to 1.

    Returns
    -------
    :class:`numpy.ndarray`
        *CIE 1960 UCS* colourspace array.

    References
    ----------
    :cite:`Wikipedia2008c`

    Examples
    --------
    >>> import numpy as np
    >>> uv = np.array([0.37720213, 0.33413508])
    >>> uv_to_UCS(uv)  # doctest: +ELLIPSIS
    array([ 1.1288911...,  1.        ,  0.8639104...])
    """

    u, v = tsplit(uv)
    V = as_float_scalar(to_domain_1(V))

    UVW = tstack([V * u / v, full(u.shape, V), -V * (u + v - 1) / v])

    return from_range_1(UVW)
Exemple #33
0
def log_decoding_SLog(y,
                      bit_depth=10,
                      in_normalised_code_value=True,
                      out_reflection=True,
                      **kwargs):
    """
    Defines the *Sony S-Log* log decoding curve / electro-optical transfer
    function.

    Parameters
    ----------
    y : numeric or array_like
        Non-linear *Sony S-Log* data :math:`y`.
    bit_depth : int, optional
        Bit depth used for conversion.
    in_normalised_code_value : bool, optional
        Whether the non-linear *Sony S-Log* data :math:`y` is encoded as
        normalised code values.
    out_reflection : bool, optional
        Whether the light level :math:`x` to a camera is reflection.

    Other Parameters
    ----------------
    \\**kwargs : dict, optional
        Keywords arguments for deprecation management.

    Returns
    -------
    numeric or ndarray
        Reflection or :math:`IRE / 100` input light level :math:`x` to a
        camera.

    Notes
    -----

    +------------+-----------------------+---------------+
    | **Domain** | **Scale - Reference** | **Scale - 1** |
    +============+=======================+===============+
    | ``y``      | [0, 1]                | [0, 1]        |
    +------------+-----------------------+---------------+

    +------------+-----------------------+---------------+
    | **Range**  | **Scale - Reference** | **Scale - 1** |
    +============+=======================+===============+
    | ``x``      | [0, 1]                | [0, 1]        |
    +------------+-----------------------+---------------+

    References
    ----------
    :cite:`SonyCorporation2012a`

    Examples
    --------
    >>> log_decoding_SLog(0.384970815928670)  # doctest: +ELLIPSIS
    0.1...
    """

    in_normalised_code_value = handle_arguments_deprecation(
        {
            'ArgumentRenamed': [['in_legal', 'in_normalised_code_value']],
        }, **kwargs).get('in_normalised_code_value', in_normalised_code_value)

    y = to_domain_1(y)

    x = legal_to_full(y, bit_depth) if in_normalised_code_value else y

    with domain_range_scale('ignore'):
        x = np.where(
            y >= log_encoding_SLog(0.0, bit_depth, in_normalised_code_value),
            10**((x - 0.616596 - 0.03) / 0.432699) - 0.037584,
            (x - 0.030001222851889303) / 5.0,
        )

    if out_reflection:
        x = x * 0.9

    return as_float(from_range_1(x))
Exemple #34
0
def log_decoding_CanonLog3(
    clog3: FloatingOrArrayLike,
    bit_depth: Integer = 10,
    in_normalised_code_value: Boolean = True,
    out_reflection: Boolean = True,
) -> FloatingOrNDArray:
    """
    Define the *Canon Log 3* log decoding curve / electro-optical transfer
    function.

    Parameters
    ----------
    clog3
        *Canon Log 3* non-linear data.
    bit_depth
        Bit depth used for conversion.
    in_normalised_code_value
        Whether the *Canon Log 3* non-linear data is encoded with normalised
        code values.
    out_reflection
        Whether the light level :math:`x` to a camera is reflection.

    Returns
    -------
    :class:`numpy.floating` or :class:`numpy.ndarray`
        Linear data :math:`x`.

    Notes
    -----
    +------------+-----------------------+---------------+
    | **Domain** | **Scale - Reference** | **Scale - 1** |
    +============+=======================+===============+
    | ``clog3``  | [0, 1]                | [0, 1]        |
    +------------+-----------------------+---------------+

    +------------+-----------------------+---------------+
    | **Range**  | **Scale - Reference** | **Scale - 1** |
    +============+=======================+===============+
    | ``x``      | [0, 1]                | [0, 1]        |
    +------------+-----------------------+---------------+

    References
    ----------
    :cite:`Canona`

    Examples
    --------
    >>> log_decoding_CanonLog3(34.338936938868677 / 100)  # doctest: +ELLIPSIS
    0.1800000...
    """

    clog3 = to_domain_1(clog3)

    clog3 = (legal_to_full(clog3, bit_depth)
             if in_normalised_code_value else clog3)

    x = np.select(
        (clog3 < 0.04076162, clog3 <= 0.105357102, clog3 > 0.105357102),
        (
            -(10**((0.07623209 - clog3) / 0.42889912) - 1) / 14.98325,
            (clog3 - 0.073059361) / 2.3069815,
            (10**((clog3 - 0.069886632) / 0.42889912) - 1) / 14.98325,
        ),
    )

    if out_reflection:
        x = x * 0.9

    return as_float(from_range_1(x))
Exemple #35
0
def log_encoding_CanonLog(
    x: FloatingOrArrayLike,
    bit_depth: Integer = 10,
    out_normalised_code_value: Boolean = True,
    in_reflection: Boolean = True,
) -> FloatingOrNDArray:
    """
    Define the *Canon Log* log encoding curve / opto-electronic transfer
    function.

    Parameters
    ----------
    x
        Linear data :math:`x`.
    bit_depth
        Bit depth used for conversion.
    out_normalised_code_value
        Whether the *Canon Log* non-linear data is encoded as normalised code
        values.
    in_reflection
        Whether the light level :math:`x` to a camera is reflection.

    Returns
    -------
    :class:`numpy.floating` or :class:`numpy.ndarray`
        *Canon Log* non-linear data.

    References
    ----------
    :cite:`Thorpe2012a`

    Notes
    -----
    +------------+-----------------------+---------------+
    | **Domain** | **Scale - Reference** | **Scale - 1** |
    +============+=======================+===============+
    | ``x``      | [0, 1]                | [0, 1]        |
    +------------+-----------------------+---------------+

    +------------+-----------------------+---------------+
    | **Range**  | **Scale - Reference** | **Scale - 1** |
    +============+=======================+===============+
    | ``clog``   | [0, 1]                | [0, 1]        |
    +------------+-----------------------+---------------+

    Examples
    --------
    >>> log_encoding_CanonLog(0.18) * 100  # doctest: +ELLIPSIS
    34.3389651...

    The values of *Table 2 Canon-Log Code Values* table in :cite:`Thorpe2012a`
    are obtained as follows:

    >>> x = np.array([0, 2, 18, 90, 720]) / 100
    >>> np.around(log_encoding_CanonLog(x) * (2 ** 10 - 1)).astype(np.int)
    array([ 128,  169,  351,  614, 1016])
    >>> np.around(log_encoding_CanonLog(x, 10, False) * 100, 1)
    array([   7.3,   12. ,   32.8,   62.7,  108.7])
    """

    x = to_domain_1(x)

    if in_reflection:
        x = x / 0.9

    with domain_range_scale("ignore"):
        clog = np.where(
            x < log_decoding_CanonLog(0.0730597, bit_depth, False),
            -(0.529136 * (np.log10(-x * 10.1596 + 1)) - 0.0730597),
            0.529136 * np.log10(10.1596 * x + 1) + 0.0730597,
        )

    clog_cv = (full_to_legal(clog, bit_depth)
               if out_normalised_code_value else clog)

    return as_float(from_range_1(clog_cv))
Exemple #36
0
def log_decoding_CanonLog2(
    clog2: FloatingOrArrayLike,
    bit_depth: Integer = 10,
    in_normalised_code_value: Boolean = True,
    out_reflection: Boolean = True,
) -> FloatingOrNDArray:
    """
    Define the *Canon Log 2* log decoding curve / electro-optical transfer
    function.

    Parameters
    ----------
    clog2
        *Canon Log 2* non-linear data.
    bit_depth
        Bit depth used for conversion.
    in_normalised_code_value
        Whether the *Canon Log 2* non-linear data is encoded with normalised
        code values.
    out_reflection
        Whether the light level :math:`x` to a camera is reflection.

    Returns
    -------
    :class:`numpy.floating` or :class:`numpy.ndarray`
        Linear data :math:`x`.

    Notes
    -----
    +------------+-----------------------+---------------+
    | **Domain** | **Scale - Reference** | **Scale - 1** |
    +============+=======================+===============+
    | ``clog2``  | [0, 1]                | [0, 1]        |
    +------------+-----------------------+---------------+

    +------------+-----------------------+---------------+
    | **Range**  | **Scale - Reference** | **Scale - 1** |
    +============+=======================+===============+
    | ``x``      | [0, 1]                | [0, 1]        |
    +------------+-----------------------+---------------+

    References
    ----------
    :cite:`Canona`

    Examples
    --------
    >>> log_decoding_CanonLog2(39.825469498316735 / 100)  # doctest: +ELLIPSIS
    0.1799999...
    """

    clog2 = to_domain_1(clog2)

    clog2 = (legal_to_full(clog2, bit_depth)
             if in_normalised_code_value else clog2)

    x = np.where(
        clog2 < 0.035388128,
        -(10**((0.035388128 - clog2) / 0.281863093) - 1) / 87.09937546,
        (10**((clog2 - 0.035388128) / 0.281863093) - 1) / 87.09937546,
    )

    if out_reflection:
        x = x * 0.9

    return as_float(from_range_1(x))
Exemple #37
0
def log_encoding_CanonLog3(
    x: FloatingOrArrayLike,
    bit_depth: Integer = 10,
    out_normalised_code_value: Boolean = True,
    in_reflection: Boolean = True,
) -> FloatingOrNDArray:
    """
    Define the *Canon Log 3* log encoding curve / opto-electronic transfer
    function.

    Parameters
    ----------
    x
        Linear data :math:`x`.
    bit_depth
        Bit depth used for conversion.
    out_normalised_code_value
        Whether the *Canon Log 3* non-linear data is encoded as normalised code
        values.
    in_reflection
        Whether the light level :math:`x` to a camera is reflection.

    Returns
    -------
    :class:`numpy.floating` or :class:`numpy.ndarray`
        *Canon Log 3* non-linear data.

    Notes
    -----
    -   Introspection of the grafting points by Shaw, N. (2018) shows that the
        *Canon Log 3* IDT was likely derived from its encoding curve as the
        later is grafted at *+/-0.014*::

            >>> clog3 = 0.04076162
            >>> (clog3 - 0.073059361) / 2.3069815
            -0.014000000000000002
            >>> clog3 = 0.105357102
            >>> (clog3 - 0.073059361) / 2.3069815
            0.013999999999999997

    +------------+-----------------------+---------------+
    | **Domain** | **Scale - Reference** | **Scale - 1** |
    +============+=======================+===============+
    | ``x``      | [0, 1]                | [0, 1]        |
    +------------+-----------------------+---------------+

    +------------+-----------------------+---------------+
    | **Range**  | **Scale - Reference** | **Scale - 1** |
    +============+=======================+===============+
    | ``clog3``  | [0, 1]                | [0, 1]        |
    +------------+-----------------------+---------------+

    References
    ----------
    :cite:`Canona`

    Examples
    --------
    >>> log_encoding_CanonLog3(0.18) * 100  # doctest: +ELLIPSIS
    34.3389369...
    """

    x = to_domain_1(x)

    if in_reflection:
        x = x / 0.9

    with domain_range_scale("ignore"):
        clog3 = np.select(
            (
                x < log_decoding_CanonLog3(0.04076162, bit_depth, False,
                                           False),
                x <= log_decoding_CanonLog3(0.105357102, bit_depth, False,
                                            False),
                x > log_decoding_CanonLog3(0.105357102, bit_depth, False,
                                           False),
            ),
            (
                -0.42889912 * np.log10(-x * 14.98325 + 1) + 0.07623209,
                2.3069815 * x + 0.073059361,
                0.42889912 * np.log10(x * 14.98325 + 1) + 0.069886632,
            ),
        )

    clog3_cv = (full_to_legal(clog3, bit_depth)
                if out_normalised_code_value else clog3)

    return as_float(from_range_1(clog3_cv))
Exemple #38
0
def log_decoding_CanonLog(
    clog: FloatingOrArrayLike,
    bit_depth: Integer = 10,
    in_normalised_code_value: Boolean = True,
    out_reflection: Boolean = True,
) -> FloatingOrNDArray:
    """
    Define the *Canon Log* log decoding curve / electro-optical transfer
    function.

    Parameters
    ----------
    clog
        *Canon Log* non-linear data.
    bit_depth
        Bit depth used for conversion.
    in_normalised_code_value
        Whether the *Canon Log* non-linear data is encoded with normalised
        code values.
    out_reflection
        Whether the light level :math:`x` to a camera is reflection.

    Returns
    -------
    :class:`numpy.floating` or :class:`numpy.ndarray`
        Linear data :math:`x`.

    Notes
    -----
    +------------+-----------------------+---------------+
    | **Domain** | **Scale - Reference** | **Scale - 1** |
    +============+=======================+===============+
    | ``clog``   | [0, 1]                | [0, 1]        |
    +------------+-----------------------+---------------+

    +------------+-----------------------+---------------+
    | **Range**  | **Scale - Reference** | **Scale - 1** |
    +============+=======================+===============+
    | ``x``      | [0, 1]                | [0, 1]        |
    +------------+-----------------------+---------------+

    References
    ----------
    :cite:`Thorpe2012a`

    Examples
    --------
    >>> log_decoding_CanonLog(34.338965172606912 / 100)  # doctest: +ELLIPSIS
    0.17999999...
    """

    clog = to_domain_1(clog)

    clog = legal_to_full(clog, bit_depth) if in_normalised_code_value else clog

    x = np.where(
        clog < 0.0730597,
        -(10**((0.0730597 - clog) / 0.529136) - 1) / 10.1596,
        (10**((clog - 0.0730597) / 0.529136) - 1) / 10.1596,
    )

    if out_reflection:
        x = x * 0.9

    return as_float(from_range_1(x))
Exemple #39
0
def log_encoding_CanonLog2(
    x: FloatingOrArrayLike,
    bit_depth: Integer = 10,
    out_normalised_code_value: Boolean = True,
    in_reflection: Boolean = True,
) -> FloatingOrNDArray:
    """
    Define the *Canon Log 2* log encoding curve / opto-electronic transfer
    function.

    Parameters
    ----------
    x
        Linear data :math:`x`.
    bit_depth
        Bit depth used for conversion.
    out_normalised_code_value
        Whether the *Canon Log 2* non-linear data is encoded as normalised
        code values.
    in_reflection
        Whether the light level :math:`x` to a camera is reflection.

    Returns
    -------
    :class:`numpy.floating` or :class:`numpy.ndarray`
        *Canon Log 2* non-linear data.

    Notes
    -----
    +------------+-----------------------+---------------+
    | **Domain** | **Scale - Reference** | **Scale - 1** |
    +============+=======================+===============+
    | ``x``      | [0, 1]                | [0, 1]        |
    +------------+-----------------------+---------------+

    +------------+-----------------------+---------------+
    | **Range**  | **Scale - Reference** | **Scale - 1** |
    +============+=======================+===============+
    | ``clog2``  | [0, 1]                | [0, 1]        |
    +------------+-----------------------+---------------+

    References
    ----------
    :cite:`Canona`

    Examples
    --------
    >>> log_encoding_CanonLog2(0.18) * 100  # doctest: +ELLIPSIS
    39.8254694...
    """

    x = to_domain_1(x)

    if in_reflection:
        x = x / 0.9

    with domain_range_scale("ignore"):
        clog2 = np.where(
            x < log_decoding_CanonLog2(0.035388128, bit_depth, False),
            -(0.281863093 * (np.log10(-x * 87.09937546 + 1)) - 0.035388128),
            0.281863093 * np.log10(x * 87.09937546 + 1) + 0.035388128,
        )

    clog2_cv = (full_to_legal(clog2, bit_depth)
                if out_normalised_code_value else clog2)

    return as_float(from_range_1(clog2_cv))
Exemple #40
0
def RGB_to_HCL(RGB: ArrayLike,
               gamma: Floating = 3,
               Y_0: Floating = 100) -> NDArray:
    """
    Convert from *RGB* colourspace to *HCL* colourspace according to
    *Sarifuddin and Missaoui (2005)* method.

    Parameters
    ----------
    RGB
        *RGB* colourspace array.
    gamma
        Non-linear lightness exponent matching *Lightness* :math:`L^*`.
    Y_0
        White reference luminance :math:`Y_0`.

    Returns
    -------
    :class:`numpy.ndarray`
        *HCL* array.

    Notes
    -----
    +------------+-----------------------+---------------+
    | **Domain** | **Scale - Reference** | **Scale - 1** |
    +============+=======================+===============+
    | ``RGB``    | [0, 1]                | [0, 1]        |
    +------------+-----------------------+---------------+

    +------------+-----------------------+---------------+
    | **Range**  | **Scale - Reference** | **Scale - 1** |
    +============+=======================+===============+
    | ``HCL``    | [0, 1]                | [0, 1]        |
    +------------+-----------------------+---------------+

    References
    ----------
    :cite:`Sarifuddin2005`, :cite:`Wikipedia2015`

    Examples
    --------
    >>> RGB = np.array([0.45620519, 0.03081071, 0.04091952])
    >>> RGB_to_HCL(RGB)  # doctest: +ELLIPSIS
    array([-0.0316785...,  0.2841715...,  0.2285964...])
    """

    R, G, B = tsplit(to_domain_1(RGB))

    Min = np.minimum(np.minimum(R, G), B)
    Max = np.maximum(np.maximum(R, G), B)

    alpha = (Min / Max) / Y_0

    Q = np.exp(alpha * gamma)

    L = (Q * Max + (Q - 1) * Min) / 2

    R_G = R - G
    G_B = G - B
    B_R = B - R

    C = Q * (np.abs(R_G) + np.abs(G_B) + np.abs(B_R)) / 3

    H = np.arctan(G_B / R_G)

    _2_3_H = 2 / 3 * H
    _4_3_H = 4 / 3 * H

    H = np.select(
        [
            np.logical_and(R_G >= 0, G_B >= 0),
            np.logical_and(R_G >= 0, G_B < 0),
            np.logical_and(R_G < 0, G_B >= 0),
            np.logical_and(R_G < 0, G_B < 0),
        ],
        [
            _2_3_H,
            _4_3_H,
            np.pi + _4_3_H,
            _2_3_H - np.pi,
        ],
    )

    HCL = tstack([H, C, L])

    return from_range_1(HCL)
Exemple #41
0
def HSV_to_RGB(HSV: ArrayLike) -> NDArray:
    """
    Convert from *HSV* colourspace to *RGB* colourspace.

    Parameters
    ----------
    HSV
        *HSV* colourspace array.

    Returns
    -------
    :class:`numpy.ndarray`
        *RGB* colourspace array.

    Notes
    -----
    +------------+-----------------------+---------------+
    | **Domain** | **Scale - Reference** | **Scale - 1** |
    +============+=======================+===============+
    | ``HSV``    | [0, 1]                | [0, 1]        |
    +------------+-----------------------+---------------+

    +------------+-----------------------+---------------+
    | **Range**  | **Scale - Reference** | **Scale - 1** |
    +============+=======================+===============+
    | ``RGB``    | [0, 1]                | [0, 1]        |
    +------------+-----------------------+---------------+

    References
    ----------
    :cite:`EasyRGBn`, :cite:`Smith1978b`, :cite:`Wikipedia2003`

    Examples
    --------
    >>> HSV = np.array([0.99603944, 0.93246304, 0.45620519])
    >>> HSV_to_RGB(HSV)  # doctest: +ELLIPSIS
    array([ 0.4562051...,  0.0308107...,  0.0409195...])
    """

    H, S, V = tsplit(to_domain_1(HSV))

    h = as_float_array(H * 6)
    h[np.asarray(h == 6)] = 0

    i = np.floor(h)
    j = V * (1 - S)
    k = V * (1 - S * (h - i))
    l = V * (1 - S * (1 - (h - i)))  # noqa

    i = tstack([i, i, i]).astype(np.uint8)

    RGB = np.choose(
        i,
        [
            tstack([V, l, j]),
            tstack([k, V, j]),
            tstack([j, V, l]),
            tstack([j, k, V]),
            tstack([l, j, V]),
            tstack([V, j, k]),
        ],
        mode="clip",
    )

    return from_range_1(RGB)
Exemple #42
0
def log_decoding_CanonLog2(clog2,
                           bit_depth=10,
                           in_legal=True,
                           out_reflection=True):
    """
    Defines the *Canon Log 2* log decoding curve / electro-optical transfer
    function.

    Parameters
    ----------
    clog2 : numeric or array_like
        *Canon Log 2* non-linear data.
    bit_depth : int, optional
        Bit depth used for conversion.
    in_legal : bool, optional
        Whether the *Canon Log 2* non-linear data is encoded in legal
        range.
    out_reflection : bool, optional
        Whether the light level :math:`x` to a camera is reflection.

    Returns
    -------
    numeric or ndarray
        Linear data :math:`x`.

    Notes
    -----

    +------------+-----------------------+---------------+
    | **Domain** | **Scale - Reference** | **Scale - 1** |
    +============+=======================+===============+
    | ``clog2``  | [0, 1]                | [0, 1]        |
    +------------+-----------------------+---------------+

    +------------+-----------------------+---------------+
    | **Range**  | **Scale - Reference** | **Scale - 1** |
    +============+=======================+===============+
    | ``x``      | [0, 1]                | [0, 1]        |
    +------------+-----------------------+---------------+

    References
    ----------
    :cite:`Canona`

    Examples
    --------
    >>> log_decoding_CanonLog2(39.825469498316735 / 100)  # doctest: +ELLIPSIS
    0.1799999...
    """

    clog2 = to_domain_1(clog2)

    clog2 = legal_to_full(clog2, bit_depth) if in_legal else clog2

    x = np.where(
        clog2 < 0.035388128,
        -(10**((0.035388128 - clog2) / 0.281863093) - 1) / 87.09937546,
        (10**((clog2 - 0.035388128) / 0.281863093) - 1) / 87.09937546,
    )

    if out_reflection:
        x = x * 0.9

    return as_float(from_range_1(x))
Exemple #43
0
def RGB_to_HSV(RGB: ArrayLike) -> NDArray:
    """
    Convert from *RGB* colourspace to *HSV* colourspace.

    Parameters
    ----------
    RGB
        *RGB* colourspace array.

    Returns
    -------
    :class:`numpy.ndarray`
        *HSV* array.

    Notes
    -----
    +------------+-----------------------+---------------+
    | **Domain** | **Scale - Reference** | **Scale - 1** |
    +============+=======================+===============+
    | ``RGB``    | [0, 1]                | [0, 1]        |
    +------------+-----------------------+---------------+

    +------------+-----------------------+---------------+
    | **Range**  | **Scale - Reference** | **Scale - 1** |
    +============+=======================+===============+
    | ``HSV``    | [0, 1]                | [0, 1]        |
    +------------+-----------------------+---------------+

    References
    ----------
    :cite:`EasyRGBj`, :cite:`Smith1978b`, :cite:`Wikipedia2003`

    Examples
    --------
    >>> RGB = np.array([0.45620519, 0.03081071, 0.04091952])
    >>> RGB_to_HSV(RGB)  # doctest: +ELLIPSIS
    array([ 0.9960394...,  0.9324630...,  0.4562051...])
    """

    RGB = to_domain_1(RGB)

    maximum = np.amax(RGB, -1)
    delta = np.ptp(RGB, -1)

    V = maximum

    R, G, B = tsplit(RGB)

    S = as_float_array(delta / maximum)
    S[np.asarray(delta == 0)] = 0

    delta_R = (((maximum - R) / 6) + (delta / 2)) / delta
    delta_G = (((maximum - G) / 6) + (delta / 2)) / delta
    delta_B = (((maximum - B) / 6) + (delta / 2)) / delta

    H = delta_B - delta_G
    H = np.where(G == maximum, (1 / 3) + delta_R - delta_B, H)
    H = np.where(B == maximum, (2 / 3) + delta_G - delta_R, H)
    H[np.asarray(H < 0)] += 1
    H[np.asarray(H > 1)] -= 1
    H[np.asarray(delta == 0)] = 0

    HSV = tstack([H, S, V])

    return from_range_1(HSV)
Exemple #44
0
def oetf_inverse_ARIBSTDB67(E_p, r=0.5, constants=ARIBSTDB67_CONSTANTS):
    """
    Defines *ARIB STD-B67 (Hybrid Log-Gamma)* inverse opto-electrical transfer
    function (OETF / OECF).

    Parameters
    ----------
    E_p : numeric or array_like
        Non-linear signal :math:`E'`.
    r : numeric, optional
        Video level corresponding to reference white level.
    constants : Structure, optional
        *ARIB STD-B67 (Hybrid Log-Gamma)* constants.

    Returns
    -------
    numeric or ndarray
        Voltage :math:`E` normalised by the reference white level and
        proportional to the implicit light intensity that would be detected
        with a reference camera color channel R, G, B.

    Notes
    -----

    +------------+-----------------------+---------------+
    | **Domain** | **Scale - Reference** | **Scale - 1** |
    +============+=======================+===============+
    | ``E_p``    | [0, 1]                | [0, 1]        |
    +------------+-----------------------+---------------+

    +------------+-----------------------+---------------+
    | **Range**  | **Scale - Reference** | **Scale - 1** |
    +============+=======================+===============+
    | ``E``      | [0, 1]                | [0, 1]        |
    +------------+-----------------------+---------------+

    -   This definition uses the *mirror* negative number handling mode of
        :func:`colour.models.gamma_function` definition to the sign of negative
        numbers.

    References
    ----------
    :cite:`AssociationofRadioIndustriesandBusinesses2015a`

    Examples
    --------
    >>> oetf_inverse_ARIBSTDB67(0.212132034355964)  # doctest: +ELLIPSIS
    0.1799999...
    """

    E_p = to_domain_1(E_p)

    a = constants.a
    b = constants.b
    c = constants.c

    with domain_range_scale('ignore'):
        E = np.where(
            E_p <= oetf_ARIBSTDB67(1),
            gamma_function((E_p / r), 2, 'mirror'),
            np.exp((E_p - c) / a) + b,
        )

    return as_float(from_range_1(E))
Exemple #45
0
def chromatic_adaptation_VonKries(XYZ, XYZ_w, XYZ_wr, transform='CAT02'):
    """
    Adapts given stimulus from test viewing conditions to reference viewing
    conditions.

    Parameters
    ----------
    XYZ : array_like
        *CIE XYZ* tristimulus values of stimulus to adapt.
    XYZ_w : array_like
        Test viewing condition *CIE XYZ* tristimulus values of whitepoint.
    XYZ_wr : array_like
        Reference viewing condition *CIE XYZ* tristimulus values of whitepoint.
    transform : unicode, optional
        **{'CAT02', 'XYZ Scaling', 'Von Kries', 'Bradford', 'Sharp',
        'Fairchild', 'CMCCAT97', 'CMCCAT2000', 'CAT02_BRILL_CAT', 'Bianco',
        'Bianco PC'}**,
        Chromatic adaptation transform.

    Returns
    -------
    ndarray
        *CIE XYZ_c* tristimulus values of the stimulus corresponding colour.

    Notes
    -----

    +------------+-----------------------+---------------+
    | **Domain** | **Scale - Reference** | **Scale - 1** |
    +============+=======================+===============+
    | ``XYZ``    | [0, 1]                | [0, 1]        |
    +------------+-----------------------+---------------+
    | ``XYZ_n``  | [0, 1]                | [0, 1]        |
    +------------+-----------------------+---------------+
    | ``XYZ_r``  | [0, 1]                | [0, 1]        |
    +------------+-----------------------+---------------+

    +------------+-----------------------+---------------+
    | **Range**  | **Scale - Reference** | **Scale - 1** |
    +============+=======================+===============+
    | ``XYZ_c``  | [0, 1]                | [0, 1]        |
    +------------+-----------------------+---------------+

    References
    ----------
    :cite:`Fairchild2013t`

    Examples
    --------
    >>> XYZ = np.array([0.20654008, 0.12197225, 0.05136952])
    >>> XYZ_w = np.array([0.95045593, 1.00000000, 1.08905775])
    >>> XYZ_wr = np.array([0.96429568, 1.00000000, 0.82510460])
    >>> chromatic_adaptation_VonKries(XYZ, XYZ_w, XYZ_wr)  # doctest: +ELLIPSIS
    array([ 0.2163881...,  0.1257    ,  0.0384749...])

    Using Bradford method:

    >>> XYZ = np.array([0.20654008, 0.12197225, 0.05136952])
    >>> XYZ_w = np.array([0.95045593, 1.00000000, 1.08905775])
    >>> XYZ_wr = np.array([0.96429568, 1.00000000, 0.82510460])
    >>> transform = 'Bradford'
    >>> chromatic_adaptation_VonKries(XYZ, XYZ_w, XYZ_wr, transform)
    ... # doctest: +ELLIPSIS
    array([ 0.2166600...,  0.1260477...,  0.0385506...])
    """

    XYZ = to_domain_1(XYZ)

    M_CAT = chromatic_adaptation_matrix_VonKries(XYZ_w, XYZ_wr, transform)
    XYZ_a = dot_vector(M_CAT, XYZ)

    return from_range_1(XYZ_a)
Exemple #46
0
def log_encoding_CanonLog(x, bit_depth=10, out_legal=True, in_reflection=True):
    """
    Defines the *Canon Log* log encoding curve / opto-electronic transfer
    function.

    Parameters
    ----------
    x : numeric or array_like
        Linear data :math:`x`.
    bit_depth : int, optional
        Bit depth used for conversion.
    out_legal : bool, optional
        Whether the *Canon Log* non-linear data is encoded in legal
        range.
    in_reflection : bool, optional
        Whether the light level :math:`x` to a camera is reflection.

    Returns
    -------
    numeric or ndarray
        *Canon Log* non-linear data.

    References
    ----------
    :cite:`Thorpe2012a`

    Notes
    -----

    +------------+-----------------------+---------------+
    | **Domain** | **Scale - Reference** | **Scale - 1** |
    +============+=======================+===============+
    | ``x``      | [0, 1]                | [0, 1]        |
    +------------+-----------------------+---------------+

    +------------+-----------------------+---------------+
    | **Range**  | **Scale - Reference** | **Scale - 1** |
    +============+=======================+===============+
    | ``clog``   | [0, 1]                | [0, 1]        |
    +------------+-----------------------+---------------+

    Examples
    --------
    >>> log_encoding_CanonLog(0.18) * 100  # doctest: +ELLIPSIS
    34.3389651...
    """

    x = to_domain_1(x)

    if in_reflection:
        x = x / 0.9

    with domain_range_scale('ignore'):
        clog = np.where(
            x < log_decoding_CanonLog(0.0730597, bit_depth, False),
            -(0.529136 * (np.log10(-x * 10.1596 + 1)) - 0.0730597),
            0.529136 * np.log10(10.1596 * x + 1) + 0.0730597,
        )

    clog = full_to_legal(clog, bit_depth) if out_legal else clog

    return as_float(from_range_1(clog))
Exemple #47
0
def log_decoding_CanonLog3(clog3,
                           bit_depth=10,
                           in_legal=True,
                           out_reflection=True):
    """
    Defines the *Canon Log 3* log decoding curve / electro-optical transfer
    function.

    Parameters
    ----------
    clog3 : numeric or array_like
        *Canon Log 3* non-linear data.
    bit_depth : int, optional
        Bit depth used for conversion.
    in_legal : bool, optional
        Whether the *Canon Log 3* non-linear data is encoded in legal
        range.
    out_reflection : bool, optional
        Whether the light level :math:`x` to a camera is reflection.

    Returns
    -------
    numeric or ndarray
        Linear data :math:`x`.

    Notes
    -----

    +------------+-----------------------+---------------+
    | **Domain** | **Scale - Reference** | **Scale - 1** |
    +============+=======================+===============+
    | ``clog3``  | [0, 1]                | [0, 1]        |
    +------------+-----------------------+---------------+

    +------------+-----------------------+---------------+
    | **Range**  | **Scale - Reference** | **Scale - 1** |
    +============+=======================+===============+
    | ``x``      | [0, 1]                | [0, 1]        |
    +------------+-----------------------+---------------+

    References
    ----------
    :cite:`Canona`

    Examples
    --------
    >>> log_decoding_CanonLog3(34.338936938868677 / 100)  # doctest: +ELLIPSIS
    0.1800000...
    """

    clog3 = to_domain_1(clog3)

    clog3 = legal_to_full(clog3, bit_depth) if in_legal else clog3

    x = np.select(
        (clog3 < 0.04076162, clog3 <= 0.105357102, clog3 > 0.105357102),
        (-(10**((0.07623209 - clog3) / 0.42889912) - 1) / 14.98325,
         (clog3 - 0.073059361) / 2.3069815,
         (10**((clog3 - 0.069886632) / 0.42889912) - 1) / 14.98325))

    if out_reflection:
        x = x * 0.9

    return as_float(from_range_1(x))
Exemple #48
0
def log_encoding_CanonLog3(x,
                           bit_depth=10,
                           out_legal=True,
                           in_reflection=True):
    """
    Defines the *Canon Log 3* log encoding curve / opto-electronic transfer
    function.

    Parameters
    ----------
    x : numeric or array_like
        Linear data :math:`x`.
    bit_depth : int, optional
        Bit depth used for conversion.
    out_legal : bool, optional
        Whether the *Canon Log 3* non-linear data is encoded in legal
        range.
    in_reflection : bool, optional
        Whether the light level :math:`x` to a camera is reflection.

    Returns
    -------
    numeric or ndarray
        *Canon Log 3* non-linear data.

    Notes
    -----
    -   Introspection of the grafting points by Shaw, N. (2018) shows that the
        *Canon Log 3* IDT was likely derived from its encoding curve as the
        later is grafted at *+/-0.014*::

            >>> clog3 = 0.04076162
            >>> (clog3 - 0.073059361) / 2.3069815
            -0.014000000000000002
            >>> clog3 = 0.105357102
            >>> (clog3 - 0.073059361) / 2.3069815
            0.013999999999999997

    Notes
    -----

    +------------+-----------------------+---------------+
    | **Domain** | **Scale - Reference** | **Scale - 1** |
    +============+=======================+===============+
    | ``x``      | [0, 1]                | [0, 1]        |
    +------------+-----------------------+---------------+

    +------------+-----------------------+---------------+
    | **Range**  | **Scale - Reference** | **Scale - 1** |
    +============+=======================+===============+
    | ``clog3``  | [0, 1]                | [0, 1]        |
    +------------+-----------------------+---------------+

    References
    ----------
    :cite:`Canona`

    Examples
    --------
    >>> log_encoding_CanonLog3(0.18) * 100  # doctest: +ELLIPSIS
    34.3389369...
    """

    x = to_domain_1(x)

    if in_reflection:
        x = x / 0.9

    with domain_range_scale('ignore'):
        clog3 = np.select(
            (x < log_decoding_CanonLog3(0.04076162, bit_depth, False, False),
             x <= log_decoding_CanonLog3(0.105357102, bit_depth, False, False),
             x > log_decoding_CanonLog3(0.105357102, bit_depth, False, False)),
            (-0.42889912 * np.log10(-x * 14.98325 + 1) + 0.07623209,
             2.3069815 * x + 0.073059361,
             0.42889912 * np.log10(x * 14.98325 + 1) + 0.069886632))

    clog3 = full_to_legal(clog3, bit_depth) if out_legal else clog3

    return as_float(from_range_1(clog3))
Exemple #49
0
def hdr_IPT_to_XYZ(IPT_hdr, Y_s=0.2, Y_abs=100, method='Fairchild 2011'):
    """
    Converts from *hdr-IPT* colourspace to *CIE XYZ* tristimulus values.

    Parameters
    ----------
    IPT_hdr : array_like
        *hdr-IPT* colourspace array.
    Y_s : numeric or array_like
        Relative luminance :math:`Y_s` of the surround.
    Y_abs : numeric or array_like
        Absolute luminance :math:`Y_{abs}` of the scene diffuse white in
        :math:`cd/m^2`.
    method : unicode, optional
        **{'Fairchild 2011', 'Fairchild 2010'}**,
        Computation method.

    Returns
    -------
    ndarray
        *CIE XYZ* tristimulus values.

    Notes
    -----

    +-------------+-------------------------+---------------------+
    | **Domain**  | **Scale - Reference**   | **Scale - 1**       |
    +=============+=========================+=====================+
    | ``IPT_hdr`` | ``I_hdr`` : [0, 100]    | ``I_hdr`` : [0, 1]  |
    |             |                         |                     |
    |             | ``P_hdr`` : [-100, 100] | ``P_hdr`` : [-1, 1] |
    |             |                         |                     |
    |             | ``T_hdr`` : [-100, 100] | ``T_hdr`` : [-1, 1] |
    +-------------+-------------------------+---------------------+
    | ``Y_s``     | [0, 1]                  | [0, 1]              |
    +-------------+-------------------------+---------------------+

    +-------------+-------------------------+---------------------+
    | **Range**   | **Scale - Reference**   | **Scale - 1**       |
    +=============+=========================+=====================+
    | ``XYZ``     | [0, 1]                  | [0, 1]              |
    +-------------+-------------------------+---------------------+

    References
    ----------
    :cite:`Fairchild2010`, :cite:`Fairchild2011`

    Examples
    --------
    >>> IPT_hdr = np.array([48.39376346, 42.44990202, 22.01954033])
    >>> hdr_IPT_to_XYZ(IPT_hdr)  # doctest: +ELLIPSIS
    array([ 0.2065400...,  0.1219722...,  0.0513695...])
    >>> IPT_hdr = np.array([30.02873147, 83.93845061, 34.90287382])
    >>> hdr_IPT_to_XYZ(IPT_hdr, method='Fairchild 2010')
    ... # doctest: +ELLIPSIS
    array([ 0.2065400...,  0.1219722...,  0.0513695...])
    """

    IPT_hdr = to_domain_100(IPT_hdr)

    method_l = method.lower()
    assert method.lower() in [
        m.lower() for m in HDR_IPT_METHODS
    ], ('"{0}" method is invalid, must be one of {1}!'.format(
        method, HDR_IPT_METHODS))

    if method_l == 'fairchild 2010':
        luminance_callable = luminance_Fairchild2010
    else:
        luminance_callable = luminance_Fairchild2011

    e = exponent_hdr_IPT(Y_s, Y_abs, method)[..., np.newaxis]

    LMS = dot_vector(IPT_IPT_TO_LMS_MATRIX, IPT_hdr)

    # Domain and range scaling has already be handled.
    with domain_range_scale('ignore'):
        LMS_prime = np.sign(LMS) * np.abs(luminance_callable(LMS, e))

    XYZ = dot_vector(IPT_LMS_TO_XYZ_MATRIX, LMS_prime)

    return from_range_1(XYZ)
Exemple #50
0
def log_encoding_SLog3(x,
                       bit_depth=10,
                       out_normalised_code_value=True,
                       in_reflection=True,
                       **kwargs):
    """
    Defines the *Sony S-Log3* log encoding curve / opto-electronic transfer
    function.

    Parameters
    ----------
    x : numeric or array_like
        Reflection or :math:`IRE / 100` input light level :math:`x` to a
        camera.
    bit_depth : int, optional
        Bit depth used for conversion.
    out_normalised_code_value : bool, optional
        Whether the non-linear *Sony S-Log3* data :math:`y` is encoded as
        normalised code values.
    in_reflection : bool, optional
        Whether the light level :math:`x` to a camera is reflection.

    Other Parameters
    ----------------
    \\**kwargs : dict, optional
        Keywords arguments for deprecation management.

    Returns
    -------
    numeric or ndarray
        Non-linear *Sony S-Log3* data :math:`y`.

    Notes
    -----

    +------------+-----------------------+---------------+
    | **Domain** | **Scale - Reference** | **Scale - 1** |
    +============+=======================+===============+
    | ``x``      | [0, 1]                | [0, 1]        |
    +------------+-----------------------+---------------+

    +------------+-----------------------+---------------+
    | **Range**  | **Scale - Reference** | **Scale - 1** |
    +============+=======================+===============+
    | ``y``      | [0, 1]                | [0, 1]        |
    +------------+-----------------------+---------------+

    References
    ----------
    :cite:`SonyCorporationd`

    Examples
    --------
    >>> log_encoding_SLog3(0.18)  # doctest: +ELLIPSIS
    0.4105571...

    The values of *S-Log3 10bit code values (18%, 90%)* table in
    :cite:`SonyCorporationd` are obtained as follows:

    >>> x = np.array([0, 18, 90]) / 100
    >>> np.around(log_encoding_SLog3(x, 10, False) * 100).astype(np.int)
    array([ 4, 41, 61])
    >>> np.around(log_encoding_SLog3(x) * (2 ** 10 - 1)).astype(np.int)
    array([ 95, 420, 598])
    """

    out_normalised_code_value = handle_arguments_deprecation(
        {
            'ArgumentRenamed': [['out_legal', 'out_normalised_code_value']],
        }, **kwargs).get('out_normalised_code_value',
                         out_normalised_code_value)

    x = to_domain_1(x)

    if not in_reflection:
        x = x * 0.9

    y = np.where(
        x >= 0.01125000,
        (420 + np.log10((x + 0.01) / (0.18 + 0.01)) * 261.5) / 1023,
        (x * (171.2102946929 - 95) / 0.01125000 + 95) / 1023,
    )

    y = y if out_normalised_code_value else legal_to_full(y, bit_depth)

    return as_float(from_range_1(y))
Exemple #51
0
def log_decoding_SLog3(y,
                       bit_depth=10,
                       in_normalised_code_value=True,
                       out_reflection=True,
                       **kwargs):
    """
    Defines the *Sony S-Log3* log decoding curve / electro-optical transfer
    function.

    Parameters
    ----------
    y : numeric or array_like
        Non-linear *Sony S-Log3* data :math:`y`.
    bit_depth : int, optional
        Bit depth used for conversion.
    in_normalised_code_value : bool, optional
        Whether the non-linear *Sony S-Log3* data :math:`y` is encoded as
        normalised code values.
    out_reflection : bool, optional
        Whether the light level :math:`x` to a camera is reflection.

    Other Parameters
    ----------------
    \\**kwargs : dict, optional
        Keywords arguments for deprecation management.

    Returns
    -------
    numeric or ndarray
        Reflection or :math:`IRE / 100` input light level :math:`x` to a
        camera.

    Notes
    -----

    +------------+-----------------------+---------------+
    | **Domain** | **Scale - Reference** | **Scale - 1** |
    +============+=======================+===============+
    | ``y``      | [0, 1]                | [0, 1]        |
    +------------+-----------------------+---------------+

    +------------+-----------------------+---------------+
    | **Range**  | **Scale - Reference** | **Scale - 1** |
    +============+=======================+===============+
    | ``x``      | [0, 1]                | [0, 1]        |
    +------------+-----------------------+---------------+

    References
    ----------
    :cite:`SonyCorporationd`

    Examples
    --------
    >>> log_decoding_SLog3(0.410557184750733)  # doctest: +ELLIPSIS
    0.1...
    """

    in_normalised_code_value = handle_arguments_deprecation(
        {
            'ArgumentRenamed': [['in_legal', 'in_normalised_code_value']],
        }, **kwargs).get('in_normalised_code_value', in_normalised_code_value)

    y = to_domain_1(y)

    y = y if in_normalised_code_value else full_to_legal(y, bit_depth)

    x = np.where(
        y >= 171.2102946929 / 1023,
        ((10**((y * 1023 - 420) / 261.5)) * (0.18 + 0.01) - 0.01),
        (y * 1023 - 95) * 0.01125000 / (171.2102946929 - 95),
    )

    if not out_reflection:
        x = x / 0.9

    return as_float(from_range_1(x))
Exemple #52
0
def log_encoding_SLog(x,
                      bit_depth=10,
                      out_normalised_code_value=True,
                      in_reflection=True,
                      **kwargs):
    """
    Defines the *Sony S-Log* log encoding curve / opto-electronic transfer
    function.

    Parameters
    ----------
    x : numeric or array_like
        Reflection or :math:`IRE / 100` input light level :math:`x` to a
        camera.
    bit_depth : int, optional
        Bit depth used for conversion.
    out_normalised_code_value : bool, optional
        Whether the non-linear *Sony S-Log* data :math:`y` is encoded as
        normalised code values.
    in_reflection : bool, optional
        Whether the light level :math:`x` to a camera is reflection.

    Other Parameters
    ----------------
    \\**kwargs : dict, optional
        Keywords arguments for deprecation management.

    Returns
    -------
    numeric or ndarray
        Non-linear *Sony S-Log* data :math:`y`.

    Notes
    -----

    +------------+-----------------------+---------------+
    | **Domain** | **Scale - Reference** | **Scale - 1** |
    +============+=======================+===============+
    | ``x``      | [0, 1]                | [0, 1]        |
    +------------+-----------------------+---------------+

    +------------+-----------------------+---------------+
    | **Range**  | **Scale - Reference** | **Scale - 1** |
    +============+=======================+===============+
    | ``y``      | [0, 1]                | [0, 1]        |
    +------------+-----------------------+---------------+

    References
    ----------
    :cite:`SonyCorporation2012a`

    Examples
    --------
    >>> log_encoding_SLog(0.18)  # doctest: +ELLIPSIS
    0.3849708...

    The values of *IRE and CV of S-Log2 @ISO800* table in
    :cite:`SonyCorporation2012a` are obtained as follows:

    >>> x = np.array([0, 18, 90]) / 100
    >>> np.around(log_encoding_SLog(x, 10, False) * 100).astype(np.int)
    array([ 3, 38, 65])
    >>> np.around(log_encoding_SLog(x) * (2 ** 10 - 1)).astype(np.int)
    array([ 90, 394, 636])
    """

    out_normalised_code_value = handle_arguments_deprecation(
        {
            'ArgumentRenamed': [['out_legal', 'out_normalised_code_value']],
        }, **kwargs).get('out_normalised_code_value',
                         out_normalised_code_value)

    x = to_domain_1(x)

    if in_reflection:
        x = x / 0.9

    y = np.where(
        x >= 0,
        ((0.432699 * np.log10(x + 0.037584) + 0.616596) + 0.03),
        x * 5 + 0.030001222851889303,
    )

    y = full_to_legal(y, bit_depth) if out_normalised_code_value else y

    return as_float(from_range_1(y))
Exemple #53
0
def oetf_ST2084(C, L_p=10000, constants=ST2084_CONSTANTS):
    """
    Defines *SMPTE ST 2084:2014* optimised perceptual opto-electronic transfer
    function (OETF / OECF).

    Parameters
    ----------
    C : numeric or array_like
        Target optical output :math:`C` in :math:`cd/m^2` of the ideal
        reference display.
    L_p : numeric, optional
        System peak luminance :math:`cd/m^2`, this parameter should stay at its
        default :math:`10000 cd/m^2` value for practical applications. It is
        exposed so that the definition can be used as a fitting function.
    constants : Structure, optional
        *SMPTE ST 2084:2014* constants.

    Returns
    -------
    numeric or ndarray
        Color value abbreviated as :math:`N`, that is directly proportional to
        the encoded signal representation, and which is not directly
        proportional to the optical output of a display device.

    Warnings
    --------
    *SMPTE ST 2084:2014* is an absolute transfer function.

    Notes
    -----

    +------------+-----------------------+---------------+
    | **Domain** | **Scale - Reference** | **Scale - 1** |
    +============+=======================+===============+
    | ``C``      | [0, 1]                | [0, 1]        |
    +------------+-----------------------+---------------+

    +------------+-----------------------+---------------+
    | **Range**  | **Scale - Reference** | **Scale - 1** |
    +============+=======================+===============+
    | ``N``      | [0, 1]                | [0, 1]        |
    +------------+-----------------------+---------------+

    -   *SMPTE ST 2084:2014* is an absolute transfer function, thus the
        domain and range values for the *Reference* and *1* scales are only
        indicative that the data is not affected by scale transformations.

    References
    ----------
    :cite:`Miller2014a`,
    :cite:`SocietyofMotionPictureandTelevisionEngineers2014a`

    Examples
    --------
    >>> oetf_ST2084(100)  # doctest: +ELLIPSIS
    0.5080784...
    """

    C = to_domain_1(C)

    Y_p = spow(C / L_p, constants.m_1)

    N = spow((constants.c_1 + constants.c_2 * Y_p) / (constants.c_3 * Y_p + 1),
             constants.m_2)

    return from_range_1(N)
Exemple #54
0
def eotf_ST2084(N, L_p=10000, constants=ST2084_CONSTANTS):
    """
    Defines *SMPTE ST 2084:2014* optimised perceptual electro-optical transfer
    function (EOTF / EOCF).

    This perceptual quantizer (PQ) has been modeled by Dolby Laboratories
    using *Barten (1999)* contrast sensitivity function.

    Parameters
    ----------
    N : numeric or array_like
        Color value abbreviated as :math:`N`, that is directly proportional to
        the encoded signal representation, and which is not directly
        proportional to the optical output of a display device.
    L_p : numeric, optional
        System peak luminance :math:`cd/m^2`, this parameter should stay at its
        default :math:`10000 cd/m^2` value for practical applications. It is
        exposed so that the definition can be used as a fitting function.
    constants : Structure, optional
        *SMPTE ST 2084:2014* constants.

    Returns
    -------
    numeric or ndarray
          Target optical output :math:`C` in :math:`cd/m^2` of the ideal
          reference display.

    Warnings
    --------
    *SMPTE ST 2084:2014* is an absolute transfer function.

    Notes
    -----

    +------------+-----------------------+---------------+
    | **Domain** | **Scale - Reference** | **Scale - 1** |
    +============+=======================+===============+
    | ``N``      | [0, 1]                | [0, 1]        |
    +------------+-----------------------+---------------+

    +------------+-----------------------+---------------+
    | **Range**  | **Scale - Reference** | **Scale - 1** |
    +============+=======================+===============+
    | ``C``      | [0, 1]                | [0, 1]        |
    +------------+-----------------------+---------------+

    -   *SMPTE ST 2084:2014* is an absolute transfer function, thus the
        domain and range values for the *Reference* and *1* scales are only
        indicative that the data is not affected by scale transformations.

    References
    ----------
    :cite:`Miller2014a`,
    :cite:`SocietyofMotionPictureandTelevisionEngineers2014a`

    Examples
    --------
    >>> eotf_ST2084(0.508078421517399)  # doctest: +ELLIPSIS
    100.0000000...
    """

    N = to_domain_1(N)

    m_1_d = 1 / constants.m_1
    m_2_d = 1 / constants.m_2

    V_p = spow(N, m_2_d)

    n = V_p - constants.c_1
    # Limiting negative values.
    n = np.where(n < 0, 0, n)

    L = spow((n / (constants.c_2 - constants.c_3 * V_p)), m_1_d)
    C = L_p * L

    return from_range_1(C)
Exemple #55
0
def HSL_to_RGB(HSL: ArrayLike) -> NDArray:
    """
    Convert from *HSL* colourspace to *RGB* colourspace.

    Parameters
    ----------
    HSL
        *HSL* colourspace array.

    Returns
    -------
    :class:`numpy.ndarray`
        *RGB* colourspace array.

    Notes
    -----
    +------------+-----------------------+---------------+
    | **Domain** | **Scale - Reference** | **Scale - 1** |
    +============+=======================+===============+
    | ``HSL``    | [0, 1]                | [0, 1]        |
    +------------+-----------------------+---------------+

    +------------+-----------------------+---------------+
    | **Range**  | **Scale - Reference** | **Scale - 1** |
    +============+=======================+===============+
    | ``RGB``    | [0, 1]                | [0, 1]        |
    +------------+-----------------------+---------------+

    References
    ----------
    :cite:`EasyRGBk`, :cite:`Smith1978b`, :cite:`Wikipedia2003`

    Examples
    --------
    >>> HSL = np.array([0.99603944, 0.87347144, 0.24350795])
    >>> HSL_to_RGB(HSL)  # doctest: +ELLIPSIS
    array([ 0.4562051...,  0.0308107...,  0.0409195...])
    """

    H, S, L = tsplit(to_domain_1(HSL))

    def H_to_RGB(vi: NDArray, vj: NDArray, vH: NDArray) -> NDArray:
        """Convert *hue* value to *RGB* colourspace."""

        vH = as_float_array(vH)

        vH[np.asarray(vH < 0)] += 1
        vH[np.asarray(vH > 1)] -= 1

        v = np.where(
            6 * vH < 1,
            vi + (vj - vi) * 6 * vH,
            np.nan,
        )
        v = np.where(np.logical_and(2 * vH < 1, np.isnan(v)), vj, v)
        v = np.where(
            np.logical_and(3 * vH < 2, np.isnan(v)),
            vi + (vj - vi) * ((2 / 3) - vH) * 6,
            v,
        )
        v = np.where(np.isnan(v), vi, v)

        return v

    j = np.where(L < 0.5, L * (1 + S), (L + S) - (S * L))
    i = 2 * L - j

    R = H_to_RGB(i, j, H + (1 / 3))
    G = H_to_RGB(i, j, H)
    B = H_to_RGB(i, j, H - (1 / 3))

    R = np.where(S == 0, L, R)
    G = np.where(S == 0, L, G)
    B = np.where(S == 0, L, B)

    RGB = tstack([R, G, B])

    return from_range_1(RGB)
Exemple #56
0
def luminance_Fairchild2011(L_hdr, epsilon=0.474, method='hdr-CIELAB'):
    """
    Computes *luminance* :math:`Y` of given *Lightness* :math:`L_{hdr}` using
    *Fairchild and Chen (2011)* method according to *Michealis-Menten*
    kinetics.

    Parameters
    ----------
    L_hdr : array_like
        *Lightness* :math:`L_{hdr}`.
    epsilon : numeric or array_like, optional
        :math:`\\epsilon` exponent.
    method : unicode, optional
        **{'hdr-CIELAB', 'hdr-IPT'}**,
        *Lightness* :math:`L_{hdr}` computation method.

    Returns
    -------
    array_like
        *luminance* :math:`Y`.

    Notes
    -----

    +------------+-----------------------+---------------+
    | **Domain** | **Scale - Reference** | **Scale - 1** |
    +============+=======================+===============+
    | ``L_hdr``  | [0, 100]              | [0, 1]        |
    +------------+-----------------------+---------------+

    +------------+-----------------------+---------------+
    | **Range**  | **Scale - Reference** | **Scale - 1** |
    +============+=======================+===============+
    | ``Y``      | [0, 1]                | [0, 1]        |
    +------------+-----------------------+---------------+

    References
    ----------
    :cite:`Fairchild2011`

    Examples
    --------
    >>> luminance_Fairchild2011(51.852958445912506)  # doctest: +ELLIPSIS
    0.1219722...
    >>> luminance_Fairchild2011(51.643108411718522, method='hdr-IPT')
    ... # doctest: +ELLIPSIS
    0.1219722...
    """

    L_hdr = to_domain_100(L_hdr)

    if method.lower() == 'hdr-cielab':
        maximum_perception = 247
    else:
        maximum_perception = 246

    Y = np.exp(
        np.log(
            substrate_concentration_MichealisMenten(
                L_hdr - 0.02, maximum_perception, 2**epsilon)) / epsilon)

    return from_range_1(Y)
Exemple #57
0
def HCL_to_RGB(HCL: ArrayLike,
               gamma: Floating = 3,
               Y_0: Floating = 100) -> NDArray:
    """
    Convert from *HCL* colourspace to *RGB* colourspace according to
    *Sarifuddin and Missaoui (2005)* method.

    Parameters
    ----------
    HCL
        *HCL* colourspace array.
    gamma
        Non-linear lightness exponent matching *Lightness* :math:`L^*`.
    Y_0
        White reference luminance :math:`Y_0`.

    Returns
    -------
    :class:`numpy.ndarray`
        *RGB* colourspace array.

    Notes
    -----
    +------------+-----------------------+---------------+
    | **Domain** | **Scale - Reference** | **Scale - 1** |
    +============+=======================+===============+
    | ``HCL``    | [0, 1]                | [0, 1]        |
    +------------+-----------------------+---------------+

    +------------+-----------------------+---------------+
    | **Range**  | **Scale - Reference** | **Scale - 1** |
    +============+=======================+===============+
    | ``RGB``    | [0, 1]                | [0, 1]        |
    +------------+-----------------------+---------------+

    References
    ----------
    :cite:`Sarifuddin2005`, :cite:`Wikipedia2015`

    Examples
    --------
    >>> HCL = np.array([-0.03167854, 0.28417150, 0.22859647])
    >>> HCL_to_RGB(HCL)  # doctest: +ELLIPSIS
    array([ 0.4562033...,  0.0308104...,  0.0409192...])
    """

    H, C, L = tsplit(to_domain_1(HCL))

    Q = np.exp((1 - (3 * C) / (4 * L)) * (gamma / Y_0))

    Min = (4 * L - 3 * C) / (4 * Q - 2)
    Max = Min + (3 * C) / (2 * Q)

    def _1_2_3(a: ArrayLike) -> NDArray:
        """Tail-stack given :math:`a` array as a *bool* dtype."""

        return tstack([a, a, a], dtype=np.bool_)

    tan_3_2_H = np.tan(3 / 2 * H)
    tan_3_4_H_MP = np.tan(3 / 4 * (H - np.pi))
    tan_3_4_H = np.tan(3 / 4 * H)
    tan_3_2_H_PP = np.tan(3 / 2 * (H + np.pi))

    RGB = np.select(
        [
            _1_2_3(np.logical_and(0 <= H, H <= np.radians(60))),
            _1_2_3(np.logical_and(np.radians(60) < H, H <= np.radians(120))),
            _1_2_3(np.logical_and(np.radians(120) < H, H <= np.pi)),
            _1_2_3(np.logical_and(np.radians(-60) <= H, H < 0)),
            _1_2_3(np.logical_and(np.radians(-120) <= H, H < np.radians(-60))),
            _1_2_3(np.logical_and(-np.pi < H, H < np.radians(-120))),
        ],
        [
            tstack([
                Max,
                (Max * tan_3_2_H + Min) / (1 + tan_3_2_H),
                Min,
            ]),
            tstack([
                (Max * (1 + tan_3_4_H_MP) - Min) / tan_3_4_H_MP,
                Max,
                Min,
            ]),
            tstack([
                Min,
                Max,
                Max * (1 + tan_3_4_H_MP) - Min * tan_3_4_H_MP,
            ]),
            tstack([
                Max,
                Min,
                Min * (1 + tan_3_4_H) - Max * tan_3_4_H,
            ]),
            tstack([
                (Min * (1 + tan_3_4_H) - Max) / tan_3_4_H,
                Min,
                Max,
            ]),
            tstack([
                Min,
                (Min * tan_3_2_H_PP + Max) / (1 + tan_3_2_H_PP),
                Max,
            ]),
        ],
    )

    return from_range_1(RGB)
Exemple #58
0
def log_decoding_CanonLog(clog,
                          bit_depth=10,
                          in_legal=True,
                          out_reflection=True):
    """
    Defines the *Canon Log* log decoding curve / electro-optical transfer
    function.

    Parameters
    ----------
    clog : numeric or array_like
        *Canon Log* non-linear data.
    bit_depth : int, optional
        Bit depth used for conversion.
    in_legal : bool, optional
        Whether the *Canon Log* non-linear data is encoded in legal
        range.
    out_reflection : bool, optional
        Whether the light level :math:`x` to a camera is reflection.

    Returns
    -------
    numeric or ndarray
        Linear data :math:`x`.

    Notes
    -----

    +------------+-----------------------+---------------+
    | **Domain** | **Scale - Reference** | **Scale - 1** |
    +============+=======================+===============+
    | ``clog``   | [0, 1]                | [0, 1]        |
    +------------+-----------------------+---------------+

    +------------+-----------------------+---------------+
    | **Range**  | **Scale - Reference** | **Scale - 1** |
    +============+=======================+===============+
    | ``x``      | [0, 1]                | [0, 1]        |
    +------------+-----------------------+---------------+

    References
    ----------
    :cite:`Thorpe2012a`

    Examples
    --------
    >>> log_decoding_CanonLog(34.338965172606912 / 100)  # doctest: +ELLIPSIS
    0.17999999...
    """

    clog = to_domain_1(clog)

    clog = legal_to_full(clog, bit_depth) if in_legal else clog

    x = np.where(
        clog < 0.0730597,
        -(10**((0.0730597 - clog) / 0.529136) - 1) / 10.1596,
        (10**((clog - 0.0730597) / 0.529136) - 1) / 10.1596,
    )

    if out_reflection:
        x = x * 0.9

    return as_float(from_range_1(x))
Exemple #59
0
def Luv_to_XYZ(
        Luv,
        illuminant=ILLUMINANTS['CIE 1931 2 Degree Standard Observer']['D65']):
    """
    Converts from *CIE L\\*u\\*v\\** colourspace to *CIE XYZ* tristimulus
    values.

    Parameters
    ----------
    Luv : array_like
        *CIE L\\*u\\*v\\** colourspace array.
    illuminant : array_like, optional
        Reference *illuminant* *xy* chromaticity coordinates or *CIE xyY*
        colourspace array.

    Returns
    -------
    ndarray
        *CIE XYZ* tristimulus values.

    Notes
    -----

    +----------------+-----------------------+-----------------+
    | **Domain**     | **Scale - Reference** | **Scale - 1**   |
    +================+=======================+=================+
    | ``Luv``        | ``L`` : [0, 100]      | ``L`` : [0, 1]  |
    |                |                       |                 |
    |                | ``u`` : [-100, 100]   | ``u`` : [-1, 1] |
    |                |                       |                 |
    |                | ``v`` : [-100, 100]   | ``v`` : [-1, 1] |
    +----------------+-----------------------+-----------------+
    | ``illuminant`` | [0, 1]                | [0, 1]          |
    +----------------+-----------------------+-----------------+

    +----------------+-----------------------+-----------------+
    | **Range**      | **Scale - Reference** | **Scale - 1**   |
    +================+=======================+=================+
    | ``XYZ``        | [0, 1]                | [0, 1]          |
    +----------------+-----------------------+-----------------+

    References
    ----------
    :cite:`CIETC1-482004m`, :cite:`Wikipedia2007b`

    Examples
    --------
    >>> Luv = np.array([41.52787529, 96.83626054, 17.75210149])
    >>> Luv_to_XYZ(Luv)  # doctest: +ELLIPSIS
    array([ 0.2065400...,  0.1219722...,  0.0513695...])
    """

    L, u, v = tsplit(to_domain_100(Luv))

    X_r, Y_r, Z_r = tsplit(xyY_to_XYZ(xy_to_xyY(illuminant)))

    with domain_range_scale('100'):
        Y = luminance_CIE1976(L, Y_r)

    a = 1 / 3 * ((52 * L / (u + 13 * L * (4 * X_r /
                                          (X_r + 15 * Y_r + 3 * Z_r)))) - 1)
    b = -5 * Y
    c = -1 / 3.0
    d = Y * (39 * L / (v + 13 * L * (9 * Y_r /
                                     (X_r + 15 * Y_r + 3 * Z_r))) - 5)

    X = (d - b) / (a - c)
    Z = X * a + b

    XYZ = tstack([X, Y, Z])

    return from_range_1(XYZ)
Exemple #60
0
def log_encoding_CanonLog2(x,
                           bit_depth=10,
                           out_legal=True,
                           in_reflection=True):
    """
    Defines the *Canon Log 2* log encoding curve / opto-electronic transfer
    function.

    Parameters
    ----------
    x : numeric or array_like
        Linear data :math:`x`.
    bit_depth : int, optional
        Bit depth used for conversion.
    out_legal : bool, optional
        Whether the *Canon Log 2* non-linear data is encoded in legal
        range.
    in_reflection : bool, optional
        Whether the light level :math:`x` to a camera is reflection.

    Returns
    -------
    numeric or ndarray
        *Canon Log 2* non-linear data.

    Notes
    -----

    +------------+-----------------------+---------------+
    | **Domain** | **Scale - Reference** | **Scale - 1** |
    +============+=======================+===============+
    | ``x``      | [0, 1]                | [0, 1]        |
    +------------+-----------------------+---------------+

    +------------+-----------------------+---------------+
    | **Range**  | **Scale - Reference** | **Scale - 1** |
    +============+=======================+===============+
    | ``clog2``  | [0, 1]                | [0, 1]        |
    +------------+-----------------------+---------------+

    References
    ----------
    :cite:`Canona`

    Examples
    --------
    >>> log_encoding_CanonLog2(0.18) * 100  # doctest: +ELLIPSIS
    39.8254694...
    """

    x = to_domain_1(x)

    if in_reflection:
        x = x / 0.9

    with domain_range_scale('ignore'):
        clog2 = np.where(
            x < log_decoding_CanonLog2(0.035388128, bit_depth, False),
            -(0.281863093 * (np.log10(-x * 87.09937546 + 1)) - 0.035388128),
            0.281863093 * np.log10(x * 87.09937546 + 1) + 0.035388128,
        )

    clog2 = full_to_legal(clog2, bit_depth) if out_legal else clog2

    return as_float(from_range_1(clog2))