Example #1
0
    def test_row_as_diagonal(self):
        """
        Tests :func:`colour.utilities.array.row_as_diagonal` definition.
        """

        np.testing.assert_almost_equal(
            row_as_diagonal(np.array([[0.25891593, 0.07299478, 0.36586996],
                                      [0.30851087, 0.37131459, 0.16274825],
                                      [0.71061831, 0.67718718, 0.09562581],
                                      [0.71588836, 0.76772047, 0.15476079],
                                      [0.92985142, 0.22263399, 0.88027331]])),
            np.array([[[0.25891593, 0.00000000, 0.00000000],
                       [0.00000000, 0.07299478, 0.00000000],
                       [0.00000000, 0.00000000, 0.36586996]],
                      [[0.30851087, 0.00000000, 0.00000000],
                       [0.00000000, 0.37131459, 0.00000000],
                       [0.00000000, 0.00000000, 0.16274825]],
                      [[0.71061831, 0.00000000, 0.00000000],
                       [0.00000000, 0.67718718, 0.00000000],
                       [0.00000000, 0.00000000, 0.09562581]],
                      [[0.71588836, 0.00000000, 0.00000000],
                       [0.00000000, 0.76772047, 0.00000000],
                       [0.00000000, 0.00000000, 0.15476079]],
                      [[0.92985142, 0.00000000, 0.00000000],
                       [0.00000000, 0.22263399, 0.00000000],
                       [0.00000000, 0.00000000, 0.88027331]]]))
Example #2
0
    def test_row_as_diagonal(self):
        """
        Tests :func:`colour.utilities.array.row_as_diagonal` definition.
        """

        np.testing.assert_almost_equal(
            row_as_diagonal(np.array(
                [[0.25891593, 0.07299478, 0.36586996],
                 [0.30851087, 0.37131459, 0.16274825],
                 [0.71061831, 0.67718718, 0.09562581],
                 [0.71588836, 0.76772047, 0.15476079],
                 [0.92985142, 0.22263399, 0.88027331]])
            ),
            np.array(
                [[[0.25891593, 0.00000000, 0.00000000],
                  [0.00000000, 0.07299478, 0.00000000],
                  [0.00000000, 0.00000000, 0.36586996]],
                 [[0.30851087, 0.00000000, 0.00000000],
                  [0.00000000, 0.37131459, 0.00000000],
                  [0.00000000, 0.00000000, 0.16274825]],
                 [[0.71061831, 0.00000000, 0.00000000],
                  [0.00000000, 0.67718718, 0.00000000],
                  [0.00000000, 0.00000000, 0.09562581]],
                 [[0.71588836, 0.00000000, 0.00000000],
                  [0.00000000, 0.76772047, 0.00000000],
                  [0.00000000, 0.00000000, 0.15476079]],
                 [[0.92985142, 0.00000000, 0.00000000],
                  [0.00000000, 0.22263399, 0.00000000],
                  [0.00000000, 0.00000000, 0.88027331]]]
            )
        )  # yapf: disable
Example #3
0
def chromatic_adaptation_matrix_VonKries(XYZ_w, XYZ_wr, transform='CAT02'):
    M = CHROMATIC_ADAPTATION_TRANSFORMS.get(transform)

    print('XYZ_w', XYZ_w)
    print('XYZ_wr', XYZ_wr)

    if M is None:
        raise KeyError(
            '"{0}" chromatic adaptation transform is not defined! Supported '
            'methods: "{1}".'.format(transform,
                                     CHROMATIC_ADAPTATION_TRANSFORMS.keys()))

    print('M', M)
    print('M inv', np.linalg.inv(M))

    rgb_w = np.einsum('...i,...ij->...j', XYZ_w, np.transpose(M))
    rgb_wr = np.einsum('...i,...ij->...j', XYZ_wr, np.transpose(M))

    print('rgb_w: ', rgb_w)
    print('rgb_wr: ', rgb_wr)

    D = rgb_wr / rgb_w
    print('D: ', D)

    D = row_as_diagonal(D)

    M_CAT = dot_matrix(np.linalg.inv(M), D)
    M_CAT = dot_matrix(M_CAT, M)

    return M_CAT
