Ejemplo n.º 1
0
def f(x: FloatingOrArrayLike, F_S: FloatingOrArrayLike) -> FloatingOrNDArray:
    """
    Define the nonlinear response function of the *:math:`LLAB(l:c)`* colour
    appearance model used to model the nonlinear behaviour of various visual
    responses.

    Parameters
    ----------
    x
        Visual response variable :math:`x`.
    F_S
        Surround induction factor :math:`F_S`.

    Returns
    -------
    :class:`numpy.floating` or :class:`numpy.ndarray`
        Modeled visual response variable :math:`x`.

    Examples
    --------
    >>> x = np.array([0.23350512, 0.23351103, 0.23355179])
    >>> f(0.200009186234000, 3)  # doctest: +ELLIPSIS
    0.5848125...
    """

    x = as_float_array(x)
    F_S = as_float_array(F_S)

    x_m = np.where(
        x > 0.008856,
        spow(x, 1 / F_S),
        ((spow(0.008856, 1 / F_S) - (16 / 116)) / 0.008856) * x + (16 / 116),
    )

    return as_float(x_m)
Ejemplo n.º 2
0
def f(x, F_S):
    """
    Defines the nonlinear response function of the *:math:`LLAB(l:c)`* colour
    appearance model used to model the nonlinear behaviour of various visual
    responses.

    Parameters
    ----------
    x : numeric or array_like or array_like
        Visual response variable :math:`x`.
    F_S : numeric or array_like
        Surround induction factor :math:`F_S`.

    Returns
    -------
    numeric or array_like
        Modeled visual response variable :math:`x`.

    Examples
    --------
    >>> x = np.array([0.23350512, 0.23351103, 0.23355179])
    >>> f(0.200009186234000, 3)  # doctest: +ELLIPSIS
    array(0.5848125...)
    """

    x = as_float_array(x)
    F_S = as_float_array(F_S)

    x_m = np.where(
        x > 0.008856,
        spow(x, 1 / F_S),
        ((spow(0.008856, 1 / F_S) - (16 / 116)) / 0.008856) * x + (16 / 116),
    )

    return x_m
Ejemplo n.º 3
0
def f(x, F_S):
    """
    Defines the nonlinear response function of the *:math:`LLAB(l:c)`* colour
    appearance model used to model the nonlinear behaviour of various visual
    responses.

    Parameters
    ----------
    x : numeric or array_like or array_like
        Visual response variable :math:`x`.
    F_S : numeric or array_like
        Surround induction factor :math:`F_S`.

    Returns
    -------
    numeric or array_like
        Modeled visual response variable :math:`x`.

    Examples
    --------
    >>> x = np.array([0.23350512, 0.23351103, 0.23355179])
    >>> f(0.200009186234000, 3)  # doctest: +ELLIPSIS
    array(0.5848125...)
    """

    x = as_float_array(x)
    F_S = as_float_array(F_S)

    x_m = np.where(
        x > 0.008856,
        spow(x, 1 / F_S),
        ((spow(0.008856, 1 / F_S) - (16 / 116)) / 0.008856) * x + (16 / 116),
    )

    return x_m
Ejemplo n.º 4
0
def chroma_components(L_star_P, S_RG, S_YB):
    """
    Returns the *chroma* components :math:`C_{RG}` and :math:`C_{YB}`.

    Parameters
    ----------
    L_star_P : numeric or array_like
        *Achromatic Lightness* correlate :math:`L_p^\\star`.
    S_RG : numeric or array_like
        *Saturation* component :math:`S_{RG}`.
    S_YB : numeric or array_like
        *Saturation* component :math:`S_{YB}`.

    Returns
    -------
    ndarray
        *Chroma* components :math:`C_{RG}` and :math:`C_{YB}`.

    Examples
    --------
    >>> L_star_P = 49.99988297570504
    >>> S_RG = -0.002885271638197
    >>> S_YB = -0.013039632941332
    >>> chroma_components(L_star_P, S_RG, S_YB)  # doctest: +ELLIPSIS
    array([-0.00288527, -0.01303961])
    """

    L_star_P = as_float_array(L_star_P)
    S_RG = as_float_array(S_RG)
    S_YB = as_float_array(S_YB)

    C_RG = spow(L_star_P / 50, 0.7) * S_RG
    C_YB = spow(L_star_P / 50, 0.7) * S_YB

    return tstack([C_RG, C_YB])
Ejemplo n.º 5
0
def chroma_components(L_star_P, S_RG, S_YB):
    """
    Returns the *chroma* components :math:`C_{RG}` and :math:`C_{YB}`.

    Parameters
    ----------
    L_star_P : numeric or array_like
        *Achromatic Lightness* correlate :math:`L_p^\\star`.
    S_RG : numeric or array_like
        *Saturation* component :math:`S_{RG}`.
    S_YB : numeric or array_like
        *Saturation* component :math:`S_{YB}`.

    Returns
    -------
    ndarray
        *Chroma* components :math:`C_{RG}` and :math:`C_{YB}`.

    Examples
    --------
    >>> L_star_P = 49.99988297570504
    >>> S_RG = -0.002885271638197
    >>> S_YB = -0.013039632941332
    >>> chroma_components(L_star_P, S_RG, S_YB)  # doctest: +ELLIPSIS
    array([-0.00288527, -0.01303961])
    """

    L_star_P = as_float_array(L_star_P)
    S_RG = as_float_array(S_RG)
    S_YB = as_float_array(S_YB)

    C_RG = spow(L_star_P / 50, 0.7) * S_RG
    C_YB = spow(L_star_P / 50, 0.7) * S_YB

    return tstack([C_RG, C_YB])
Ejemplo n.º 6
0
    def RGB_c(x_1, x_2, y_1, y_2, z):
        """
        Computes the corresponding colour cone responses component.
        """

        return ((Y_o * x_2 + n) * spow(K, 1 / y_2) * spow(
            (z + n) / (Y_o * x_1 + n), y_1 / y_2) - n)
Ejemplo n.º 7
0
    def RGB_c(x_1, x_2, y_1, y_2, z):
        """
        Computes the corresponding colour cone responses component.
        """

        return ((Y_o * x_2 + n) * spow(K, 1 / y_2) * spow(
            (z + n) / (Y_o * x_1 + n), y_1 / y_2) - n)
Ejemplo n.º 8
0
def chroma_correlate(
    J: FloatingOrArrayLike,
    n: FloatingOrArrayLike,
    N_c: FloatingOrArrayLike,
    N_cb: FloatingOrArrayLike,
    e_t: FloatingOrArrayLike,
    a: FloatingOrArrayLike,
    b: FloatingOrArrayLike,
    RGB_a: ArrayLike,
) -> FloatingOrNDArray:
    """
    Return the *chroma* correlate :math:`C`.

    Parameters
    ----------
    J
        *Lightness* correlate :math:`J`.
    n
        Function of the luminance factor of the background :math:`n`.
    N_c
        Surround chromatic induction factor :math:`N_{c}`.
    N_cb
        Chromatic induction factor :math:`N_{cb}`.
    e_t
        Eccentricity factor :math:`e_t`.
    a
        Opponent colour dimension :math:`a`.
    b
        Opponent colour dimension :math:`b`.
    RGB_a
        Compressed stimulus *CMCCAT2000* transform sharpened *RGB* array.

    Returns
    -------
    :class:`numpy.floating` or :class:`numpy.ndarray`
        *Chroma* correlate :math:`C`.

    Examples
    --------
    >>> J = 41.7310911325
    >>> n = 0.2
    >>> N_c = 1.0
    >>> N_cb = 1.00030400456
    >>> e_t = 1.17400547285
    >>> a = -0.000624112068243
    >>> b = -0.000506270106773
    >>> RGB_a = np.array([7.94632020, 7.94711528, 7.94899595])
    >>> chroma_correlate(J, n, N_c, N_cb, e_t, a, b, RGB_a)
    ... # doctest: +ELLIPSIS
    0.1047077...
    """

    J = as_float_array(J)
    n = as_float_array(n)

    t = temporary_magnitude_quantity_forward(N_c, N_cb, e_t, a, b, RGB_a)
    C = spow(t, 0.9) * spow(J / 100, 0.5) * spow(1.64 - 0.29**n, 0.73)

    return C
