Esempio n. 1
0
    def test_n_dimensional_intermediate_lightness_function_CIE1976(self):
        """
        Tests :func:`colour.colorimetry.lightness.\
intermediate_lightness_function_CIE1976` definition n-dimensional arrays
        support.
        """

        Y = 12.19722535
        f_Y_Y_n = 0.495929964178047
        np.testing.assert_almost_equal(
            intermediate_lightness_function_CIE1976(Y), f_Y_Y_n, decimal=7)

        Y = np.tile(Y, 6)
        f_Y_Y_n = np.tile(f_Y_Y_n, 6)
        np.testing.assert_almost_equal(
            intermediate_lightness_function_CIE1976(Y), f_Y_Y_n, decimal=7)

        Y = np.reshape(Y, (2, 3))
        f_Y_Y_n = np.reshape(f_Y_Y_n, (2, 3))
        np.testing.assert_almost_equal(
            intermediate_lightness_function_CIE1976(Y), f_Y_Y_n, decimal=7)

        Y = np.reshape(Y, (2, 3, 1))
        f_Y_Y_n = np.reshape(f_Y_Y_n, (2, 3, 1))
        np.testing.assert_almost_equal(
            intermediate_lightness_function_CIE1976(Y), f_Y_Y_n, decimal=7)
Esempio n. 2
0
    def test_nan_intermediate_lightness_function_CIE1976(self):
        """
        Test :func:`colour.colorimetry.lightness.\
intermediate_lightness_function_CIE1976` definition nan support.
        """

        intermediate_lightness_function_CIE1976(
            np.array([-1.0, 0.0, 1.0, -np.inf, np.inf, np.nan]))
Esempio n. 3
0
    def test_nan_intermediate_lightness_function_CIE1976(self):
        """
        Tests :func:`colour.colorimetry.lightness.\
intermediate_lightness_function_CIE1976` definition nan support.
        """

        intermediate_lightness_function_CIE1976(
            np.array([-1.0, 0.0, 1.0, -np.inf, np.inf, np.nan]))
Esempio n. 4
0
    def test_domain_range_scale_intermediate_lightness_function_CIE1976(self):
        """
        Tests :func:`colour.colorimetry.lightness.\
intermediate_lightness_function_CIE1976` definition domain and range scale
        support.
        """

        f_Y_Y_n = intermediate_lightness_function_CIE1976(12.19722535, 100)

        for scale in ('reference', 1, 100):
            with domain_range_scale(scale):
                np.testing.assert_almost_equal(
                    intermediate_lightness_function_CIE1976(12.19722535, 100),
                    f_Y_Y_n,
                    decimal=7)
Esempio n. 5
0
    def test_domain_range_scale_intermediate_lightness_function_CIE1976(self):
        """
        Tests :func:`colour.colorimetry.lightness.\
intermediate_lightness_function_CIE1976` definition domain and range scale
        support.
        """

        f_Y_Y_n = intermediate_lightness_function_CIE1976(12.19722535, 100)

        for scale in ('reference', 1, 100):
            with domain_range_scale(scale):
                np.testing.assert_almost_equal(
                    intermediate_lightness_function_CIE1976(12.19722535, 100),
                    f_Y_Y_n,
                    decimal=7)
Esempio n. 6
0
    def test_intermediate_lightness_function_CIE1976(self):
        """
        Tests :func:`colour.colorimetry.lightness.\
intermediate_lightness_function_CIE1976` definition.
        """

        self.assertAlmostEqual(
            intermediate_lightness_function_CIE1976(12.19722535),
            0.495929964178047,
            places=7)

        self.assertAlmostEqual(
            intermediate_lightness_function_CIE1976(23.04276781),
            0.613072093530391,
            places=7)

        self.assertAlmostEqual(
            intermediate_lightness_function_CIE1976(6.15720079),
            0.394876333449113,
            places=7)
Esempio n. 7
0
    def test_intermediate_lightness_function_CIE1976(self):
        """
        Tests :func:`colour.colorimetry.lightness.\
intermediate_lightness_function_CIE1976` definition.
        """

        self.assertAlmostEqual(
            intermediate_lightness_function_CIE1976(12.19722535),
            0.495929964178047,
            places=7)

        self.assertAlmostEqual(
            intermediate_lightness_function_CIE1976(23.04276781),
            0.613072093530391,
            places=7)

        self.assertAlmostEqual(
            intermediate_lightness_function_CIE1976(6.15720079),
            0.394876333449113,
            places=7)
