Exemplo n.º 1
0
def RGB_to_rgb(RGB: ArrayLike) -> NDArray:
    """
    Convert given *RGB* array to *Hunt-Pointer-Estevez*
    :math:`\\rho\\gamma\\beta` colourspace.

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

    Returns
    -------
    :class:`numpy.ndarray`
        *Hunt-Pointer-Estevez* :math:`\\rho\\gamma\\beta` colourspace array.

    Examples
    --------
    >>> RGB = np.array([19.99370783, 20.00393634, 20.01326387])
    >>> RGB_to_rgb(RGB)  # doctest: +ELLIPSIS
    array([ 19.9969397...,  20.0018612...,  20.0135053...])
    """

    rgb = vector_dot(matrix_dot(MATRIX_XYZ_TO_HPE, CAT_INVERSE_CAT02), RGB)

    return rgb
Exemplo n.º 2
0
def rgb_to_RGB(rgb: ArrayLike) -> NDArray:
    """
    Convert given *Hunt-Pointer-Estevez* :math:`\\rho\\gamma\\beta`
    colourspace array to *RGB* array.

    Parameters
    ----------
    rgb
        *Hunt-Pointer-Estevez* :math:`\\rho\\gamma\\beta` colourspace array.

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

    Examples
    --------
    >>> rgb = np.array([19.99693975, 20.00186123, 20.01350530])
    >>> rgb_to_RGB(rgb)  # doctest: +ELLIPSIS
    array([ 19.9937078...,  20.0039363...,  20.0132638...])
    """

    RGB = vector_dot(matrix_dot(CAT_CAT02, MATRIX_HPE_TO_XYZ), rgb)

    return RGB
Exemplo n.º 3
0
    def test_matrix_dot(self):
        """Test :func:`colour.utilities.array.matrix_dot` definition."""

        a = np.array(
            [
                [0.7328, 0.4296, -0.1624],
                [-0.7036, 1.6975, 0.0061],
                [0.0030, 0.0136, 0.9834],
            ]
        )
        a = np.reshape(np.tile(a, (6, 1)), (6, 3, 3))

        b = a

        np.testing.assert_almost_equal(
            matrix_dot(a, b),
            np.array(
                [
                    [
                        [0.23424208, 1.04184824, -0.27609032],
                        [-1.70994078, 2.57932265, 0.13061813],
                        [-0.00442036, 0.03774904, 0.96667132],
                    ],
                    [
                        [0.23424208, 1.04184824, -0.27609032],
                        [-1.70994078, 2.57932265, 0.13061813],
                        [-0.00442036, 0.03774904, 0.96667132],
                    ],
                    [
                        [0.23424208, 1.04184824, -0.27609032],
                        [-1.70994078, 2.57932265, 0.13061813],
                        [-0.00442036, 0.03774904, 0.96667132],
                    ],
                    [
                        [0.23424208, 1.04184824, -0.27609032],
                        [-1.70994078, 2.57932265, 0.13061813],
                        [-0.00442036, 0.03774904, 0.96667132],
                    ],
                    [
                        [0.23424208, 1.04184824, -0.27609032],
                        [-1.70994078, 2.57932265, 0.13061813],
                        [-0.00442036, 0.03774904, 0.96667132],
                    ],
                    [
                        [0.23424208, 1.04184824, -0.27609032],
                        [-1.70994078, 2.57932265, 0.13061813],
                        [-0.00442036, 0.03774904, 0.96667132],
                    ],
                ]
            ),
            decimal=7,
        )