Ejemplo n.º 9
0
def chromatic_adaptation(
    RGB: ArrayLike,
    RGB_0: ArrayLike,
    RGB_0r: ArrayLike,
    Y: FloatingOrArrayLike,
    D: FloatingOrArrayLike = 1,
) -> NDArray:
    """
    Apply chromatic adaptation to given *RGB* normalised cone responses
    array.

    Parameters
    ----------
    RGB
        *RGB* normalised cone responses array of test sample / stimulus.
    RGB_0
        *RGB* normalised cone responses array of reference white.
    RGB_0r
        *RGB* normalised cone responses array of reference illuminant
        *CIE Standard Illuminant D Series* *D65*.
    Y
        Tristimulus values :math:`Y` of the stimulus.
    D
         *Discounting-the-Illuminant* factor normalised to domain [0, 1].

    Returns
    -------
    :class:`numpy.ndarray`
        Adapted *CIE XYZ* tristimulus values.

    Examples
    --------
    >>> RGB = np.array([0.94142795, 1.04040120, 1.08970885])
    >>> RGB_0 = np.array([0.94146023, 1.04039386, 1.08950293])
    >>> RGB_0r = np.array([0.94146023, 1.04039386, 1.08950293])
    >>> Y = 20.0
    >>> chromatic_adaptation(RGB, RGB_0, RGB_0r, Y)  # doctest: +ELLIPSIS
    array([ 19.01,  20.  ,  21.78])
    """

    R, G, B = tsplit(RGB)
    R_0, G_0, B_0 = tsplit(RGB_0)
    R_0r, G_0r, B_0r = tsplit(RGB_0r)
    Y = as_float_array(Y)
    D = as_float_array(D)

    beta = spow(B_0 / B_0r, 0.0834)

    R_r = (D * (R_0r / R_0) + 1 - D) * R
    G_r = (D * (G_0r / G_0) + 1 - D) * G
    B_r = (D * (B_0r / spow(B_0, beta)) + 1 - D) * spow(B, beta)

    RGB_r = tstack([R_r, G_r, B_r])

    Y = tstack([Y, Y, Y])

    XYZ_r = vector_dot(MATRIX_RGB_TO_XYZ_LLAB, RGB_r * Y)

    return XYZ_r
Ejemplo n.º 10
0
def achromatic_signal(L_AS, S, S_w, N_bb, A_a):
    """
    Returns the achromatic signal :math:`A`.

    Parameters
    ----------
    L_AS : numeric or array_like
        Scotopic luminance :math:`L_{AS}` of the illuminant.
    S : numeric or array_like
        Scotopic response :math:`S` to the stimulus.
    S_w : numeric or array_like
        Scotopic response :math:`S_w` for the reference white.
    N_bb : numeric or array_like
        Brightness background induction factor :math:`N_{bb}`.
    A_a: numeric or array_like
        Achromatic post adaptation signal of the stimulus :math:`A_a`.

    Returns
    -------
    numeric or ndarray
        Achromatic signal :math:`A`.

    Examples
    --------
    >>> L_AS = 769.9376286541402
    >>> S = 20.0
    >>> S_w = 100.0
    >>> N_bb = 0.725000000000000
    >>> A_a = 18.982718664838487
    >>> achromatic_signal(L_AS, S, S_w, N_bb, A_a)  # doctest: +ELLIPSIS
    15.5068546...
    """

    L_AS = as_float_array(L_AS)
    S = as_float_array(S)
    S_w = as_float_array(S_w)
    N_bb = as_float_array(N_bb)
    A_a = as_float_array(A_a)

    j = 0.00001 / ((5 * L_AS / 2.26) + 0.00001)

    # Computing scotopic luminance level adaptation factor :math:`F_{LS}`.
    F_LS = 3800 * (j**2) * (5 * L_AS / 2.26)
    F_LS += 0.2 * (spow(1 - (j**2), 0.4)) * (spow(5 * L_AS / 2.26, 1 / 6))

    # Computing cone bleach factors :math:`B_S`.
    B_S = 0.5 / (1 + 0.3 * spow((5 * L_AS / 2.26) * (S / S_w), 0.3))
    B_S += 0.5 / (1 + 5 * (5 * L_AS / 2.26))

    # Computing adapted scotopic signal :math:`A_S`.
    A_S = (f_n(F_LS * S / S_w) * 3.05 * B_S) + 0.3

    # Computing achromatic signal :math:`A`.
    A = N_bb * (A_a - 1 + A_S - 0.3 + np.sqrt((1 + (0.3**2))))

    return A
Ejemplo n.º 11
0
def lightness_Fairchild2010(
    Y: FloatingOrArrayLike, epsilon: FloatingOrArrayLike = 1.836
) -> FloatingOrNDArray:
    """
    Compute *Lightness* :math:`L_{hdr}` of given *luminance* :math:`Y` using
    *Fairchild and Wyble (2010)* method according to *Michaelis-Menten*
    kinetics.

    Parameters
    ----------
    Y
        *Luminance* :math:`Y`.
    epsilon
        :math:`\\epsilon` exponent.

    Returns
    -------
    :class:`numpy.floating` or :class:`numpy.ndarray`
        *Lightness* :math:`L_{hdr}`.

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

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

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

    Examples
    --------
    >>> lightness_Fairchild2010(12.19722535 / 100)  # doctest: +ELLIPSIS
    31.9963902...
    """

    Y = to_domain_1(Y)

    maximum_perception = 100

    L_hdr = (
        reaction_rate_MichaelisMenten_Michaelis1913(
            spow(Y, epsilon), maximum_perception, spow(0.184, epsilon)
        )
        + 0.02
    )

    return as_float(from_range_100(L_hdr))
Ejemplo n.º 12
0
def K_coefficient(xez_1, xez_2, bRGB_o1, bRGB_o2, Y_o, n=1):
    """
    Computes the coefficient :math:`K` for correcting the difference between
    the test and references illuminances.

    Parameters
    ----------
    xez_1: array_like
        Intermediate values :math:`\\xi_1`, :math:`\\eta_1`, :math:`\\zeta_1`
        for the test illuminant and background.
    xez_2: array_like
        Intermediate values :math:`\\xi_2`, :math:`\\eta_2`, :math:`\\zeta_2`
        for the reference illuminant and background.
    bRGB_o1: array_like
        Chromatic adaptation exponential factors :math:`\\beta_1(R_{o1})`,
        :math:`\\beta_1(G_{o1})` and :math:`\\beta_2(B_{o1})` of test sample.
    bRGB_o2: array_like
        Chromatic adaptation exponential factors :math:`\\beta_1(R_{o2})`,
        :math:`\\beta_1(G_{o2})` and :math:`\\beta_2(B_{o2})` of reference
        sample.
    Y_o : numeric or array_like
        Luminance factor :math:`Y_o` of achromatic background as percentage
        normalised to domain [18, 100] in **'Reference'** domain-range scale.
    n : numeric or array_like, optional
        Noise component in fundamental primary system.

    Returns
    -------
    numeric or array_like
        Coefficient :math:`K`.

    Examples
    --------
    >>> xez_1 = np.array([1.11857195, 0.93295530, 0.32680879])
    >>> xez_2 = np.array([1.00000372, 1.00000176, 0.99999461])
    >>> bRGB_o1 = np.array([3.74852518, 3.63920879, 2.78924811])
    >>> bRGB_o2 = np.array([3.68102374, 3.68102256, 3.56557351])
    >>> Y_o = 20
    >>> K_coefficient(xez_1, xez_2, bRGB_o1, bRGB_o2, Y_o)
    1.0
    """

    xi_1, eta_1, _zeta_1 = tsplit(xez_1)
    xi_2, eta_2, _zeta_2 = tsplit(xez_2)
    bR_o1, bG_o1, _bB_o1 = tsplit(bRGB_o1)
    bR_o2, bG_o2, _bB_o2 = tsplit(bRGB_o2)
    Y_o = as_float_array(Y_o)

    K = (spow((Y_o * xi_1 + n) / (20 * xi_1 + n), (2 / 3) * bR_o1) / spow(
        (Y_o * xi_2 + n) / (20 * xi_2 + n), (2 / 3) * bR_o2))

    K *= (spow((Y_o * eta_1 + n) / (20 * eta_1 + n), (1 / 3) * bG_o1) / spow(
        (Y_o * eta_2 + n) / (20 * eta_2 + n), (1 / 3) * bG_o2))

    return K