Example #4
0
def chromatic_adaptation_Fairchild1990(
    XYZ_1: ArrayLike,
    XYZ_n: ArrayLike,
    XYZ_r: ArrayLike,
    Y_n: FloatingOrArrayLike,
    discount_illuminant: Boolean = False,
) -> NDArray:
    """
    Adapt given stimulus *CIE XYZ_1* tristimulus values from test viewing
    conditions to reference viewing conditions using *Fairchild (1990)*
    chromatic adaptation model.

    Parameters
    ----------
    XYZ_1
        *CIE XYZ_1* tristimulus values of test sample / stimulus.
    XYZ_n
        Test viewing condition *CIE XYZ_n* tristimulus values of whitepoint.
    XYZ_r
        Reference viewing condition *CIE XYZ_r* tristimulus values of
        whitepoint.
    Y_n
        Luminance :math:`Y_n` of test adapting stimulus in :math:`cd/m^2`.
    discount_illuminant
        Truth value indicating if the illuminant should be discounted.

    Returns
    -------
    :class:`numpy.ndarray`
        Adapted *CIE XYZ_2* tristimulus values of stimulus.

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

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

    References
    ----------
    :cite:`Fairchild1991a`, :cite:`Fairchild2013s`

    Examples
    --------
    >>> XYZ_1 = np.array([19.53, 23.07, 24.97])
    >>> XYZ_n = np.array([111.15, 100.00, 35.20])
    >>> XYZ_r = np.array([94.81, 100.00, 107.30])
    >>> Y_n = 200
    >>> chromatic_adaptation_Fairchild1990(XYZ_1, XYZ_n, XYZ_r, Y_n)
    ... # doctest: +ELLIPSIS
    array([ 23.3252634...,  23.3245581...,  76.1159375...])
    """

    XYZ_1 = to_domain_100(XYZ_1)
    XYZ_n = to_domain_100(XYZ_n)
    XYZ_r = to_domain_100(XYZ_r)
    Y_n = as_float_array(Y_n)

    LMS_1 = vector_dot(MATRIX_XYZ_TO_RGB_FAIRCHILD1990, XYZ_1)
    LMS_n = vector_dot(MATRIX_XYZ_TO_RGB_FAIRCHILD1990, XYZ_n)
    LMS_r = vector_dot(MATRIX_XYZ_TO_RGB_FAIRCHILD1990, XYZ_r)

    p_LMS = degrees_of_adaptation(
        LMS_1, Y_n, discount_illuminant=discount_illuminant
    )

    a_LMS_1 = p_LMS / LMS_n
    a_LMS_2 = p_LMS / LMS_r

    A_1 = row_as_diagonal(a_LMS_1)
    A_2 = row_as_diagonal(a_LMS_2)

    LMSp_1 = vector_dot(A_1, LMS_1)

    c = 0.219 - 0.0784 * np.log10(Y_n)
    C = row_as_diagonal(tstack([c, c, c]))

    LMS_a = vector_dot(C, LMSp_1)
    LMSp_2 = vector_dot(np.linalg.inv(C), LMS_a)

    LMS_c = vector_dot(np.linalg.inv(A_2), LMSp_2)
    XYZ_c = vector_dot(MATRIX_RGB_TO_XYZ_FAIRCHILD1990, LMS_c)

    return from_range_100(XYZ_c)
Example #5
0
def chromatic_adaptation_matrix_VonKries(XYZ_w, XYZ_wr, transform='CAT02'):
    """
    Computes the *chromatic adaptation* matrix from test viewing conditions
    to reference viewing conditions.

    Parameters
    ----------
    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
        Chromatic adaptation matrix :math:`M_{cat}`.

    Raises
    ------
    KeyError
        If chromatic adaptation method is not defined.

    Notes
    -----

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

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

    Examples
    --------
    >>> XYZ_w = np.array([0.95045593, 1.00000000, 1.08905775])
    >>> XYZ_wr = np.array([0.96429568, 1.00000000, 0.82510460])
    >>> chromatic_adaptation_matrix_VonKries(XYZ_w, XYZ_wr)
    ... # doctest: +ELLIPSIS
    array([[ 1.0425738...,  0.0308910..., -0.0528125...],
           [ 0.0221934...,  1.0018566..., -0.0210737...],
           [-0.0011648..., -0.0034205...,  0.7617890...]])

    Using Bradford method:

    >>> XYZ_w = np.array([0.95045593, 1.00000000, 1.08905775])
    >>> XYZ_wr = np.array([0.96429568, 1.00000000, 0.82510460])
    >>> method = 'Bradford'
    >>> chromatic_adaptation_matrix_VonKries(XYZ_w, XYZ_wr, method)
    ... # doctest: +ELLIPSIS
    array([[ 1.0479297...,  0.0229468..., -0.0501922...],
           [ 0.0296278...,  0.9904344..., -0.0170738...],
           [-0.0092430...,  0.0150551...,  0.7518742...]])
    """

    XYZ_w = to_domain_1(XYZ_w)
    XYZ_wr = to_domain_1(XYZ_wr)

    M = CHROMATIC_ADAPTATION_TRANSFORMS.get(transform)

    if M is None:
        raise KeyError(
            '"{0}" chromatic adaptation transform is not defined! Supported '
            'methods: "{1}".'.format(transform,
                                     CHROMATIC_ADAPTATION_TRANSFORMS.keys()))

    rgb_w = np.einsum('...i,...ij->...j', XYZ_w, np.transpose(M))
    rgb_wr = np.einsum('...i,...ij->...j', XYZ_wr, np.transpose(M))

    D = rgb_wr / rgb_w

    D = row_as_diagonal(D)

    M_CAT = dot_matrix(np.linalg.inv(M), D)
    M_CAT = dot_matrix(M_CAT, M)

    return M_CAT