Exemplo n.º 4
0
def XYZ_to_RLAB(
    XYZ: ArrayLike,
    XYZ_n: ArrayLike,
    Y_n: FloatingOrArrayLike,
    sigma: FloatingOrArrayLike = VIEWING_CONDITIONS_RLAB["Average"],
    D: FloatingOrArrayLike = D_FACTOR_RLAB["Hard Copy Images"],
) -> CAM_Specification_RLAB:
    """
    Compute the *RLAB* model color appearance correlates.

    Parameters
    ----------
    XYZ
        *CIE XYZ* tristimulus values of test sample / stimulus.
    XYZ_n
        *CIE XYZ* tristimulus values of reference white.
    Y_n
        Absolute adapting luminance in :math:`cd/m^2`.
    sigma
        Relative luminance of the surround, see
        :attr:`colour.VIEWING_CONDITIONS_RLAB` for reference.
    D
        *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) / np.sum(LMS_n, axis=-1)[..., np.newaxis]
    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

    M = matrix_dot(matrix_dot(MATRIX_R, row_as_diagonal(LMS_a_L)),
                   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 = as_float(430 * (spow(X_ref, sigma) - spow(Y_ref, sigma)))
    bR = as_float(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,
        as_float(from_range_degrees(hR)),
        sR,
        None,
        aR,
        bR,
    )
Exemplo n.º 5
0
def matrix_anomalous_trichromacy_Machado2009(
    cmfs: LMS_ConeFundamentals,
    primaries: RGB_DisplayPrimaries,
    d_LMS: ArrayLike,
) -> NDArray:
    """
    Compute the *Machado et al. (2009)* *CVD* matrix for given *LMS* cone
    fundamentals colour matching functions and display primaries tri-spectral
    distributions with given :math:`\\Delta_{LMS}` shift amount in nanometers
    to simulate anomalous trichromacy.

    Parameters
    ----------
    cmfs
        *LMS* cone fundamentals colour matching functions.
    primaries
        *RGB* display primaries tri-spectral distributions.
    d_LMS
        :math:`\\Delta_{LMS}` shift amount in nanometers.

    Notes
    -----
    -   Input *LMS* cone fundamentals colour matching functions interval is
        expected to be 1 nanometer, incompatible input will be interpolated
        at 1 nanometer interval.
    -   Input :math:`\\Delta_{LMS}` shift amount is in domain [0, 20].

    Returns
    -------
    :class:`numpy.ndarray`
        Anomalous trichromacy matrix.

    References
    ----------
    :cite:`Colblindorb`, :cite:`Colblindora`, :cite:`Colblindorc`,
    :cite:`Machado2009`

    Examples
    --------
    >>> from colour.characterisation import MSDS_DISPLAY_PRIMARIES
    >>> from colour.colorimetry import MSDS_CMFS_LMS
    >>> cmfs = MSDS_CMFS_LMS['Stockman & Sharpe 2 Degree Cone Fundamentals']
    >>> d_LMS = np.array([15, 0, 0])
    >>> primaries = MSDS_DISPLAY_PRIMARIES['Apple Studio Display']
    >>> matrix_anomalous_trichromacy_Machado2009(cmfs, primaries, d_LMS)
    ... # doctest: +ELLIPSIS
    array([[-0.2777465...,  2.6515008..., -1.3737543...],
           [ 0.2718936...,  0.2004786...,  0.5276276...],
           [ 0.0064404...,  0.2592157...,  0.7343437...]])
    """

    if cmfs.shape.interval != 1:
        # pylint: disable=E1102
        cmfs = reshape_msds(
            cmfs,  # type: ignore[assignment]
            SpectralShape(cmfs.shape.start, cmfs.shape.end, 1),
            "Interpolate",
        )

    M_n = matrix_RGB_to_WSYBRG(cmfs, primaries)
    cmfs_a = msds_cmfs_anomalous_trichromacy_Machado2009(cmfs, d_LMS)
    M_a = matrix_RGB_to_WSYBRG(cmfs_a, primaries)

    return matrix_dot(np.linalg.inv(M_n), M_a)
Exemplo n.º 6
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
Exemplo n.º 7
0
def matrix_RGB_to_RGB(
    input_colourspace: RGB_Colourspace,
    output_colourspace: RGB_Colourspace,
    chromatic_adaptation_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 matrix :math:`M` converting from given input *RGB*
    colourspace to output *RGB* colourspace using given *chromatic
    adaptation* method.

    Parameters
    ----------
    input_colourspace
        *RGB* input colourspace.
    output_colourspace
        *RGB* output colourspace.
    chromatic_adaptation_transform
        *Chromatic adaptation* transform, if *None* no chromatic adaptation is
        performed.

    Returns
    -------
    :class:`numpy.ndarray`
        Conversion matrix :math:`M`.

    Examples
    --------
    >>> from colour.models import (
    ...    RGB_COLOURSPACE_sRGB, RGB_COLOURSPACE_PROPHOTO_RGB)
    >>> matrix_RGB_to_RGB(RGB_COLOURSPACE_sRGB, RGB_COLOURSPACE_PROPHOTO_RGB)
    ... # doctest: +ELLIPSIS
    array([[ 0.5288241...,  0.3340609...,  0.1373616...],
           [ 0.0975294...,  0.8790074...,  0.0233981...],
           [ 0.0163599...,  0.1066124...,  0.8772485...]])
    """

    M = input_colourspace.matrix_RGB_to_XYZ

    if chromatic_adaptation_transform is not None:
        M_CAT = matrix_chromatic_adaptation_VonKries(
            xy_to_XYZ(input_colourspace.whitepoint),
            xy_to_XYZ(output_colourspace.whitepoint),
            chromatic_adaptation_transform,
        )

        M = matrix_dot(M_CAT, input_colourspace.matrix_RGB_to_XYZ)

    M = matrix_dot(output_colourspace.matrix_XYZ_to_RGB, M)

    return M