Ejemplo n.º 13
0
def K_coefficient(xez_1, xez_2, bRGB_o1, bRGB_o2, Y_o, n=1):
    """
    Computes the coefficient :math:`K` for correcting the difference between
    the test and references illuminances.

    Parameters
    ----------
    xez_1: array_like
        Intermediate values :math:`\\xi_1`, :math:`\\eta_1`, :math:`\\zeta_1`
        for the test illuminant and background.
    xez_2: array_like
        Intermediate values :math:`\\xi_2`, :math:`\\eta_2`, :math:`\\zeta_2`
        for the reference illuminant and background.
    bRGB_o1: array_like
        Chromatic adaptation exponential factors :math:`\\beta_1(R_{o1})`,
        :math:`\\beta_1(G_{o1})` and :math:`\\beta_2(B_{o1})` of test sample.
    bRGB_o2: array_like
        Chromatic adaptation exponential factors :math:`\\beta_1(R_{o2})`,
        :math:`\\beta_1(G_{o2})` and :math:`\\beta_2(B_{o2})` of reference
        sample.
    Y_o : numeric or array_like
        Luminance factor :math:`Y_o` of achromatic background as percentage
        normalised to domain [18, 100] in **'Reference'** domain-range scale.
    n : numeric or array_like, optional
        Noise component in fundamental primary system.

    Returns
    -------
    numeric or array_like
        Coefficient :math:`K`.

    Examples
    --------
    >>> xez_1 = np.array([1.11857195, 0.93295530, 0.32680879])
    >>> xez_2 = np.array([1.00000372, 1.00000176, 0.99999461])
    >>> bRGB_o1 = np.array([3.74852518, 3.63920879, 2.78924811])
    >>> bRGB_o2 = np.array([3.68102374, 3.68102256, 3.56557351])
    >>> Y_o = 20
    >>> K_coefficient(xez_1, xez_2, bRGB_o1, bRGB_o2, Y_o)
    1.0
    """

    xi_1, eta_1, _zeta_1 = tsplit(xez_1)
    xi_2, eta_2, _zeta_2 = tsplit(xez_2)
    bR_o1, bG_o1, _bB_o1 = tsplit(bRGB_o1)
    bR_o2, bG_o2, _bB_o2 = tsplit(bRGB_o2)
    Y_o = as_float_array(Y_o)

    K = (spow((Y_o * xi_1 + n) / (20 * xi_1 + n), (2 / 3) * bR_o1) / spow(
        (Y_o * xi_2 + n) / (20 * xi_2 + n), (2 / 3) * bR_o2))

    K *= (spow((Y_o * eta_1 + n) / (20 * eta_1 + n), (1 / 3) * bG_o1) / spow(
        (Y_o * eta_2 + n) / (20 * eta_2 + n), (1 / 3) * bG_o2))

    return K
Ejemplo n.º 14
0
def opponent_colour_dimensions(
    XYZ: ArrayLike,
    Y_b: FloatingOrArrayLike,
    F_S: FloatingOrArrayLike,
    F_L: FloatingOrArrayLike,
) -> NDArray:
    """
    Return opponent colour dimensions from given adapted *CIE XYZ* tristimulus
    values.

    The opponent colour dimensions are based on a modified *CIE L\\*a\\*b\\**
    colourspace formulae.

    Parameters
    ----------
    XYZ
        Adapted *CIE XYZ* tristimulus values.
    Y_b
        Luminance factor of the background in :math:`cd/m^2`.
    F_S
        Surround induction factor :math:`F_S`.
    F_L
        Lightness induction factor :math:`F_L`.

    Returns
    -------
    :class:`numpy.ndarray`
        Opponent colour dimensions.

    Examples
    --------
    >>> XYZ = np.array([19.00999572, 20.00091862, 21.77993863])
    >>> Y_b = 20.0
    >>> F_S = 3.0
    >>> F_L = 1.0
    >>> opponent_colour_dimensions(XYZ, Y_b, F_S, F_L)  # doctest: +ELLIPSIS
    array([  3.7368047...e+01,  -4.4986443...e-03,  -5.2604647...e-03])
    """

    X, Y, Z = tsplit(XYZ)
    Y_b = as_float_array(Y_b)
    F_S = as_float_array(F_S)
    F_L = as_float_array(F_L)

    # Account for background lightness contrast.
    z = 1 + F_L * spow(Y_b / 100, 0.5)

    # Computing modified *CIE L\\*a\\*b\\** colourspace array.
    L = 116 * spow(f(Y / 100, F_S), z) - 16
    a = 500 * (f(X / 95.05, F_S) - f(Y / 100, F_S))
    b = 200 * (f(Y / 100, F_S) - f(Z / 108.88, F_S))

    Lab = tstack([L, a, b])

    return Lab
Ejemplo n.º 15
0
    def RGB_c(
        x_1: NDArray,
        x_2: NDArray,
        y_1: NDArray,
        y_2: NDArray,
        z: NDArray,
        n: NDArray,
    ) -> NDArray:
        """Compute the corresponding colour cone responses component."""

        return (Y_o * x_2 + n) * spow(K, 1 / y_2) * spow(
            (z + n) / (Y_o * x_1 + n), y_1 / y_2) - n
Ejemplo n.º 16
0
def adjusted_reference_white_signals(
    rgb_p: ArrayLike,
    rgb_b: ArrayLike,
    rgb_w: ArrayLike,
    p: FloatingOrArrayLike,
) -> NDArray:
    """
    Adjust the white point for simultaneous chromatic contrast.

    Parameters
    ----------
    rgb_p
        Cone signals *Hunt-Pointer-Estevez* :math:`\\rho\\gamma\\beta`
        colourspace array of the proximal field.
    rgb_b
        Cone signals *Hunt-Pointer-Estevez* :math:`\\rho\\gamma\\beta`
        colourspace array of the background.
    rgb_w
        Cone signals array *Hunt-Pointer-Estevez* :math:`\\rho\\gamma\\beta`
        colourspace array of the reference white.
    p
        Simultaneous contrast / assimilation factor :math:`p` with value
        normalised to domain [-1, 0] when simultaneous contrast occurs and
        normalised to domain [0, 1] when assimilation occurs.

    Returns
    -------
    :class:`numpy.ndarray`
        Adjusted cone signals *Hunt-Pointer-Estevez* :math:`\\rho\\gamma\\beta`
        colourspace array of the reference white.

    Examples
    --------
    >>> rgb_p = np.array([98.07193550, 101.13755950, 100.00000000])
    >>> rgb_b = np.array([0.99984505, 0.99983840, 0.99982674])
    >>> rgb_w = np.array([97.37325710, 101.54968030, 108.88000000])
    >>> p = 0.1
    >>> adjusted_reference_white_signals(rgb_p, rgb_b, rgb_w, p)
    ... # doctest: +ELLIPSIS
    array([ 88.0792742...,  91.8569553...,  98.4876543...])
    """

    rgb_p = as_float_array(rgb_p)
    rgb_b = as_float_array(rgb_b)
    rgb_w = as_float_array(rgb_w)
    p = as_float_array(p)

    p_rgb = rgb_p / rgb_b
    rgb_w = (rgb_w * (spow((1 - p) * p_rgb + (1 + p) / p_rgb, 0.5)) / (spow(
        (1 + p) * p_rgb + (1 - p) / p_rgb, 0.5)))

    return rgb_w