Example #6
0
def XYZ_to_RLAB(XYZ,
                XYZ_n,
                Y_n,
                sigma=VIEWING_CONDITIONS_RLAB['Average'],
                D=D_FACTOR_RLAB['Hard Copy Images']):
    """
    Computes the *RLAB* model color appearance correlates.

    Parameters
    ----------
    XYZ : array_like
        *CIE XYZ* tristimulus values of test sample / stimulus.
    XYZ_n : array_like
        *CIE XYZ* tristimulus values of reference white.
    Y_n : numeric or array_like
        Absolute adapting luminance in :math:`cd/m^2`.
    sigma : numeric or array_like, optional
        Relative luminance of the surround, see
        :attr:`colour.VIEWING_CONDITIONS_RLAB` for reference.
    D : numeric or array_like, optional
        *Discounting-the-Illuminant* factor normalised to domain [0, 1].

    Returns
    -------
    CAM_Specification_RLAB
        *RLAB* colour appearance model specification.

    Notes
    -----

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

    +------------------------------+-----------------------\
+---------------+
    | **Range**                    | **Scale - Reference** \
| **Scale - 1** |
    +==============================+=======================\
+===============+
    | ``CAM_Specification_RLAB.h`` | [0, 360]              \
| [0, 1]        |
    +------------------------------+-----------------------\
+---------------+

    References
    ----------
    :cite:`Fairchild1996a`, :cite:`Fairchild2013w`

    Examples
    --------
    >>> XYZ = np.array([19.01, 20.00, 21.78])
    >>> XYZ_n = np.array([109.85, 100, 35.58])
    >>> Y_n = 31.83
    >>> sigma = VIEWING_CONDITIONS_RLAB['Average']
    >>> D = D_FACTOR_RLAB['Hard Copy Images']
    >>> XYZ_to_RLAB(XYZ, XYZ_n, Y_n, sigma, D)  # doctest: +ELLIPSIS
    CAM_Specification_RLAB(J=49.8347069..., C=54.8700585..., \
h=286.4860208..., s=1.1010410..., HC=None, a=15.5711021..., b=-52.6142956...)
    """

    XYZ = to_domain_100(XYZ)
    XYZ_n = to_domain_100(XYZ_n)
    Y_n = as_float_array(Y_n)
    D = as_float_array(D)
    sigma = as_float_array(sigma)

    # Converting to cone responses.
    LMS_n = XYZ_to_rgb(XYZ_n)

    # Computing the :math:`A` matrix.
    LMS_l_E = (3 * LMS_n) / (LMS_n[0] + LMS_n[1] + LMS_n[2])
    LMS_p_L = ((1 + spow(Y_n[..., np.newaxis], 1 / 3) + LMS_l_E) /
               (1 + spow(Y_n[..., np.newaxis], 1 / 3) + (1 / LMS_l_E)))
    LMS_a_L = (LMS_p_L + D[..., np.newaxis] * (1 - LMS_p_L)) / LMS_n

    aR = row_as_diagonal(LMS_a_L)
    M = matrix_dot(matrix_dot(MATRIX_R, aR), MATRIX_XYZ_TO_HPE)
    XYZ_ref = vector_dot(M, XYZ)

    X_ref, Y_ref, Z_ref = tsplit(XYZ_ref)

    # Computing the correlate of *Lightness* :math:`L^R`.
    LR = 100 * spow(Y_ref, sigma)

    # Computing opponent colour dimensions :math:`a^R` and :math:`b^R`.
    aR = 430 * (spow(X_ref, sigma) - spow(Y_ref, sigma))
    bR = 170 * (spow(Y_ref, sigma) - spow(Z_ref, sigma))

    # Computing the *hue* angle :math:`h^R`.
    hR = np.degrees(np.arctan2(bR, aR)) % 360
    # TODO: Implement hue composition computation.

    # Computing the correlate of *chroma* :math:`C^R`.
    CR = np.hypot(aR, bR)

    # Computing the correlate of *saturation* :math:`s^R`.
    sR = CR / LR

    return CAM_Specification_RLAB(LR, CR, from_range_degrees(hR), sR, None, aR,
                                  bR)
Example #7
0
File: rlab.py Project: brehm/colour
def XYZ_to_RLAB(XYZ,
                XYZ_n,
                Y_n,
                sigma=RLAB_VIEWING_CONDITIONS.get('Average'),
                D=RLAB_D_FACTOR.get('Hard Copy Images')):
    """
    Computes the RLAB model color appearance correlates.

    Parameters
    ----------
    XYZ : array_like
        *CIE XYZ* tristimulus values of test sample / stimulus in domain
        [0, 100].
    XYZ_n : array_like
        *CIE XYZ* tristimulus values of reference white in domain [0, 100].
    Y_n : numeric or array_like
        Absolute adapting luminance in :math:`cd/m^2`.
    sigma : numeric or array_like, optional
        Relative luminance of the surround, see :attr:`RLAB_VIEWING_CONDITIONS`
        for reference.
    D : numeric or array_like, optional
        *Discounting-the-Illuminant* factor in domain [0, 1].

    Returns
    -------
    RLAB_Specification
        RLAB colour appearance model specification.

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

    Notes
    -----
    -   Input *CIE XYZ* tristimulus values are in domain [0, 100].
    -   Input *CIE XYZ_n* tristimulus values are in domain [0, 100].

    Examples
    --------
    >>> XYZ = np.array([19.01, 20.00, 21.78])
    >>> XYZ_n = np.array([109.85, 100, 35.58])
    >>> Y_n = 31.83
    >>> sigma = RLAB_VIEWING_CONDITIONS['Average']
    >>> D = RLAB_D_FACTOR['Hard Copy Images']
    >>> XYZ_to_RLAB(XYZ, XYZ_n, Y_n, sigma, D)  # doctest: +ELLIPSIS
    RLAB_Specification(J=49.8347069..., C=54.8700585..., h=286.4860208..., \
s=1.1010410..., HC=None, a=15.5711021..., b=-52.6142956...)
    """

    Y_n = np.asarray(Y_n)
    D = np.asarray(D)
    sigma = np.asarray(sigma)

    # Converting to cone responses.
    LMS_n = XYZ_to_rgb(XYZ_n)

    # Computing the :math:`A` matrix.
    LMS_l_E = (3 * LMS_n) / (LMS_n[0] + LMS_n[1] + LMS_n[2])
    LMS_p_L = ((1 + (Y_n[..., np.newaxis] ** (1 / 3)) + LMS_l_E) /
               (1 + (Y_n[..., np.newaxis] ** (1 / 3)) + (1 / LMS_l_E)))
    LMS_a_L = (LMS_p_L + D[..., np.newaxis] * (1 - LMS_p_L)) / LMS_n

    aR = row_as_diagonal(LMS_a_L)
    M = dot_matrix(dot_matrix(R_MATRIX, aR), XYZ_TO_HPE_MATRIX)
    XYZ_ref = dot_vector(M, XYZ)

    X_ref, Y_ref, Z_ref = tsplit(XYZ_ref)

    # -------------------------------------------------------------------------
    # Computing the correlate of *Lightness* :math:`L^R`.
    # -------------------------------------------------------------------------
    LR = 100 * (Y_ref ** sigma)

    # Computing opponent colour dimensions :math:`a^R` and :math:`b^R`.
    aR = 430 * ((X_ref ** sigma) - (Y_ref ** sigma))
    bR = 170 * ((Y_ref ** sigma) - (Z_ref ** sigma))

    # -------------------------------------------------------------------------
    # Computing the *hue* angle :math:`h^R`.
    # -------------------------------------------------------------------------
    hR = np.degrees(np.arctan2(bR, aR)) % 360
    # TODO: Implement hue composition computation.

    # -------------------------------------------------------------------------
    # Computing the correlate of *chroma* :math:`C^R`.
    # -------------------------------------------------------------------------
    CR = np.sqrt((aR ** 2) + (bR ** 2))

    # -------------------------------------------------------------------------
    # Computing the correlate of *saturation* :math:`s^R`.
    # -------------------------------------------------------------------------
    sR = CR / LR

    return RLAB_Specification(LR, CR, hR, sR, None, aR, bR)