Esempio n. 8
0
    def test_n_dimensional_intermediate_lightness_function_CIE1976(self):
        """
        Tests :func:`colour.colorimetry.lightness.\
intermediate_lightness_function_CIE1976` definition n-dimensional arrays
        support.
        """

        Y = 12.19722535
        f_Y_Y_n = intermediate_lightness_function_CIE1976(Y)

        Y = np.tile(Y, 6)
        f_Y_Y_n = np.tile(f_Y_Y_n, 6)
        np.testing.assert_almost_equal(
            intermediate_lightness_function_CIE1976(Y), f_Y_Y_n, decimal=7)

        Y = np.reshape(Y, (2, 3))
        f_Y_Y_n = np.reshape(f_Y_Y_n, (2, 3))
        np.testing.assert_almost_equal(
            intermediate_lightness_function_CIE1976(Y), f_Y_Y_n, decimal=7)

        Y = np.reshape(Y, (2, 3, 1))
        f_Y_Y_n = np.reshape(f_Y_Y_n, (2, 3, 1))
        np.testing.assert_almost_equal(
            intermediate_lightness_function_CIE1976(Y), f_Y_Y_n, decimal=7)
Esempio n. 9
0
def XYZ_to_Lab(
        XYZ,
        illuminant=ILLUMINANTS['CIE 1931 2 Degree Standard Observer']['D65']):
    """
    Converts from *CIE XYZ* tristimulus values to *CIE L\\*a\\*b\\**
    colourspace.

    Parameters
    ----------
    XYZ : array_like
        *CIE XYZ* tristimulus values.
    illuminant : array_like, optional
        Reference *illuminant* *CIE xy* chromaticity coordinates or *CIE xyY*
        colourspace array.

    Returns
    -------
    ndarray
        *CIE L\\*a\\*b\\** colourspace array.

    Notes
    -----

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

    +----------------+-----------------------+-----------------+
    | **Range**      | **Scale - Reference** | **Scale - 1**   |
    +================+=======================+=================+
    | ``Lab``        | ``L`` : [0, 100]      | ``L`` : [0, 1]  |
    |                |                       |                 |
    |                | ``a`` : [-100, 100]   | ``a`` : [-1, 1] |
    |                |                       |                 |
    |                | ``b`` : [-100, 100]   | ``b`` : [-1, 1] |
    +----------------+-----------------------+-----------------+

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

    Examples
    --------
    >>> import numpy as np
    >>> XYZ = np.array([0.20654008, 0.12197225, 0.05136952])
    >>> XYZ_to_Lab(XYZ)  # doctest: +ELLIPSIS
    array([ 41.5278752...,  52.6385830...,  26.9231792...])
    """

    X, Y, Z = tsplit(to_domain_1(XYZ))

    X_n, Y_n, Z_n = tsplit(xyY_to_XYZ(xy_to_xyY(illuminant)))

    f_X_X_n = intermediate_lightness_function_CIE1976(X, X_n)
    f_Y_Y_n = intermediate_lightness_function_CIE1976(Y, Y_n)
    f_Z_Z_n = intermediate_lightness_function_CIE1976(Z, Z_n)

    L = 116 * f_Y_Y_n - 16
    a = 500 * (f_X_X_n - f_Y_Y_n)
    b = 200 * (f_Y_Y_n - f_Z_Z_n)

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

    return from_range_100(Lab)