Ejemplo n.º 17
0
def chromatic_adaptation(RGB, RGB_0, RGB_0r, Y, D=1):
    """
    Applies chromatic adaptation to given *RGB* normalised cone responses
    array.

    Parameters
    ----------
    RGB : array_like
        *RGB* normalised cone responses array of test sample / stimulus.
    RGB_0 : array_like
        *RGB* normalised cone responses array of reference white.
    RGB_0r : array_like
        *RGB* normalised cone responses array of reference illuminant
        *CIE Standard Illuminant D Series* *D65*.
    Y : numeric or array_like
        Tristimulus values :math:`Y` of the stimulus.
    D : numeric or array_like, optional
         *Discounting-the-Illuminant* factor normalised to domain [0, 1].

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

    Examples
    --------
    >>> RGB = np.array([0.94142795, 1.04040120, 1.08970885])
    >>> RGB_0 = np.array([0.94146023, 1.04039386, 1.08950293])
    >>> RGB_0r = np.array([0.94146023, 1.04039386, 1.08950293])
    >>> Y = 20.0
    >>> chromatic_adaptation(RGB, RGB_0, RGB_0r, Y)  # doctest: +ELLIPSIS
    array([ 19.01,  20.  ,  21.78])
    """

    R, G, B = tsplit(RGB)
    R_0, G_0, B_0 = tsplit(RGB_0)
    R_0r, G_0r, B_0r = tsplit(RGB_0r)
    Y = as_float_array(Y)

    beta = spow(B_0 / B_0r, 0.0834)

    R_r = (D * (R_0r / R_0) + 1 - D) * R
    G_r = (D * (G_0r / G_0) + 1 - D) * G
    B_r = (D * (B_0r / spow(B_0, beta)) + 1 - D) * spow(B, beta)

    RGB_r = tstack([R_r, G_r, B_r])

    Y = tstack([Y, Y, Y])

    XYZ_r = dot_vector(LLAB_RGB_TO_XYZ_MATRIX, RGB_r * Y)

    return XYZ_r
Ejemplo n.º 18
0
Archivo: llab.py Proyecto: yixw/colour
def chromatic_adaptation(RGB, RGB_0, RGB_0r, Y, D=1):
    """
    Applies chromatic adaptation to given *RGB* normalised cone responses
    array.

    Parameters
    ----------
    RGB : array_like
        *RGB* normalised cone responses array of test sample / stimulus.
    RGB_0 : array_like
        *RGB* normalised cone responses array of reference white.
    RGB_0r : array_like
        *RGB* normalised cone responses array of reference illuminant
        *CIE Standard Illuminant D Series* *D65*.
    Y : numeric or array_like
        Tristimulus values :math:`Y` of the stimulus.
    D : numeric or array_like, optional
         *Discounting-the-Illuminant* factor normalised to domain [0, 1].

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

    Examples
    --------
    >>> RGB = np.array([0.94142795, 1.04040120, 1.08970885])
    >>> RGB_0 = np.array([0.94146023, 1.04039386, 1.08950293])
    >>> RGB_0r = np.array([0.94146023, 1.04039386, 1.08950293])
    >>> Y = 20.0
    >>> chromatic_adaptation(RGB, RGB_0, RGB_0r, Y)  # doctest: +ELLIPSIS
    array([ 19.01,  20.  ,  21.78])
    """

    R, G, B = tsplit(RGB)
    R_0, G_0, B_0 = tsplit(RGB_0)
    R_0r, G_0r, B_0r = tsplit(RGB_0r)
    Y = as_float_array(Y)

    beta = spow(B_0 / B_0r, 0.0834)

    R_r = (D * (R_0r / R_0) + 1 - D) * R
    G_r = (D * (G_0r / G_0) + 1 - D) * G
    B_r = (D * (B_0r / spow(B_0, beta)) + 1 - D) * spow(B, beta)

    RGB_r = tstack([R_r, G_r, B_r])

    Y = tstack([Y, Y, Y])

    XYZ_r = dot_vector(LLAB_RGB_TO_XYZ_MATRIX, RGB_r * Y)

    return XYZ_r
Ejemplo n.º 19
0
def opponent_colour_dimensions(XYZ, Y_b, F_S, F_L):
    """
    Returns opponent colour dimensions from given adapted *CIE XYZ* tristimulus
    values.

    The opponent colour dimensions are based on a modified *CIE L\\*a\\*b\\**
    colourspace formulae.

    Parameters
    ----------
    XYZ : array_like
        Adapted *CIE XYZ* tristimulus values.
    Y_b : numeric or array_like
        Luminance factor of the background in :math:`cd/m^2`.
    F_S : numeric or array_like
        Surround induction factor :math:`F_S`.
    F_L : numeric or array_like
        Lightness induction factor :math:`F_L`.

    Returns
    -------
    ndarray
        Opponent colour dimensions.

    Examples
    --------
    >>> XYZ = np.array([19.00999572, 20.00091862, 21.77993863])
    >>> Y_b = 20.0
    >>> F_S = 3.0
    >>> F_L = 1.0
    >>> opponent_colour_dimensions(XYZ, Y_b, F_S, F_L)  # doctest: +ELLIPSIS
    array([  3.7368047...e+01,  -4.4986443...e-03,  -5.2604647...e-03])
    """

    X, Y, Z = tsplit(XYZ)
    Y_b = as_float_array(Y_b)
    F_S = as_float_array(F_S)
    F_L = as_float_array(F_L)

    # Account for background lightness contrast.
    z = 1 + F_L * spow(Y_b / 100, 0.5)

    # Computing modified *CIE L\\*a\\*b\\** colourspace array.
    L = 116 * spow(f(Y / 100, F_S), z) - 16
    a = 500 * (f(X / 95.05, F_S) - f(Y / 100, F_S))
    b = 200 * (f(Y / 100, F_S) - f(Z / 108.88, F_S))

    Lab = tstack([L, a, b])

    return Lab
Ejemplo n.º 20
0
def chroma_correlate(J, n, N_c, N_cb, e_t, a, b, RGB_a):
    """
    Returns the *chroma* correlate :math:`C`.

    Parameters
    ----------
    J : numeric or array_like
        *Lightness* correlate :math:`J`.
    n : numeric or array_like
        Function of the luminance factor of the background :math:`n`.
    N_c : numeric or array_like
        Surround chromatic induction factor :math:`N_{c}`.
    N_cb : numeric or array_like
        Chromatic induction factor :math:`N_{cb}`.
    e_t : numeric or array_like
        Eccentricity factor :math:`e_t`.
    a : numeric or array_like
        Opponent colour dimension :math:`a`.
    b : numeric or array_like
        Opponent colour dimension :math:`b`.
    RGB_a : array_like
        Compressed stimulus *CMCCAT2000* transform sharpened *RGB* array.

    Returns
    -------
    numeric or ndarray
        *Chroma* correlate :math:`C`.

    Examples
    --------
    >>> J = 41.7310911325
    >>> n = 0.2
    >>> N_c = 1.0
    >>> N_cb = 1.00030400456
    >>> e_t = 1.17400547285
    >>> a = -0.000624112068243
    >>> b = -0.000506270106773
    >>> RGB_a = np.array([7.94632020, 7.94711528, 7.94899595])
    >>> chroma_correlate(J, n, N_c, N_cb, e_t, a, b, RGB_a)
    ... # doctest: +ELLIPSIS
    0.1047077...
    """

    J = as_float_array(J)
    n = as_float_array(n)

    t = temporary_magnitude_quantity_forward(N_c, N_cb, e_t, a, b, RGB_a)
    C = spow(t, 0.9) * spow(J / 100, 0.5) * spow(1.64 - 0.29 ** n, 0.73)

    return C