Example #8
0
def XYZ_to_RLAB(XYZ,
                XYZ_n,
                Y_n,
                sigma=RLAB_VIEWING_CONDITIONS['Average'],
                D=RLAB_D_FACTOR['Hard Copy Images']):
    """
    Computes the *RLAB* model color appearance correlates.

    Parameters
    ----------
    XYZ : array_like
        *CIE XYZ* tristimulus values of test sample / stimulus in domain
        [0, 100].
    XYZ_n : array_like
        *CIE XYZ* tristimulus values of reference white in domain [0, 100].
    Y_n : numeric or array_like
        Absolute adapting luminance in :math:`cd/m^2`.
    sigma : numeric or array_like, optional
        Relative luminance of the surround, see
        :attr:`colour.RLAB_VIEWING_CONDITIONS` for reference.
    D : numeric or array_like, optional
        *Discounting-the-Illuminant* factor in domain [0, 1].

    Returns
    -------
    RLAB_Specification
        *RLAB* colour appearance model specification.

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

    Notes
    -----
    -   Input *CIE XYZ* tristimulus values are in domain [0, 100].
    -   Input *CIE XYZ_n* tristimulus values are in domain [0, 100].

    References
    ----------
    -   :cite:`Fairchild1996a`
    -   :cite:`Fairchild2013w`

    Examples
    --------
    >>> XYZ = np.array([19.01, 20.00, 21.78])
    >>> XYZ_n = np.array([109.85, 100, 35.58])
    >>> Y_n = 31.83
    >>> sigma = RLAB_VIEWING_CONDITIONS['Average']
    >>> D = RLAB_D_FACTOR['Hard Copy Images']
    >>> XYZ_to_RLAB(XYZ, XYZ_n, Y_n, sigma, D)  # doctest: +ELLIPSIS
    RLAB_Specification(J=49.8347069..., C=54.8700585..., h=286.4860208..., \
s=1.1010410..., HC=None, a=15.5711021..., b=-52.6142956...)
    """

    Y_n = np.asarray(Y_n)
    D = np.asarray(D)
    sigma = np.asarray(sigma)

    # Converting to cone responses.
    LMS_n = XYZ_to_rgb(XYZ_n)

    # Computing the :math:`A` matrix.
    LMS_l_E = (3 * LMS_n) / (LMS_n[0] + LMS_n[1] + LMS_n[2])
    LMS_p_L = ((1 + (Y_n[..., np.newaxis] ** (1 / 3)) + LMS_l_E) /
               (1 + (Y_n[..., np.newaxis] ** (1 / 3)) + (1 / LMS_l_E)))
    LMS_a_L = (LMS_p_L + D[..., np.newaxis] * (1 - LMS_p_L)) / LMS_n

    aR = row_as_diagonal(LMS_a_L)
    M = dot_matrix(dot_matrix(R_MATRIX, aR), XYZ_TO_HPE_MATRIX)
    XYZ_ref = dot_vector(M, XYZ)

    X_ref, Y_ref, Z_ref = tsplit(XYZ_ref)

    # Computing the correlate of *Lightness* :math:`L^R`.
    LR = 100 * (Y_ref ** sigma)

    # Computing opponent colour dimensions :math:`a^R` and :math:`b^R`.
    aR = 430 * ((X_ref ** sigma) - (Y_ref ** sigma))
    bR = 170 * ((Y_ref ** sigma) - (Z_ref ** sigma))

    # Computing the *hue* angle :math:`h^R`.
    hR = np.degrees(np.arctan2(bR, aR)) % 360
    # TODO: Implement hue composition computation.

    # Computing the correlate of *chroma* :math:`C^R`.
    CR = np.hypot(aR, bR)

    # Computing the correlate of *saturation* :math:`s^R`.
    sR = CR / LR

    return RLAB_Specification(LR, CR, hR, sR, None, aR, bR)