Esempio n. 10
0
def error_function(
    coefficients: ArrayLike,
    target: ArrayLike,
    cmfs: MultiSpectralDistributions,
    illuminant: SpectralDistribution,
    max_error: Optional[Floating] = None,
    additional_data: Boolean = False,
) -> Union[
    Tuple[Floating, NDArray],
    Tuple[Floating, NDArray, NDArray, NDArray, NDArray],
]:
    """
    Compute :math:`\\Delta E_{76}` between the target colour and the colour
    defined by given spectral model, along with its gradient.

    Parameters
    ----------
    coefficients
        Dimensionless coefficients for *Jakob and Hanika (2019)* reflectance
        spectral model.
    target
        *CIE L\\*a\\*b\\** colourspace array of the target colour.
    cmfs
        Standard observer colour matching functions.
    illuminant
        Illuminant spectral distribution.
    max_error
        Raise ``StopMinimizationEarly`` if the error is smaller than this.
        The default is *None* and the function doesn't raise anything.
    additional_data
        If *True*, some intermediate calculations are returned, for use in
        correctness tests: R, XYZ and Lab.

    Returns
    -------
    :class:`tuple` or :class:`tuple`
        Tuple of computed :math:`\\Delta E_{76}` error and gradient of error,
        i.e. the first derivatives of error with respect to the input
        coefficients or tuple of computed :math:`\\Delta E_{76}` error,
        gradient of error, computed spectral reflectance, *CIE XYZ* tristimulus
        values corresponding to ``R`` and *CIE L\\*a\\*b\\** colourspace array
        corresponding to ``XYZ``.

    Raises
    ------
    StopMinimizationEarly
        Raised when the error is below ``max_error``.
    """

    target = as_float_array(target)

    c_0, c_1, c_2 = as_float_array(coefficients)
    wv = np.linspace(0, 1, len(cmfs.shape))

    U = c_0 * wv**2 + c_1 * wv + c_2
    t1 = np.sqrt(1 + U**2)
    R = 1 / 2 + U / (2 * t1)

    t2 = 1 / (2 * t1) - U**2 / (2 * t1**3)
    dR = np.array([wv**2 * t2, wv * t2, t2])

    XYZ = sd_to_XYZ_integration(R, cmfs, illuminant, shape=cmfs.shape) / 100
    dXYZ = np.transpose(
        sd_to_XYZ_integration(dR, cmfs, illuminant, shape=cmfs.shape) / 100
    )

    XYZ_n = sd_to_XYZ_integration(illuminant, cmfs)
    XYZ_n /= XYZ_n[1]
    XYZ_XYZ_n = XYZ / XYZ_n

    XYZ_f = intermediate_lightness_function_CIE1976(XYZ, XYZ_n)
    dXYZ_f = np.where(
        XYZ_XYZ_n[..., np.newaxis] > (24 / 116) ** 3,
        1
        / (
            3
            * spow(XYZ_n[..., np.newaxis], 1 / 3)
            * spow(XYZ[..., np.newaxis], 2 / 3)
        )
        * dXYZ,
        (841 / 108) * dXYZ / XYZ_n[..., np.newaxis],
    )

    def intermediate_XYZ_to_Lab(
        XYZ_i: NDArray, offset: Optional[Floating] = 16
    ) -> NDArray:
        """
        Return the final intermediate value for the *CIE Lab* to *CIE XYZ*
        conversion.
        """

        return np.array(
            [
                116 * XYZ_i[1] - offset,
                500 * (XYZ_i[0] - XYZ_i[1]),
                200 * (XYZ_i[1] - XYZ_i[2]),
            ]
        )

    Lab_i = intermediate_XYZ_to_Lab(as_float_array(XYZ_f))
    dLab_i = intermediate_XYZ_to_Lab(as_float_array(dXYZ_f), 0)

    error = np.sqrt(np.sum((Lab_i - target) ** 2))
    if max_error is not None and error <= max_error:
        raise StopMinimizationEarly(coefficients, error)

    derror = (
        np.sum(
            dLab_i * (Lab_i[..., np.newaxis] - target[..., np.newaxis]), axis=0
        )
        / error
    )

    if additional_data:
        return error, derror, R, XYZ, Lab_i
    else:
        return error, derror