Ejemplo n.º 21
0
def chroma_correlate(
    s: FloatingOrArrayLike,
    Y_b: FloatingOrArrayLike,
    Y_w: FloatingOrArrayLike,
    Q: FloatingOrArrayLike,
    Q_w: FloatingOrArrayLike,
) -> FloatingOrNDArray:
    """
    Return the *chroma* correlate :math:`C_94`.

    Parameters
    ----------
    s
        *Saturation* correlate :math:`s`.
    Y_b
         Tristimulus values :math:`Y_b` the background.
    Y_w
         Tristimulus values :math:`Y_b` the reference white.
    Q
        *Brightness* correlate :math:`Q` of the stimulus.
    Q_w
        *Brightness* correlate :math:`Q` of the reference white.

    Returns
    -------
    :class:`numpy.floating` or :class:`numpy.ndarray`
        *Chroma* correlate :math:`C_94`.

    Examples
    --------
    >>> s = 0.0199093206929
    >>> Y_b = 100.0
    >>> Y_w = 100.0
    >>> Q = 22.209765491265024
    >>> Q_w = 40.518065821226081
    >>> chroma_correlate(s, Y_b, Y_w, Q, Q_w)  # doctest: +ELLIPSIS
    0.1210508...
    """

    s = as_float_array(s)
    Y_b = as_float_array(Y_b)
    Y_w = as_float_array(Y_w)
    Q = as_float_array(Q)
    Q_w = as_float_array(Q_w)

    C_94 = (2.44 * spow(s, 0.69) * (spow(Q / Q_w, Y_b / Y_w)) *
            (1.64 - spow(0.29, Y_b / Y_w)))

    return C_94
Ejemplo n.º 22
0
def chroma_correlate(a, b):
    """
    Returns the correlate of *chroma* :math:`Ch_L`.

    Parameters
    ----------
    a : numeric or array_like
        Opponent colour dimension :math:`a`.
    b : numeric or array_like
        Opponent colour dimension :math:`b`.

    Returns
    -------
    numeric or ndarray
        Correlate of *chroma* :math:`Ch_L`.

    Examples
    --------
    >>> a = -4.49864756e-03
    >>> b = -5.26046353e-03
    >>> chroma_correlate(a, b)  # doctest: +ELLIPSIS
    0.0086506...
    """

    a = as_float_array(a)
    b = as_float_array(b)

    c = spow(a ** 2 + b ** 2, 0.5)
    Ch_L = 25 * np.log(1 + 0.05 * c)

    return Ch_L
Ejemplo n.º 23
0
def saturation_correlate(M, Q):
    """
    Returns the *saturation* correlate :math:`s`.

    Parameters
    ----------
    M : numeric or array_like
        *Colourfulness* correlate :math:`M`.
    Q : numeric or array_like
        *Brightness* correlate :math:`C`.

    Returns
    -------
    numeric or ndarray
        *Saturation* correlate :math:`s`.

    Examples
    --------
    >>> M = 0.108842175669
    >>> Q = 195.371325966
    >>> saturation_correlate(M, Q)  # doctest: +ELLIPSIS
    2.3603053...
    """

    M = as_float_array(M)
    Q = as_float_array(Q)

    s = 100 * spow(M / Q, 0.5)

    return s
Ejemplo n.º 24
0
def luminance_to_retinal_illuminance(XYZ, Y_c):
    """
    Converts from luminance in :math:`cd/m^2` to retinal illuminance in
    trolands.

    Parameters
    ----------
    XYZ : array_like
        *CIE XYZ* tristimulus values.
    Y_c : numeric or array_like
        Absolute adapting field luminance in :math:`cd/m^2`.

    Returns
    -------
    ndarray
        Converted *CIE XYZ* tristimulus values in trolands.

    Examples
    --------
    >>> XYZ = np.array([19.01, 20.00, 21.78])
    >>> Y_0 = 318.31
    >>> luminance_to_retinal_illuminance(XYZ, Y_0)  # doctest: +ELLIPSIS
    array([ 479.4445924...,  499.3174313...,  534.5631673...])
    """

    XYZ = as_float_array(XYZ)
    Y_c = as_float_array(Y_c)

    return 18 * spow(Y_c[..., np.newaxis] * XYZ / 100, 0.8)
Ejemplo n.º 25
0
def chromatic_induction_factors(n):
    """
    Returns the chromatic induction factors :math:`N_{bb}` and :math:`N_{cb}`.

    Parameters
    ----------
    n : numeric or array_like
        Function of the luminance factor of the background :math:`n`.

    Returns
    -------
    ndarray
        Chromatic induction factors :math:`N_{bb}` and :math:`N_{cb}`.

    Examples
    --------
    >>> chromatic_induction_factors(0.2)  # doctest: +ELLIPSIS
    array([ 1.000304,  1.000304])
    """

    n = as_float_array(n)

    N_bb = N_cb = 0.725 * spow(1 / n, 0.2)
    N_bbcb = tstack([N_bb, N_cb])

    return N_bbcb
Ejemplo n.º 26
0
def XYZ_to_LMS_ATD95(XYZ):
    """
    Converts from *CIE XYZ* tristimulus values to *LMS* cone responses.

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

    Returns
    -------
    ndarray
        *LMS* cone responses.

    Examples
    --------
    >>> XYZ = np.array([19.01, 20.00, 21.78])
    >>> XYZ_to_LMS_ATD95(XYZ)  # doctest: +ELLIPSIS
    array([ 6.2283272...,  7.4780666...,  3.8859772...])
    """

    LMS = vector_dot([
        [0.2435, 0.8524, -0.0516],
        [-0.3954, 1.1642, 0.0837],
        [0.0000, 0.0400, 0.6225],
    ], XYZ)

    LMS *= np.array([0.66, 1.0, 0.43])
    LMS = spow(LMS, 0.7)
    LMS += np.array([0.024, 0.036, 0.31])

    return LMS
Ejemplo n.º 27
0
def colourfulness_correlate(C, F_L):
    """
    Returns the *colourfulness* correlate :math:`M`.

    Parameters
    ----------
    C : numeric or array_like
        *Chroma* correlate :math:`C`.
    F_L : numeric or array_like
        *Luminance* level adaptation factor :math:`F_L`.

    Returns
    -------
    numeric or ndarray
        *Colourfulness* correlate :math:`M`.

    Examples
    --------
    >>> C = 0.104707757171
    >>> F_L = 1.16754446415
    >>> colourfulness_correlate(C, F_L)  # doctest: +ELLIPSIS
    0.1088421...
    """

    C = as_float_array(C)
    F_L = as_float_array(F_L)

    M = C * spow(F_L, 0.25)

    return M
Ejemplo n.º 28
0
def luminance_to_retinal_illuminance(XYZ, Y_c):
    """
    Converts from luminance in :math:`cd/m^2` to retinal illuminance in
    trolands.

    Parameters
    ----------
    XYZ : array_like
        *CIE XYZ* tristimulus values.
    Y_c : numeric or array_like
        Absolute adapting field luminance in :math:`cd/m^2`.

    Returns
    -------
    ndarray
        Converted *CIE XYZ* tristimulus values in trolands.

    Examples
    --------
    >>> XYZ = np.array([19.01, 20.00, 21.78])
    >>> Y_0 = 318.31
    >>> luminance_to_retinal_illuminance(XYZ, Y_0)  # doctest: +ELLIPSIS
    array([ 479.4445924...,  499.3174313...,  534.5631673...])
    """

    XYZ = as_float_array(XYZ)
    Y_c = as_float_array(Y_c)

    return 18 * spow(Y_c[..., np.newaxis] * XYZ / 100, 0.8)