Example #9
0
def chromatic_adaptation_Fairchild1990(XYZ_1,
                                       XYZ_n,
                                       XYZ_r,
                                       Y_n,
                                       discount_illuminant=False):
    """
    Adapts given stimulus *CIE XYZ_1* tristimulus values from test viewing
    conditions to reference viewing conditions using Fairchild (1990) chromatic
    adaptation model.

    Parameters
    ----------
    XYZ_1 : array_like
        *CIE XYZ_1* tristimulus values of test sample / stimulus in domain
        [0, 100].
    XYZ_n : array_like
        Test viewing condition *CIE XYZ_n* tristimulus values of whitepoint.
    XYZ_r : array_like
        Reference viewing condition *CIE XYZ_r* tristimulus values of
        whitepoint.
    Y_n : numeric or array_like
        Luminance :math:`Y_n` of test adapting stimulus in :math:`cd/m^2`.
    discount_illuminant : bool, optional
        Truth value indicating if the illuminant should be discounted.

    Returns
    -------
    ndarray
        Adapted *CIE XYZ_2* tristimulus values of stimulus.

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

    Notes
    -----
    -   Input *CIE XYZ_1*, *CIE XYZ_n* and *CIE XYZ_r* tristimulus values are
        in domain [0, 100].
    -   Output *CIE XYZ_2* tristimulus values are in domain [0, 100].

    Examples
    --------
    >>> XYZ_1 = np.array([19.53, 23.07, 24.97])
    >>> XYZ_n = np.array([111.15, 100.00, 35.20])
    >>> XYZ_r = np.array([94.81, 100.00, 107.30])
    >>> Y_n = 200
    >>> chromatic_adaptation_Fairchild1990(  # doctest: +ELLIPSIS
    ...     XYZ_1, XYZ_n, XYZ_r, Y_n)
    array([ 23.3252634...,  23.3245581...,  76.1159375...])
    """

    XYZ_1 = np.asarray(XYZ_1)
    XYZ_n = np.asarray(XYZ_n)
    XYZ_r = np.asarray(XYZ_r)
    Y_n = np.asarray(Y_n)

    LMS_1 = dot_vector(FAIRCHILD1990_XYZ_TO_RGB_MATRIX, XYZ_1)
    LMS_n = dot_vector(FAIRCHILD1990_XYZ_TO_RGB_MATRIX, XYZ_n)
    LMS_r = dot_vector(FAIRCHILD1990_XYZ_TO_RGB_MATRIX, XYZ_r)

    p_LMS = degrees_of_adaptation(LMS_1,
                                  Y_n,
                                  discount_illuminant=discount_illuminant)

    a_LMS_1 = p_LMS / LMS_n
    a_LMS_2 = p_LMS / LMS_r

    A_1 = row_as_diagonal(a_LMS_1)
    A_2 = row_as_diagonal(a_LMS_2)

    LMSp_1 = dot_vector(A_1, LMS_1)

    c = 0.219 - 0.0784 * np.log10(Y_n)
    C = row_as_diagonal(tstack((c, c, c)))

    LMS_a = dot_vector(C, LMSp_1)
    LMSp_2 = dot_vector(np.linalg.inv(C), LMS_a)

    LMS_c = dot_vector(np.linalg.inv(A_2), LMSp_2)
    XYZ_c = dot_vector(FAIRCHILD1990_RGB_TO_XYZ_MATRIX, LMS_c)

    return XYZ_c
Example #10
0
def chromatic_adaptation_Fairchild1990(XYZ_1,
                                       XYZ_n,
                                       XYZ_r,
                                       Y_n,
                                       discount_illuminant=False):
    """
    Adapts given stimulus *CIE XYZ_1* tristimulus values from test viewing
    conditions to reference viewing conditions using *Fairchild (1990)*
    chromatic adaptation model.

    Parameters
    ----------
    XYZ_1 : array_like
        *CIE XYZ_1* tristimulus values of test sample / stimulus.
    XYZ_n : array_like
        Test viewing condition *CIE XYZ_n* tristimulus values of whitepoint.
    XYZ_r : array_like
        Reference viewing condition *CIE XYZ_r* tristimulus values of
        whitepoint.
    Y_n : numeric or array_like
        Luminance :math:`Y_n` of test adapting stimulus in :math:`cd/m^2`.
    discount_illuminant : bool, optional
        Truth value indicating if the illuminant should be discounted.

    Returns
    -------
    ndarray
        Adapted *CIE XYZ_2* tristimulus values of stimulus.

    Notes
    -----

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

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

    References
    ----------
    :cite:`Fairchild1991a`, :cite:`Fairchild2013s`

    Examples
    --------
    >>> XYZ_1 = np.array([19.53, 23.07, 24.97])
    >>> XYZ_n = np.array([111.15, 100.00, 35.20])
    >>> XYZ_r = np.array([94.81, 100.00, 107.30])
    >>> Y_n = 200
    >>> chromatic_adaptation_Fairchild1990(XYZ_1, XYZ_n, XYZ_r, Y_n)
    ... # doctest: +ELLIPSIS
    array([ 23.3252634...,  23.3245581...,  76.1159375...])
    """

    XYZ_1 = to_domain_100(XYZ_1)
    XYZ_n = to_domain_100(XYZ_n)
    XYZ_r = to_domain_100(XYZ_r)
    Y_n = as_float_array(Y_n)

    LMS_1 = dot_vector(FAIRCHILD1990_XYZ_TO_RGB_MATRIX, XYZ_1)
    LMS_n = dot_vector(FAIRCHILD1990_XYZ_TO_RGB_MATRIX, XYZ_n)
    LMS_r = dot_vector(FAIRCHILD1990_XYZ_TO_RGB_MATRIX, XYZ_r)

    p_LMS = degrees_of_adaptation(
        LMS_1, Y_n, discount_illuminant=discount_illuminant)

    a_LMS_1 = p_LMS / LMS_n
    a_LMS_2 = p_LMS / LMS_r

    A_1 = row_as_diagonal(a_LMS_1)
    A_2 = row_as_diagonal(a_LMS_2)

    LMSp_1 = dot_vector(A_1, LMS_1)

    c = 0.219 - 0.0784 * np.log10(Y_n)
    C = row_as_diagonal(tstack([c, c, c]))

    LMS_a = dot_vector(C, LMSp_1)
    LMSp_2 = dot_vector(np.linalg.inv(C), LMS_a)

    LMS_c = dot_vector(np.linalg.inv(A_2), LMSp_2)
    XYZ_c = dot_vector(FAIRCHILD1990_RGB_TO_XYZ_MATRIX, LMS_c)

    return from_range_100(XYZ_c)