Esempio n. 11
0
def error_function(coefficients,
                   target,
                   cmfs,
                   illuminant,
                   max_error=None,
                   additional_data=False):
    """
    Computes :math:`\\Delta E_{76}` between the target colour and the colour
    defined by given spectral model, along with its gradient.

    Parameters
    ----------
    coefficients : array_like
        Dimensionless coefficients for *Jakob and Hanika (2019)* reflectance
        spectral model.
    target : array_like, (3,)
        *CIE L\\*a\\*b\\** colourspace array of the target colour.
    cmfs : XYZ_ColourMatchingFunctions
        Standard observer colour matching functions.
    illuminant : SpectralDistribution
        Illuminant spectral distribution.
    max_error : float, optional
        Raise ``StopMinimizationEarly`` if the error is smaller than this.
        The default is *None* and the function doesn't raise anything.
    additional_data : bool, optional
        If *True*, some intermediate calculations are returned, for use in
        correctness tests: R, XYZ and Lab.

    Returns
    -------
    error : float
        The computed :math:`\\Delta E_{76}` error.
    derror : ndarray, (3,)
        The gradient of error, i.e. the first derivatives of error with respect
        to the input coefficients.
    R : ndarray
        Computed spectral reflectance.
    XYZ : ndarray, (3,)
        *CIE XYZ* tristimulus values corresponding to ``R``.
    Lab : ndarray, (3,)
        *CIE L\\*a\\*b\\** colourspace array corresponding to ``XYZ``.

    Raises
    ------
    StopMinimizationEarly
        Raised when the error is below ``max_error``.
    """

    c_0, c_1, c_2 = as_float_array(coefficients)
    wv = np.linspace(0, 1, len(cmfs.shape))

    U = c_0 * wv ** 2 + c_1 * wv + c_2
    t1 = np.sqrt(1 + U ** 2)
    R = 1 / 2 + U / (2 * t1)

    t2 = 1 / (2 * t1) - U ** 2 / (2 * t1 ** 3)
    dR = np.array([wv ** 2 * t2, wv * t2, t2])

    E = illuminant.values * R
    dE = illuminant.values * dR

    dw = cmfs.wavelengths[1] - cmfs.wavelengths[0]
    k = 1 / (np.sum(cmfs.values[:, 1] * illuminant.values) * dw)

    XYZ = k * np.dot(E, cmfs.values) * dw
    dXYZ = np.transpose(k * np.dot(dE, cmfs.values) * dw)

    XYZ_n = sd_to_XYZ(illuminant, cmfs)
    XYZ_n /= XYZ_n[1]
    XYZ_XYZ_n = XYZ / XYZ_n

    XYZ_f = intermediate_lightness_function_CIE1976(XYZ, XYZ_n)
    dXYZ_f = np.where(
        XYZ_XYZ_n[..., np.newaxis] > (24 / 116) ** 3,
        1 / (3 * spow(XYZ_n[..., np.newaxis], 1 / 3) * spow(
            XYZ[..., np.newaxis], 2 / 3)) * dXYZ,
        (841 / 108) * dXYZ / XYZ_n[..., np.newaxis],
    )

    def intermediate_XYZ_to_Lab(XYZ_i, offset=16):
        """
        Returns the final intermediate value for the *CIE Lab* to *CIE XYZ*
        conversion.
        """

        return np.array([
            116 * XYZ_i[1] - offset, 500 * (XYZ_i[0] - XYZ_i[1]),
            200 * (XYZ_i[1] - XYZ_i[2])
        ])

    Lab_i = intermediate_XYZ_to_Lab(XYZ_f)
    dLab_i = intermediate_XYZ_to_Lab(dXYZ_f, 0)

    error = np.sqrt(np.sum((Lab_i - target) ** 2))
    if max_error is not None and error <= max_error:
        raise StopMinimizationEarly(coefficients, error)

    derror = np.sum(
        dLab_i * (Lab_i[..., np.newaxis] - target[..., np.newaxis]),
        axis=0) / error

    if additional_data:
        return error, derror, R, XYZ, Lab_i
    else:
        return error, derror
Esempio n. 12
0
def XYZ_to_Lab(
        XYZ,
        illuminant=ILLUMINANTS['CIE 1931 2 Degree Standard Observer']['D65']):
    """
    Converts from *CIE XYZ* tristimulus values to *CIE L\\*a\\*b\\**
    colourspace.

    Parameters
    ----------
    XYZ : array_like
        *CIE XYZ* tristimulus values.
    illuminant : array_like, optional
        Reference *illuminant* *xy* chromaticity coordinates or *CIE xyY*
        colourspace array.

    Returns
    -------
    ndarray
        *CIE L\\*a\\*b\\** colourspace array.

    Notes
    -----

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

    +----------------+-----------------------+-----------------+
    | **Range**      | **Scale - Reference** | **Scale - 1**   |
    +================+=======================+=================+
    | ``Lab``        | ``L`` : [0, 100]      | ``L`` : [0, 1]  |
    |                |                       |                 |
    |                | ``a`` : [-100, 100]   | ``a`` : [-1, 1] |
    |                |                       |                 |
    |                | ``b`` : [-100, 100]   | ``b`` : [-1, 1] |
    +----------------+-----------------------+-----------------+

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

    Examples
    --------
    >>> XYZ = np.array([0.20654008, 0.12197225, 0.05136952])
    >>> XYZ_to_Lab(XYZ)  # doctest: +ELLIPSIS
    array([ 41.5278752...,  52.6385830...,  26.9231792...])
    """

    X, Y, Z = tsplit(to_domain_1(XYZ))

    X_n, Y_n, Z_n = tsplit(xyY_to_XYZ(xy_to_xyY(illuminant)))

    f_X_X_n = intermediate_lightness_function_CIE1976(X, X_n)
    f_Y_Y_n = intermediate_lightness_function_CIE1976(Y, Y_n)
    f_Z_Z_n = intermediate_lightness_function_CIE1976(Z, Z_n)

    L = 116 * f_Y_Y_n - 16
    a = 500 * (f_X_X_n - f_Y_Y_n)
    b = 200 * (f_Y_Y_n - f_Z_Z_n)

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

    return from_range_100(Lab)