Ejemplo n.º 29
0
def post_adaptation_non_linear_response_compression_reverse(RGB, F_L):
    """
    Returns given *CMCCAT2000* transform sharpened *RGB* array without post
    adaptation non linear response compression.

    Parameters
    ----------
    RGB : array_like
        *CMCCAT2000* transform sharpened *RGB* array.
    F_L : array_like
        *Luminance* level adaptation factor :math:`F_L`.

    Returns
    -------
    ndarray
        Uncompressed *CMCCAT2000* transform sharpened *RGB* array.

    Examples
    --------
    >>> RGB = np.array([7.94632020, 7.94711528, 7.94899595])
    >>> F_L = 1.16754446415
    >>> post_adaptation_non_linear_response_compression_reverse(RGB, F_L)
    ... # doctest: +ELLIPSIS
    array([ 19.9969397...,  20.0018612...,  20.0135052...])
    """

    RGB = as_float_array(RGB)
    F_L = as_float_array(F_L)

    RGB_p = ((np.sign(RGB - 0.1) * (100 / F_L[..., np.newaxis]) * spow(
        (27.13 * np.absolute(RGB - 0.1)) / (400 - np.absolute(RGB - 0.1)),
        1 / 0.42)))

    return RGB_p
Ejemplo n.º 30
0
def chroma_correlate(L_star_P, S):
    """
    Returns the correlate of *chroma* :math:`C`.

    Parameters
    ----------
    L_star_P : numeric or array_like
        *Achromatic Lightness* correlate :math:`L_p^\\star`.
    S : numeric or array_like
        Correlate of *saturation* :math:`S`.

    Returns
    -------
    numeric or ndarray
        Correlate of *chroma* :math:`C`.

    Examples
    --------
    >>> L_star_P = 49.99988297570504
    >>> S = 0.013355029751778
    >>> chroma_correlate(L_star_P, S)  # doctest: +ELLIPSIS
    0.0133550...
    """

    L_star_P = as_float_array(L_star_P)
    S = as_float_array(S)

    C = spow(L_star_P / 50, 0.7) * S

    return C
Ejemplo n.º 31
0
def chromatic_induction_factors(n):
    """
    Returns the chromatic induction factors :math:`N_{bb}` and :math:`N_{cb}`.

    Parameters
    ----------
    n : numeric or array_like
        Function of the luminance factor of the background :math:`n`.

    Returns
    -------
    ndarray
        Chromatic induction factors :math:`N_{bb}` and :math:`N_{cb}`.

    Examples
    --------
    >>> chromatic_induction_factors(0.2)  # doctest: +ELLIPSIS
    array([ 1.000304,  1.000304])
    """

    n = as_float_array(n)

    N_bb = N_cb = 0.725 * spow(1 / n, 0.2)
    N_bbcb = tstack([N_bb, N_cb])

    return N_bbcb
Ejemplo n.º 32
0
def XYZ_to_LMS_ATD95(XYZ):
    """
    Converts from *CIE XYZ* tristimulus values to *LMS* cone responses.

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

    Returns
    -------
    ndarray
        *LMS* cone responses.

    Examples
    --------
    >>> XYZ = np.array([19.01, 20.00, 21.78])
    >>> XYZ_to_LMS_ATD95(XYZ)  # doctest: +ELLIPSIS
    array([ 6.2283272...,  7.4780666...,  3.8859772...])
    """

    LMS = dot_vector([
        [0.2435, 0.8524, -0.0516],
        [-0.3954, 1.1642, 0.0837],
        [0.0000, 0.0400, 0.6225],
    ], XYZ)

    LMS *= np.array([0.66, 1.0, 0.43])
    LMS = spow(LMS, 0.7)
    LMS += np.array([0.024, 0.036, 0.31])

    return LMS
Ejemplo n.º 33
0
def saturation_correlate(M, Q):
    """
    Returns the *saturation* correlate :math:`s`.

    Parameters
    ----------
    M : numeric or array_like
        *Colourfulness* correlate :math:`M`.
    Q : numeric or array_like
        *Brightness* correlate :math:`C`.

    Returns
    -------
    numeric or ndarray
        *Saturation* correlate :math:`s`.

    Examples
    --------
    >>> M = 0.108842175669
    >>> Q = 195.371325966
    >>> saturation_correlate(M, Q)  # doctest: +ELLIPSIS
    2.3603053...
    """

    M = as_float_array(M)
    Q = as_float_array(Q)

    s = 100 * spow(M / Q, 0.5)

    return s
Ejemplo n.º 34
0
def saturation_correlate(M: FloatingOrArrayLike,
                         Q: FloatingOrArrayLike) -> FloatingOrNDArray:
    """
    Return the *saturation* correlate :math:`s`.

    Parameters
    ----------
    M
        *Colourfulness* correlate :math:`M`.
    Q
        *Brightness* correlate :math:`C`.

    Returns
    -------
    :class:`numpy.floating` or :class:`numpy.ndarray`
        *Saturation* correlate :math:`s`.

    Examples
    --------
    >>> M = 0.108842175669
    >>> Q = 195.371325966
    >>> saturation_correlate(M, Q)  # doctest: +ELLIPSIS
    2.3603053...
    """

    M = as_float_array(M)
    Q = as_float_array(Q)

    s = 100 * spow(M / Q, 0.5)

    return s
Ejemplo n.º 35
0
def chroma_correlate(L_star_P, S):
    """
    Returns the correlate of *chroma* :math:`C`.

    Parameters
    ----------
    L_star_P : numeric or array_like
        *Achromatic Lightness* correlate :math:`L_p^\\star`.
    S : numeric or array_like
        Correlate of *saturation* :math:`S`.

    Returns
    -------
    numeric or ndarray
        Correlate of *chroma* :math:`C`.

    Examples
    --------
    >>> L_star_P = 49.99988297570504
    >>> S = 0.013355029751778
    >>> chroma_correlate(L_star_P, S)  # doctest: +ELLIPSIS
    0.0133550...
    """

    L_star_P = as_float_array(L_star_P)
    S = as_float_array(S)

    C = spow(L_star_P / 50, 0.7) * S

    return C
Ejemplo n.º 36
0
def lightness_Fairchild2011(Y, epsilon=0.474, method='hdr-CIELAB'):
    """
    Computes *Lightness* :math:`L_{hdr}` of given *luminance* :math:`Y` using
    *Fairchild and Chen (2011)* method according to *Michealis-Menten*
    kinetics.

    Parameters
    ----------
    Y : array_like
        *luminance* :math:`Y`.
    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
        *Lightness* :math:`L_{hdr}`.

    Notes
    -----

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

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

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

    Examples
    --------
    >>> lightness_Fairchild2011(12.19722535 / 100)  # doctest: +ELLIPSIS
    51.8529584...
    >>> lightness_Fairchild2011(12.19722535 / 100, method='hdr-IPT')
    ... # doctest: +ELLIPSIS
    51.6431084...
    """

    Y = to_domain_1(Y)

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

    L_hdr = reaction_rate_MichealisMenten(
        spow(Y, epsilon), maximum_perception, 2 ** epsilon) + 0.02

    return from_range_100(L_hdr)
Ejemplo n.º 37
0
def intermediate_lightness_function_CIE1976(Y, Y_n=100):
    """
    Returns the intermediate value :math:`f(Y/Yn)` in the *Lightness*
    :math:`L^*` computation for given *luminance* :math:`Y` using given
    reference white *luminance* :math:`Y_n` as per *CIE 1976* recommendation.

    Parameters
    ----------
    Y : numeric or array_like
        *luminance* :math:`Y`.
    Y_n : numeric or array_like, optional
        White reference *luminance* :math:`Y_n`.

    Returns
    -------
    numeric or array_like
        Intermediate value :math:`f(Y/Yn)`.

    Notes
    -----

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

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

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

    Examples
    --------
    >>> intermediate_lightness_function_CIE1976(12.19722535)
    ... # doctest: +ELLIPSIS
    0.4959299...
    >>> intermediate_lightness_function_CIE1976(12.19722535, 95)
    ... # doctest: +ELLIPSIS
    0.5044821...
    """

    Y = as_float_array(Y)
    Y_n = as_float_array(Y_n)

    Y_Y_n = Y / Y_n

    f_Y_Y_n = as_float(
        np.where(
            Y_Y_n > (24 / 116) ** 3,
            spow(Y_Y_n, 1 / 3),
            (841 / 108) * Y_Y_n + 16 / 116,
        ))

    return f_Y_Y_n