Example #11
0
def chromatic_adaptation_matrix_VonKries(XYZ_w, XYZ_wr, transform='CAT02'):
    """
    Computes the *chromatic adaptation* matrix from test viewing conditions
    to reference viewing conditions.

    Parameters
    ----------
    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
        Chromatic adaptation matrix.

    Raises
    ------
    KeyError
        If chromatic adaptation method is not defined.

    Examples
    --------
    >>> XYZ_w = np.array([1.09846607, 1.00000000, 0.35582280])
    >>> XYZ_wr = np.array([0.95042855, 1.00000000, 1.08890037])
    >>> chromatic_adaptation_matrix_VonKries(  # doctest: +ELLIPSIS
    ...     XYZ_w, XYZ_wr)
    array([[ 0.8687653..., -0.1416539...,  0.3871961...],
           [-0.1030072...,  1.0584014...,  0.1538646...],
           [ 0.0078167...,  0.0267875...,  2.9608177...]])

    Using Bradford method:

    >>> XYZ_w = np.array([1.09846607, 1.00000000, 0.35582280])
    >>> XYZ_wr = np.array([0.95042855, 1.00000000, 1.08890037])
    >>> method = 'Bradford'
    >>> chromatic_adaptation_matrix_VonKries(  # doctest: +ELLIPSIS
    ...     XYZ_w, XYZ_wr, method)
    array([[ 0.8446794..., -0.1179355...,  0.3948940...],
           [-0.1366408...,  1.1041236...,  0.1291981...],
           [ 0.0798671..., -0.1349315...,  3.1928829...]])
    """

    M = CHROMATIC_ADAPTATION_TRANSFORMS.get(transform)

    if M is None:
        raise KeyError(
            '"{0}" chromatic adaptation transform is not defined! Supported '
            'methods: "{1}".'.format(transform,
                                     CHROMATIC_ADAPTATION_TRANSFORMS.keys()))

    rgb_w = np.einsum('...i,...ij->...j', XYZ_w, np.transpose(M))
    rgb_wr = np.einsum('...i,...ij->...j', XYZ_wr, np.transpose(M))

    D = rgb_wr / rgb_w

    D = row_as_diagonal(D)

    cat = dot_matrix(np.linalg.inv(M), D)
    cat = dot_matrix(cat, M)

    return cat
Example #12
0
def chromatic_adaptation_Fairchild1990(XYZ_1,
                                       XYZ_n,
                                       XYZ_r,
                                       Y_n,
                                       discount_illuminant=False):
    """
    Adapts given stimulus *CIE XYZ_1* tristimulus values from test viewing
    conditions to reference viewing conditions using *Fairchild (1990)*
    chromatic adaptation model.

    Parameters
    ----------
    XYZ_1 : array_like
        *CIE XYZ_1* tristimulus values of test sample / stimulus in domain
        [0, 100].
    XYZ_n : array_like
        Test viewing condition *CIE XYZ_n* tristimulus values of whitepoint.
    XYZ_r : array_like
        Reference viewing condition *CIE XYZ_r* tristimulus values of
        whitepoint.
    Y_n : numeric or array_like
        Luminance :math:`Y_n` of test adapting stimulus in :math:`cd/m^2`.
    discount_illuminant : bool, optional
        Truth value indicating if the illuminant should be discounted.

    Returns
    -------
    ndarray
        Adapted *CIE XYZ_2* tristimulus values of stimulus.

    Warning
    -------
    The input domain and output range of that definition are non standard!

    Notes
    -----
    -   Input *CIE XYZ_1*, *CIE XYZ_n* and *CIE XYZ_r* tristimulus values are
        in domain [0, 100].
    -   Output *CIE XYZ_2* tristimulus values are in range [0, 100].

    References
    ----------
    -   :cite:`Fairchild1991a`
    -   :cite:`Fairchild2013s`

    Examples
    --------
    >>> XYZ_1 = np.array([19.53, 23.07, 24.97])
    >>> XYZ_n = np.array([111.15, 100.00, 35.20])
    >>> XYZ_r = np.array([94.81, 100.00, 107.30])
    >>> Y_n = 200
    >>> chromatic_adaptation_Fairchild1990(XYZ_1, XYZ_n, XYZ_r, Y_n)
    ... # doctest: +ELLIPSIS
    array([ 23.3252634...,  23.3245581...,  76.1159375...])
    """

    XYZ_1 = np.asarray(XYZ_1)
    XYZ_n = np.asarray(XYZ_n)
    XYZ_r = np.asarray(XYZ_r)
    Y_n = np.asarray(Y_n)

    LMS_1 = dot_vector(FAIRCHILD1990_XYZ_TO_RGB_MATRIX, XYZ_1)
    LMS_n = dot_vector(FAIRCHILD1990_XYZ_TO_RGB_MATRIX, XYZ_n)
    LMS_r = dot_vector(FAIRCHILD1990_XYZ_TO_RGB_MATRIX, XYZ_r)

    p_LMS = degrees_of_adaptation(
        LMS_1, Y_n, discount_illuminant=discount_illuminant)

    a_LMS_1 = p_LMS / LMS_n
    a_LMS_2 = p_LMS / LMS_r

    A_1 = row_as_diagonal(a_LMS_1)
    A_2 = row_as_diagonal(a_LMS_2)

    LMSp_1 = dot_vector(A_1, LMS_1)

    c = 0.219 - 0.0784 * np.log10(Y_n)
    C = row_as_diagonal(tstack((c, c, c)))

    LMS_a = dot_vector(C, LMSp_1)
    LMSp_2 = dot_vector(np.linalg.inv(C), LMS_a)

    LMS_c = dot_vector(np.linalg.inv(A_2), LMSp_2)
    XYZ_c = dot_vector(FAIRCHILD1990_RGB_TO_XYZ_MATRIX, LMS_c)

    return XYZ_c