Ejemplo n.º 38
0
def colourfulness_correlate(F_L, C_94):
    """
    Returns the *colourfulness* correlate :math:`M_94`.

    Parameters
    ----------
    F_L : numeric or array_like
        Luminance adaptation factor :math:`F_L`.
    C_94 : numeric
        *Chroma* correlate :math:`C_94`.

    Returns
    -------
    numeric
        *Colourfulness* correlate :math:`M_94`.

    Examples
    --------
    >>> F_L = 1.16754446414718
    >>> C_94 = 0.121050839936176
    >>> colourfulness_correlate(F_L, C_94)  # doctest: +ELLIPSIS
    0.1238964...
    """

    F_L = as_float_array(F_L)
    C_94 = as_float_array(C_94)

    M_94 = spow(F_L, 0.15) * C_94

    return M_94
Ejemplo n.º 39
0
def post_adaptation_non_linear_response_compression_reverse(RGB, F_L):
    """
    Returns given *CMCCAT2000* transform sharpened *RGB* array without post
    adaptation non linear response compression.

    Parameters
    ----------
    RGB : array_like
        *CMCCAT2000* transform sharpened *RGB* array.
    F_L : array_like
        *Luminance* level adaptation factor :math:`F_L`.

    Returns
    -------
    ndarray
        Uncompressed *CMCCAT2000* transform sharpened *RGB* array.

    Examples
    --------
    >>> RGB = np.array([7.94632020, 7.94711528, 7.94899595])
    >>> F_L = 1.16754446415
    >>> post_adaptation_non_linear_response_compression_reverse(RGB, F_L)
    ... # doctest: +ELLIPSIS
    array([ 19.9969397...,  20.0018612...,  20.0135052...])
    """

    RGB = as_float_array(RGB)
    F_L = as_float_array(F_L)

    RGB_p = (((100 / F_L[..., np.newaxis]) * spow(
        (27.13 * (RGB - 0.1)) / (400 - (RGB - 0.1)), 1 / 0.42)))

    return RGB_p
Ejemplo n.º 40
0
def colourfulness_correlate(C: FloatingOrArrayLike,
                            F_L: FloatingOrArrayLike) -> FloatingOrNDArray:
    """
    Return the *colourfulness* correlate :math:`M`.

    Parameters
    ----------
    C
        *Chroma* correlate :math:`C`.
    F_L
        *Luminance* level adaptation factor :math:`F_L`.

    Returns
    -------
    :class:`numpy.floating` or :class:`numpy.ndarray`
        *Colourfulness* correlate :math:`M`.

    Examples
    --------
    >>> C = 0.104707757171
    >>> F_L = 1.16754446415
    >>> colourfulness_correlate(C, F_L)  # doctest: +ELLIPSIS
    0.1088421...
    """

    C = as_float_array(C)
    F_L = as_float_array(F_L)

    M = C * spow(F_L, 0.25)

    return M
Ejemplo n.º 41
0
def overall_chromatic_response(M_yb, M_rg):
    """
    Returns the overall chromatic response :math:`M`.

    Parameters
    ----------
    M_yb : numeric or array_like
         Yellowness / blueness response :math:`M_{yb}`.
    M_rg : numeric or array_like
         Redness / greenness response :math:`M_{rg}`.

    Returns
    -------
    numeric or ndarray
        Overall chromatic response :math:`M`.

    Examples
    --------
    >>> M_yb = -0.008237223618825
    >>> M_rg = -0.000104447583276
    >>> overall_chromatic_response(M_yb, M_rg)  # doctest: +ELLIPSIS
    0.0082378...
    """

    M_yb = as_float_array(M_yb)
    M_rg = as_float_array(M_rg)

    M = spow((M_yb**2) + (M_rg**2), 0.5)

    return M
Ejemplo n.º 42
0
def colourfulness_correlate(C, F_L):
    """
    Returns the *colourfulness* correlate :math:`M`.

    Parameters
    ----------
    C : numeric or array_like
        *Chroma* correlate :math:`C`.
    F_L : numeric or array_like
        *Luminance* level adaptation factor :math:`F_L`.

    Returns
    -------
    numeric or ndarray
        *Colourfulness* correlate :math:`M`.

    Examples
    --------
    >>> C = 0.104707757171
    >>> F_L = 1.16754446415
    >>> colourfulness_correlate(C, F_L)  # doctest: +ELLIPSIS
    0.1088421...
    """

    C = as_float_array(C)
    F_L = as_float_array(F_L)

    M = C * spow(F_L, 0.25)

    return M
Ejemplo n.º 43
0
def f_n(x):
    """
    Defines the nonlinear response function of the *Hunt* colour appearance
    model used to model the nonlinear behaviour of various visual responses.

    Parameters
    ----------
    x : numeric or array_like or array_like
        Visual response variable :math:`x`.

    Returns
    -------
    numeric or array_like
        Modeled visual response variable :math:`x`.


    Examples
    --------
    >>> x = np.array([0.23350512, 0.23351103, 0.23355179])
    >>> f_n(x)  # doctest: +ELLIPSIS
    array([ 5.8968592...,  5.8969521...,  5.8975927...])
    """

    x = as_float_array(x)

    x_p = spow(x, 0.73)
    x_m = 40 * (x_p / (x_p + 2))

    return x_m
Ejemplo n.º 44
0
def illuminant_scotopic_luminance(L_A, CCT):
    """
    Returns the approximate scotopic luminance :math:`L_{AS}` of the
    illuminant.

    Parameters
    ----------
    L_A : numeric or array_like
        Adapting field *luminance* :math:`L_A` in :math:`cd/m^2`.
    CCT : numeric or array_like
        Correlated color temperature :math:`T_{cp}` of the illuminant.

    Returns
    -------
    numeric or ndarray
        Approximate scotopic luminance :math:`L_{AS}`.

    Examples
    --------
    >>> illuminant_scotopic_luminance(318.31, 6504.0)  # doctest: +ELLIPSIS
    769.9376286...
    """

    L_A = as_float_array(L_A)
    CCT = as_float_array(CCT)

    CCT = 2.26 * L_A * spow((CCT / 4000) - 0.4, 1 / 3)

    return CCT
Ejemplo n.º 45
0
def luminance_level_adaptation_factor(L_A):
    """
    Returns the *luminance* level adaptation factor :math:`F_L`.

    Parameters
    ----------
    L_A : numeric or array_like
        Adapting field *luminance* :math:`L_A` in :math:`cd/m^2`.

    Returns
    -------
    numeric or ndarray
        *Luminance* level adaptation factor :math:`F_L`

    Examples
    --------
    >>> luminance_level_adaptation_factor(318.31)  # doctest: +ELLIPSIS
    1.1675444...
    """

    L_A = as_float_array(L_A)

    k = 1 / (5 * L_A + 1)
    k4 = k**4
    F_L = 0.2 * k4 * (5 * L_A) + 0.1 * (1 - k4)**2 * spow(5 * L_A, 1 / 3)

    return F_L
Ejemplo n.º 46
0
def chroma_correlate(L_star_P: FloatingOrArrayLike,
                     S: FloatingOrArrayLike) -> FloatingOrNDArray:
    """
    Return the correlate of *chroma* :math:`C`.

    Parameters
    ----------
    L_star_P
        *Achromatic Lightness* correlate :math:`L_p^\\star`.
    S
        Correlate of *saturation* :math:`S`.

    Returns
    -------
    :class:`numpy.floating` or :class:`numpy.ndarray`
        Correlate of *chroma* :math:`C`.

    Examples
    --------
    >>> L_star_P = 49.99988297570504
    >>> S = 0.013355029751778
    >>> chroma_correlate(L_star_P, S)  # doctest: +ELLIPSIS
    0.0133550...
    """

    L_star_P = as_float_array(L_star_P)
    S = as_float_array(S)

    C = spow(L_star_P / 50, 0.7) * S

    return C
Ejemplo n.º 47
0
def chromatic_induction_factors(n: FloatingOrArrayLike) -> NDArray:
    """
    Return the chromatic induction factors :math:`N_{bb}` and :math:`N_{cb}`.

    Parameters
    ----------
    n
        Function of the luminance factor of the background :math:`n`.

    Returns
    -------
    :class:`numpy.ndarray`
        Chromatic induction factors :math:`N_{bb}` and :math:`N_{cb}`.

    Examples
    --------
    >>> chromatic_induction_factors(0.2)  # doctest: +ELLIPSIS
    array([ 1.000304,  1.000304])
    """

    n = as_float_array(n)

    N_bb = N_cb = as_float(0.725) * spow(1 / n, 0.2)
    N_bbcb = tstack([N_bb, N_cb])

    return N_bbcb
Ejemplo n.º 48
0
def lightness_Fairchild2010(Y, epsilon=1.836):
    """
    Computes *Lightness* :math:`L_{hdr}` of given *luminance* :math:`Y` using
    *Fairchild and Wyble (2010)* method according to *Michealis-Menten*
    kinetics.

    Parameters
    ----------
    Y : array_like
        *luminance* :math:`Y`.
    epsilon : numeric or array_like, optional
        :math:`\\epsilon` exponent.

    Returns
    -------
    array_like
        *Lightness* :math:`L_{hdr}`.

    Warning
    -------
    The input domain of that definition is non standard!

    Notes
    -----

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

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

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

    Examples
    --------
    >>> lightness_Fairchild2010(12.19722535 / 100)  # doctest: +ELLIPSIS
    31.9963902...
    """

    Y = to_domain_1(Y)

    maximum_perception = 100

    L_hdr = reaction_rate_MichealisMenten(
        spow(Y, epsilon), maximum_perception, 0.184 ** epsilon) + 0.02

    return from_range_100(L_hdr)
Ejemplo n.º 49
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)
Ejemplo n.º 50
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)
Ejemplo n.º 51
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)
Ejemplo n.º 52
0
def temporary_magnitude_quantity_reverse(C, J, n):
    """
    Returns the temporary magnitude quantity :math:`t`. for reverse *CIECAM02*
    implementation.

    Parameters
    ----------
    C : numeric or array_like
        *Chroma* correlate :math:`C`.
    J : numeric or array_like
        *Lightness* correlate :math:`J`.
    n : numeric or array_like
        Function of the luminance factor of the background :math:`n`.

    Returns
    -------
    numeric or ndarray
         Temporary magnitude quantity :math:`t`.

    Notes
    -----
    -   This definition implements negative values handling as per
        :cite:`Luo2013`.

    Examples
    --------
    >>> C = 68.8364136888275
    >>> J = 41.749268505999
    >>> n = 0.2
    >>> temporary_magnitude_quantity_reverse(C, J, n)  # doctest: +ELLIPSIS
    202.3873619...
   """

    C = as_float_array(C)
    J = np.maximum(J, EPSILON)
    n = as_float_array(n)

    t = spow(C / (np.sqrt(J / 100) * spow(1.64 - 0.29 ** n, 0.73)), 1 / 0.9)

    return t
Ejemplo n.º 53
0
def beta_1(x):
    """
    Computes the exponent :math:`\\beta_1` for the middle and long-wavelength
    sensitive cones.

    Parameters
    ----------
    x: numeric or array_like
        Middle and long-wavelength sensitive cone response.

    Returns
    -------
    numeric or array_like
        Exponent :math:`\\beta_1`.

    Examples
    --------
    >>> beta_1(318.323316315)  # doctest: +ELLIPSIS
    4.6106222...
    """

    return (6.469 + 6.362 * spow(x, 0.4495)) / (6.469 + spow(x, 0.4495))
Ejemplo n.º 54
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))
Ejemplo n.º 55
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)
Ejemplo n.º 56
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))
Ejemplo n.º 57
0
def beta_2(x):
    """
    Computes the exponent :math:`\\beta_2` for the short-wavelength sensitive
    cones.

    Parameters
    ----------
    x: numeric or array_like
        Short-wavelength sensitive cone response.

    Returns
    -------
    numeric or array_like
        Exponent :math:`\\beta_2`.

    Examples
    --------
    >>> beta_2(318.323316315)  # doctest: +ELLIPSIS
    4.6522416...
    """

    return 0.7844 * (8.414 + 8.091 * spow(x, 0.5128)) / (
        8.414 + spow(x, 0.5128))
Ejemplo n.º 58
0
def lightness_Wyszecki1963(Y):
    """
    Returns the *Lightness* :math:`W` of given *luminance* :math:`Y` using
    *Wyszecki (1963)* method.


    Parameters
    ----------
    Y : numeric or array_like
        *luminance* :math:`Y`.

    Returns
    -------
    numeric or array_like
        *Lightness* :math:`W`.

    Notes
    -----

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

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

    References
    ----------
    :cite:`Wyszecki1963b`

    Examples
    --------
    >>> lightness_Wyszecki1963(12.19722535)  # doctest: +ELLIPSIS
    40.5475745...
    """

    Y = to_domain_100(Y)

    if np.any(Y < 1) or np.any(Y > 98):
        usage_warning('"W*" Lightness computation is only applicable for '
                      '1% < "Y" < 98%, unpredictable results may occur!')

    W = 25 * spow(Y, 1 / 3) - 17

    return from_range_100(W)
Ejemplo n.º 59
0
def temporary_magnitude_quantity_forward(N_c, N_cb, e_t, a, b, RGB_a):
    """
    Returns the temporary magnitude quantity :math:`t`. for forward *CIECAM02*
    implementation.

    Parameters
    ----------
    N_c : numeric or array_like
        Surround chromatic induction factor :math:`N_{c}`.
    N_cb : numeric or array_like
        Chromatic induction factor :math:`N_{cb}`.
    e_t : numeric or array_like
        Eccentricity factor :math:`e_t`.
    a : numeric or array_like
        Opponent colour dimension :math:`a`.
    b : numeric or array_like
        Opponent colour dimension :math:`b`.
    RGB_a : array_like
        Compressed stimulus *CMCCAT2000* transform sharpened *RGB* array.

    Returns
    -------
    numeric or ndarray
         Temporary magnitude quantity :math:`t`.

    Examples
    --------
    >>> N_c = 1.0
    >>> N_cb = 1.00030400456
    >>> e_t = 1.174005472851914
    >>> a = -0.000624112068243
    >>> b = -0.000506270106773
    >>> RGB_a = np.array([7.94632020, 7.94711528, 7.94899595])
    >>> temporary_magnitude_quantity_forward(N_c, N_cb, e_t, a, b, RGB_a)
    ... # doctest: +ELLIPSIS
    0.1497462...
    """

    N_c = as_float_array(N_c)
    N_cb = as_float_array(N_cb)
    e_t = as_float_array(e_t)
    a = as_float_array(a)
    b = as_float_array(b)
    Ra, Ga, Ba = tsplit(RGB_a)

    t = (((50000 / 13) * N_c * N_cb) * (e_t * spow(a ** 2 + b ** 2, 0.5)) /
         (Ra + Ga + 21 * Ba / 20))

    return t
Ejemplo n.º 60
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))