Example #13
0
def XYZ_to_RLAB(XYZ,
                XYZ_n,
                Y_n,
                sigma=RLAB_VIEWING_CONDITIONS['Average'],
                D=RLAB_D_FACTOR['Hard Copy Images']):
    """
    Computes the *RLAB* model color appearance correlates.

    Parameters
    ----------
    XYZ : array_like
        *CIE XYZ* tristimulus values of test sample / stimulus.
    XYZ_n : array_like
        *CIE XYZ* tristimulus values of reference white.
    Y_n : numeric or array_like
        Absolute adapting luminance in :math:`cd/m^2`.
    sigma : numeric or array_like, optional
        Relative luminance of the surround, see
        :attr:`colour.RLAB_VIEWING_CONDITIONS` for reference.
    D : numeric or array_like, optional
        *Discounting-the-Illuminant* factor normalised to domain [0, 1].

    Returns
    -------
    RLAB_Specification
        *RLAB* colour appearance model specification.

    Notes
    -----

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

    +--------------------------+-----------------------+---------------+
    | **Range**                | **Scale - Reference** | **Scale - 1** |
    +==========================+=======================+===============+
    | ``RLAB_Specification.h`` | [0, 360]              | [0, 1]        |
    +--------------------------+-----------------------+---------------+

    References
    ----------
    :cite:`Fairchild1996a`, :cite:`Fairchild2013w`

    Examples
    --------
    >>> XYZ = np.array([19.01, 20.00, 21.78])
    >>> XYZ_n = np.array([109.85, 100, 35.58])
    >>> Y_n = 31.83
    >>> sigma = RLAB_VIEWING_CONDITIONS['Average']
    >>> D = RLAB_D_FACTOR['Hard Copy Images']
    >>> XYZ_to_RLAB(XYZ, XYZ_n, Y_n, sigma, D)  # doctest: +ELLIPSIS
    RLAB_Specification(J=49.8347069..., C=54.8700585..., h=286.4860208..., \
s=1.1010410..., HC=None, a=15.5711021..., b=-52.6142956...)
    """

    XYZ = to_domain_100(XYZ)
    XYZ_n = to_domain_100(XYZ_n)
    Y_n = as_float_array(Y_n)
    D = as_float_array(D)
    sigma = as_float_array(sigma)

    # Converting to cone responses.
    LMS_n = XYZ_to_rgb(XYZ_n)

    # Computing the :math:`A` matrix.
    LMS_l_E = (3 * LMS_n) / (LMS_n[0] + LMS_n[1] + LMS_n[2])
    LMS_p_L = ((1 + spow(Y_n[..., np.newaxis], 1 / 3) + LMS_l_E) /
               (1 + spow(Y_n[..., np.newaxis], 1 / 3) + (1 / LMS_l_E)))
    LMS_a_L = (LMS_p_L + D[..., np.newaxis] * (1 - LMS_p_L)) / LMS_n

    aR = row_as_diagonal(LMS_a_L)
    M = dot_matrix(dot_matrix(R_MATRIX, aR), XYZ_TO_HPE_MATRIX)
    XYZ_ref = dot_vector(M, XYZ)

    X_ref, Y_ref, Z_ref = tsplit(XYZ_ref)

    # Computing the correlate of *Lightness* :math:`L^R`.
    LR = 100 * spow(Y_ref, sigma)

    # Computing opponent colour dimensions :math:`a^R` and :math:`b^R`.
    aR = 430 * (spow(X_ref, sigma) - spow(Y_ref, sigma))
    bR = 170 * (spow(Y_ref, sigma) - spow(Z_ref, sigma))

    # Computing the *hue* angle :math:`h^R`.
    hR = np.degrees(np.arctan2(bR, aR)) % 360
    # TODO: Implement hue composition computation.

    # Computing the correlate of *chroma* :math:`C^R`.
    CR = np.hypot(aR, bR)

    # Computing the correlate of *saturation* :math:`s^R`.
    sR = CR / LR

    return RLAB_Specification(LR, CR, from_range_degrees(hR), sR, None, aR, bR)
Example #14
0
def chromatic_adaptation_matrix_VonKries(XYZ_w, XYZ_wr, transform='CAT02'):
    """
    Computes the *chromatic adaptation* matrix from test viewing conditions
    to reference viewing conditions.

    Parameters
    ----------
    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
        Chromatic adaptation matrix.

    Raises
    ------
    KeyError
        If chromatic adaptation method is not defined.

    Examples
    --------
    >>> XYZ_w = np.array([1.09846607, 1.00000000, 0.35582280])
    >>> XYZ_wr = np.array([0.95042855, 1.00000000, 1.08890037])
    >>> chromatic_adaptation_matrix_VonKries(  # doctest: +ELLIPSIS
    ...     XYZ_w, XYZ_wr)
    array([[ 0.8687653..., -0.1416539...,  0.3871961...],
           [-0.1030072...,  1.0584014...,  0.1538646...],
           [ 0.0078167...,  0.0267875...,  2.9608177...]])

    Using Bradford method:

    >>> XYZ_w = np.array([1.09846607, 1.00000000, 0.35582280])
    >>> XYZ_wr = np.array([0.95042855, 1.00000000, 1.08890037])
    >>> method = 'Bradford'
    >>> chromatic_adaptation_matrix_VonKries(  # doctest: +ELLIPSIS
    ...     XYZ_w, XYZ_wr, method)
    array([[ 0.8446794..., -0.1179355...,  0.3948940...],
           [-0.1366408...,  1.1041236...,  0.1291981...],
           [ 0.0798671..., -0.1349315...,  3.1928829...]])
    """

    M = CHROMATIC_ADAPTATION_TRANSFORMS.get(transform)

    if M is None:
        raise KeyError(
            '"{0}" chromatic adaptation transform is not defined! Supported '
            'methods: "{1}".'.format(transform,
                                     CHROMATIC_ADAPTATION_TRANSFORMS.keys()))

    rgb_w = np.einsum('...i,...ij->...j', XYZ_w, np.transpose(M))
    rgb_wr = np.einsum('...i,...ij->...j', XYZ_wr, np.transpose(M))

    D = rgb_wr / rgb_w

    D = row_as_diagonal(D)

    cat = dot_matrix(np.linalg.inv(M), D)
    cat = dot_matrix(cat, M)

    return cat
Example #15
0
def matrix_chromatic_adaptation_VonKries(
    XYZ_w: ArrayLike,
    XYZ_wr: ArrayLike,
    transform: Union[Literal["Bianco 2010", "Bianco PC 2010", "Bradford",
                             "CAT02 Brill 2008", "CAT02", "CAT16",
                             "CMCCAT2000", "CMCCAT97", "Fairchild", "Sharp",
                             "Von Kries", "XYZ Scaling", ], str, ] = "CAT02",
) -> NDArray:
    """
    Compute the *chromatic adaptation* matrix from test viewing conditions
    to reference viewing conditions.

    Parameters
    ----------
    XYZ_w
        Test viewing conditions *CIE XYZ* tristimulus values of whitepoint.
    XYZ_wr
        Reference viewing conditions *CIE XYZ* tristimulus values of
        whitepoint.
    transform
        Chromatic adaptation transform.

    Returns
    -------
    :class:`numpy.ndarray`
        Chromatic adaptation matrix :math:`M_{cat}`.

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

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

    Examples
    --------
    >>> XYZ_w = np.array([0.95045593, 1.00000000, 1.08905775])
    >>> XYZ_wr = np.array([0.96429568, 1.00000000, 0.82510460])
    >>> matrix_chromatic_adaptation_VonKries(XYZ_w, XYZ_wr)
    ... # doctest: +ELLIPSIS
    array([[ 1.0425738...,  0.0308910..., -0.0528125...],
           [ 0.0221934...,  1.0018566..., -0.0210737...],
           [-0.0011648..., -0.0034205...,  0.7617890...]])

    Using Bradford method:

    >>> XYZ_w = np.array([0.95045593, 1.00000000, 1.08905775])
    >>> XYZ_wr = np.array([0.96429568, 1.00000000, 0.82510460])
    >>> method = 'Bradford'
    >>> matrix_chromatic_adaptation_VonKries(XYZ_w, XYZ_wr, method)
    ... # doctest: +ELLIPSIS
    array([[ 1.0479297...,  0.0229468..., -0.0501922...],
           [ 0.0296278...,  0.9904344..., -0.0170738...],
           [-0.0092430...,  0.0150551...,  0.7518742...]])
    """

    XYZ_w = to_domain_1(XYZ_w)
    XYZ_wr = to_domain_1(XYZ_wr)

    transform = validate_method(
        transform,
        CHROMATIC_ADAPTATION_TRANSFORMS,
        '"{0}" chromatic adaptation transform is invalid, '
        "it must be one of {1}!",
    )

    M = CHROMATIC_ADAPTATION_TRANSFORMS[transform]

    RGB_w = np.einsum("...i,...ij->...j", XYZ_w, np.transpose(M))
    RGB_wr = np.einsum("...i,...ij->...j", XYZ_wr, np.transpose(M))

    D = RGB_wr / RGB_w

    D = row_as_diagonal(D)

    M_CAT = matrix_dot(np.linalg.inv(M), D)
    M_CAT = matrix_dot(M_CAT, M)

    return M_CAT