Exemple #1
0
    def output_specification_from_data(self, data):
        """
        Returns the CIECAM02 colour appearance model output specification
        from given data.

        Parameters
        ----------
        data : list
            Fixture data.

        Returns
        -------
        CIECAM02_Specification
            CIECAM02 colour appearance model specification.
        """

        XYZ = tstack((data['X'], data['Y'], data['Z']))
        XYZ_w = tstack((data['X_w'], data['Y_w'], data['Z_w']))

        specification = XYZ_to_CIECAM02(XYZ,
                                        XYZ_w,
                                        data['L_A'],
                                        data['Y_b'],
                                        CIECAM02_InductionFactors(
                                            data['F'],
                                            data['c'],
                                            data['N_c']))

        return specification
Exemple #2
0
    def output_specification_from_data(self, data):
        """
        Returns the ATD (1995) colour vision model output specification from
        given data.

        Parameters
        ----------
        data : list
            Fixture data.

        Returns
        -------
        ATD95_Specification
            ATD (1995) colour vision model specification.
        """

        XYZ = tstack((data['X'], data['Y'], data['Z']))
        XYZ_0 = tstack((data['X_0'], data['Y_0'], data['Z_0']))

        specification = XYZ_to_ATD95(XYZ,
                                     XYZ_0,
                                     data['Y_02'],
                                     data['K_1'],
                                     data['K_2'],
                                     data['sigma'])

        return specification
Exemple #3
0
def cartesian_to_spherical(a):
    """
    Transforms given Cartesian coordinates array to Spherical coordinates.

    Parameters
    ----------
    a : array_like
        Cartesian coordinates array (x, y, z) to transform.

    Returns
    -------
    ndarray
        Spherical coordinates array (r, theta, phi).

    See Also
    --------
    spherical_to_cartesian, cartesian_to_cylindrical, cylindrical_to_cartesian

    Examples
    --------
    >>> a = np.array([3, 1, 6])
    >>> cartesian_to_spherical(a)  # doctest: +ELLIPSIS
    array([ 6.7823299...,  1.0857465...,  0.3217505...])
    """

    x, y, z = tsplit(a)

    r = np.linalg.norm(a, axis=-1)
    theta = np.arctan2(z, np.linalg.norm(tstack((x, y)), axis=-1))
    phi = np.arctan2(y, x)

    rtp = tstack((r, theta, phi))

    return rtp
Exemple #4
0
def cartesian_to_cylindrical(a):
    """
    Transforms given Cartesian coordinates array to Cylindrical coordinates.

    Parameters
    ----------
    a : array_like
        Cartesian coordinates array (x, y, z) to transform.

    Returns
    -------
    ndarray
        Cylindrical coordinates array (z, theta, rho).

    See Also
    --------
    cartesian_to_spherical, spherical_to_cartesian, cylindrical_to_cartesian

    Examples
    --------
    >>> a = np.array([3, 1, 6])
    >>> cartesian_to_cylindrical(a)  # doctest: +ELLIPSIS
    array([ 6.        ,  0.3217505...,  3.1622776...])
    """

    x, y, z = tsplit(a)

    theta = np.arctan2(y, x)
    rho = np.linalg.norm(tstack((x, y)), axis=-1)

    return tstack((z, theta, rho))
Exemple #5
0
    def output_specification_from_data(self, data):
        """
        Returns the Nayatani (1995) colour appearance model output
        specification from given data.

        Parameters
        ----------
        data : list
            Fixture data.

        Returns
        -------
        Nayatani95_Specification
            Nayatani (1995) colour appearance model specification.
        """

        XYZ = tstack((data['X'], data['Y'], data['Z']))
        XYZ_n = tstack((data['X_n'], data['Y_n'], data['Z_n']))

        specification = XYZ_to_Nayatani95(XYZ,
                                          XYZ_n,
                                          data['Y_o'],
                                          data['E_o'],
                                          data['E_or'])

        return specification
Exemple #6
0
    def output_specification_from_data(self, data):
        """
        Returns the RLAB colour appearance model output specification
        from given data.

        Parameters
        ----------
        data : list
            Fixture data.

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

        XYZ = tstack((data['X'], data['Y'], data['Z']))
        XYZ_n = tstack((data['X_n'], data['Y_n'], data['Z_n']))

        specification = XYZ_to_RLAB(XYZ,
                                    XYZ_n,
                                    data['Y_n2'],
                                    data['sigma'],
                                    data['D'])

        return specification
Exemple #7
0
    def output_specification_from_data(self, data):
        """
        Returns the Hunt colour appearance model output specification
        from given data.

        Parameters
        ----------
        data : list
            Fixture data.

        Returns
        -------
        Hunt_Specification
            Hunt colour appearance model specification.
        """

        XYZ = tstack((data["X"], data["Y"], data["Z"]))
        XYZ_w = tstack((data["X_w"], data["Y_w"], data["Z_w"]))
        XYZ_b = tstack((data["X_w"], 0.2 * data["Y_w"], data["Z_w"]))

        specification = XYZ_to_Hunt(
            XYZ, XYZ_w, XYZ_b, data["L_A"], Hunt_InductionFactors(data["N_c"], data["N_b"]), CCT_w=data["T"]
        )

        return specification
    def test_extrapolate(self):
        """
        Tests :func:`colour.colorimetry.spectrum.\
MultiSpectralDistribution.extrapolate` method.
        """

        data = dict(zip(range(25, 35), tstack([[0] * 5 + [1] * 5] * 3)))
        multi_sd = MultiSpectralDistribution(data)
        multi_sd.extrapolate(SpectralShape(10, 50))

        np.testing.assert_almost_equal(
            multi_sd[10], np.array([0.0, 0.0, 0.0]), decimal=7)
        np.testing.assert_almost_equal(
            multi_sd[50], np.array([1.0, 1.0, 1.0]), decimal=7)

        multi_sd = MultiSpectralDistribution(
            tstack([np.linspace(0, 1, 10)] * 3), np.linspace(25, 35, 10))
        multi_sd.extrapolate(
            SpectralShape(10, 50),
            extrapolator_args={
                'method': 'Linear',
                'left': None,
                'right': None
            })
        np.testing.assert_almost_equal(
            multi_sd[10], np.array([-1.5, -1.5, -1.5]), decimal=7)
        np.testing.assert_almost_equal(
            multi_sd[50], np.array([2.5, 2.5, 2.5]), decimal=7)
    def setUp(self):
        """
        Initialises common tests attributes.
        """

        self._labels = ('x_bar', 'y_bar', 'z_bar')

        self._multi_sd = MultiSpectralDistribution(
            CIE_1931_2_DEGREE_STANDARD_OBSERVER,
            name='Observer',
            labels=self._labels)

        sd = SpectralDistribution(SAMPLE_SD_DATA)
        domain = sd.domain
        range_ = tstack([sd.values, sd.values, sd.values])
        self._sample_multi_sd = MultiSpectralDistribution(
            range_,
            domain,
            name='Sample Observer',
            labels=self._labels,
        )

        sd = SpectralDistribution(NON_UNIFORM_SAMPLE_SD_DATA)
        domain = sd.domain
        range_ = tstack([sd.values, sd.values, sd.values])
        self._non_uniform_sample_multi_sd = MultiSpectralDistribution(
            range_,
            domain,
            name='Non Uniform Sample Observer',
            strict_name='Strict Non Uniform Sample Observer',
            labels=self._labels,
            strict_labels=('Strict x_bar', 'Strict  y_bar', 'Strict  z_bar'))

        self._phi = (1 + np.sqrt(5)) / 2
Exemple #10
0
    def output_specification_from_data(self, data):
        """
        Returns the Hunt colour appearance model output specification
        from given data.

        Parameters
        ----------
        data : list
            Fixture data.

        Returns
        -------
        Hunt_Specification
            Hunt colour appearance model specification.
        """

        XYZ = tstack((data['X'], data['Y'], data['Z']))
        XYZ_w = tstack((data['X_w'], data['Y_w'], data['Z_w']))
        XYZ_b = tstack((data['X_w'], 0.2 * data['Y_w'], data['Z_w']))

        specification = XYZ_to_Hunt(XYZ,
                                    XYZ_w,
                                    XYZ_b,
                                    data['L_A'],
                                    Hunt_InductionFactors(
                                        data['N_c'],
                                        data['N_b']),
                                    CCT_w=data['T'])

        return specification
Exemple #11
0
def LCHab_to_Lab(LCHab):
    """
    Converts from *CIE L\\*C\\*Hab* colourspace to *CIE L\\*a\\*b\\**
    colourspace.

    Parameters
    ----------
    LCHab : array_like
        *CIE L\\*C\\*Hab* colourspace array.

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

    Notes
    -----

    +-------------+-----------------------+-----------------+
    | **Domain**  | **Scale - Reference** | **Scale - 1**   |
    +=============+=======================+=================+
    | ``LCHab``   | ``L``  : [0, 100]     | ``L``  : [0, 1] |
    |             |                       |                 |
    |             | ``C``  : [0, 100]     | ``C``  : [0, 1] |
    |             |                       |                 |
    |             | ``ab`` : [0, 360]     | ``ab`` : [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
    --------
    >>> LCHab = np.array([41.52787529, 59.12425901, 27.08848784])
    >>> LCHab_to_Lab(LCHab)  # doctest: +ELLIPSIS
    array([ 41.5278752...,  52.6385830...,  26.9231792...])
    """

    L, C, H = tsplit(LCHab)

    a, b = tsplit(
        polar_to_cartesian(tstack([C, np.radians(to_domain_degrees(H))])))

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

    return Lab
Exemple #12
0
def LCHuv_to_Luv(LCHuv):
    """
    Converts from *CIE L\\*C\\*Huv* colourspace to *CIE L\\*u\\*v\\**
    colourspace.

    Parameters
    ----------
    LCHuv : array_like
        *CIE L\\*C\\*Huv* colourspace array.

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

    Notes
    -----

    +------------+-----------------------+-----------------+
    | **Domain** | **Scale - Reference** | **Scale - 1**   |
    +============+=======================+=================+
    | ``LCHuv``  | ``L``  : [0, 100]     | ``L``  : [0, 1] |
    |            |                       |                 |
    |            | ``C``  : [0, 100]     | ``C``  : [0, 1] |
    |            |                       |                 |
    |            | ``uv`` : [0, 360]     | ``uv`` : [0, 1] |
    +------------+-----------------------+-----------------+

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

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

    Examples
    --------
    >>> LCHuv = np.array([41.52787529, 98.44997950, 10.38816348])
    >>> LCHuv_to_Luv(LCHuv)  # doctest: +ELLIPSIS
    array([ 41.5278752...,  96.8362605...,  17.7521014...])
    """

    L, C, H = tsplit(LCHuv)

    u, v = tsplit(
        polar_to_cartesian(tstack([C, np.radians(to_domain_degrees(H))])))

    Luv = tstack([L, u, v])

    return Luv
Exemple #13
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 in 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 = np.asarray(Y)

    beta = (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 / (B_0 ** beta)) + 1 - D) * (abs(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
Exemple #14
0
def xyY_to_XYZ(xyY):
    """
    Converts from *CIE xyY* colourspace to *CIE XYZ* tristimulus values.

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

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

    Notes
    -----

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

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

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

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

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

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

    return from_range_1(XYZ)
Exemple #15
0
def xy_to_UCS_uv(xy):
    """
    Returns the *CIE 1960 UCS* colourspace *uv* chromaticity coordinates from
    given *xy* chromaticity coordinates.

    Parameters
    ----------
    xy : array_like
        *xy* chromaticity coordinates.

    Returns
    -------
    ndarray
        *CIE UCS uv* chromaticity coordinates.

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

    Examples
    --------
    >>> xy = np.array([0.54369555, 0.32107941])
    >>> xy_to_UCS_uv(xy)  # doctest: +ELLIPSIS
    array([ 0.3772021...,  0.3341350...])
    """

    x, y = tsplit(xy)

    d = 12 * y - 2 * x + 3
    uv = tstack([4 * x / d, 6 * y / d])

    return uv
Exemple #16
0
def UCS_to_XYZ(UVW):
    """
    Converts from *CIE UCS* colourspace to *CIE XYZ* tristimulus values.

    Parameters
    ----------
    UVW : array_like
        *CIE UCS* colourspace array.

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

    Notes
    -----
    -   Input *CIE UCS* colourspace array is in domain [0, 1].
    -   Output *CIE XYZ* tristimulus values are in domain [0, 1].

    Examples
    --------
    >>> UVW = np.array([0.04699689, 0.10080000, 0.16374390])
    >>> UCS_to_XYZ(UVW)  # doctest: +ELLIPSIS
    array([ 0.0704953...,  0.1008    ,  0.0955831...])
    """

    U, V, W = tsplit(UVW)

    XYZ = tstack((3 / 2 * U, V, 3 / 2 * U - (3 * V) + (2 * W)))

    return XYZ
Exemple #17
0
def viewing_condition_dependent_parameters(Y_b, Y_w, L_A):
    """
    Returns the viewing condition dependent parameters.

    Parameters
    ----------
    Y_b : numeric or array_like
        Adapting field *Y* tristimulus value :math:`Y_b`.
    Y_w : numeric or array_like
        Whitepoint *Y* tristimulus value :math:`Y_w`.
    L_A : numeric or array_like
        Adapting field *luminance* :math:`L_A` in :math:`cd/m^2`.

    Returns
    -------
    ndarray
        Viewing condition dependent parameters.

    Examples
    --------
    >>> viewing_condition_dependent_parameters(  # doctest: +ELLIPSIS
    ...     20.0, 100.0, 318.31)
    array([ 0.2...,  1.1675444...,  1.000304...,  1.000304...,  1.9272136...])
    """

    Y_b = np.asarray(Y_b)
    Y_w = np.asarray(Y_w)

    n = Y_b / Y_w

    F_L = luminance_level_adaptation_factor(L_A)
    N_bb, N_cb = tsplit(chromatic_induction_factors(n))
    z = base_exponential_non_linearity(n)

    return tstack((n, F_L, N_bb, N_cb, z))
Exemple #18
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 = np.asarray(n)

    N_cbb = 0.725 * (1 / n) ** 0.2
    N_cbb = tstack((N_cbb, N_cbb))

    return N_cbb
Exemple #19
0
def UCS_uv_to_xy(uv):
    """
    Returns the *xy* chromaticity coordinates from given *CIE UCS* colourspace
    *uv* chromaticity coordinates.

    Parameters
    ----------
    uv : array_like
        *CIE UCS uv* chromaticity coordinates.

    Returns
    -------
    ndarray
        *xy* chromaticity coordinates.

    Notes
    -----
    -   Input *uv* chromaticity coordinates are in domain [0, 1].
    -   Output *xy* chromaticity coordinates are in domain [0, 1].

    Examples
    --------
    >>> uv = np.array([0.15085308732766581, 0.3235531372954405])
    >>> UCS_uv_to_xy(uv)  # doctest: +ELLIPSIS
    array([ 0.2641477...,  0.3777000...])
    """

    u, v = tsplit(uv)

    xy = tstack((3 * u / (2 * u - 8 * v + 4), 2 * v / (2 * u - 8 * v + 4)))

    return xy
Exemple #20
0
def XYZ_to_UCS(XYZ):
    """
    Converts from *CIE XYZ* tristimulus values to *CIE UCS* colourspace.

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

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

    Notes
    -----
    -   Input *CIE XYZ* tristimulus values are in domain [0, 1].
    -   Output *CIE UCS* colourspace array is in domain [0, 1].

    Examples
    --------
    >>> XYZ = np.array([0.07049534, 0.10080000, 0.09558313])
    >>> XYZ_to_UCS(XYZ)  # doctest: +ELLIPSIS
    array([ 0.0469968...,  0.1008    ,  0.1637439...])
    """

    X, Y, Z = tsplit(XYZ)

    UVW = tstack((2 / 3 * X, Y, 1 / 2 * (-X + 3 * Y + Z)))

    return UVW
Exemple #21
0
def opponent_colour_dimensions_forward(RGB):
    """
    Returns opponent colour dimensions from given compressed CMCCAT2000
    transform sharpened *RGB* array for forward CIECAM02 implementation

    Parameters
    ----------
    RGB : array_like
        Compressed CMCCAT2000 transform sharpened *RGB* array.

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

    Examples
    --------
    >>> RGB = np.array([7.94632020, 7.94711528, 7.94899595])
    >>> opponent_colour_dimensions_forward(RGB)  # doctest: +ELLIPSIS
    array([-0.0006241..., -0.0005062...])
    """

    R, G, B = tsplit(RGB)

    a = R - 12 * G / 11 + B / 11
    b = (R + G - 2 * B) / 9

    ab = tstack((a, b))

    return ab
Exemple #22
0
def UCS_to_uv(UVW):
    """
    Returns the *uv* chromaticity coordinates from given *CIE UCS* colourspace
    array.

    Parameters
    ----------
    UVW : array_like
        *CIE UCS* colourspace array.

    Returns
    -------
    ndarray
        *uv* chromaticity coordinates.

    Notes
    -----
    -   Input *CIE UCS* colourspace array is in domain [0, 1].
    -   Output *uv* chromaticity coordinates are in domain [0, 1].

    Examples
    --------
    >>> UCS = np.array([0.04699689, 0.10080000, 0.16374390])
    >>> UCS_to_uv(UCS)  # doctest: +ELLIPSIS
    array([ 0.1508530...,  0.3235531...])
    """

    U, V, W = tsplit(UVW)

    uv = tstack((U / (U + V + W), V / (U + V + W)))

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

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

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

    Notes
    -----

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

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

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

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

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

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

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

    return xyY
Exemple #24
0
def colour_difference_signals(rgb):
    """
    Returns the colour difference signals :math:`C_1`, :math:`C_2` and
    :math:`C_3` from given *Hunt-Pointer-Estevez* :math:`\\rho\gamma\\beta`
    colourspace array.

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

    Returns
    -------
    ndarray
        Colour difference signals :math:`C_1`, :math:`C_2` and :math:`C_3`.

    Examples
    --------
    >>> rgb = np.array([6.89594549, 6.89599915, 6.89657085])
    >>> colour_difference_signals(rgb)  # doctest: +ELLIPSIS
    array([ -5.3660000...e-05,  -5.7170000...e-04,   6.2536000...e-04])
    """

    r, g, b = tsplit(rgb)

    C_1 = r - g
    C_2 = g - b
    C_3 = b - r

    C = tstack((C_1, C_2, C_3))

    return C
Exemple #25
0
def exponential_factors(RGB_o):
    """
    Returns the chromatic adaptation exponential factors :math:`\\beta_1(R_o)`,
    :math:`\\beta_1(G_o)` and :math:`\\beta_2(B_o)` of given cone responses.

    Parameters
    ----------
    RGB_o: array_like
         Cone responses.

    Returns
    -------
    ndarray
        Chromatic adaptation exponential factors :math:`\\beta_1(R_o)`,
        :math:`\\beta_1(G_o)` and :math:`\\beta_2(B_o)`.

    Examples
    --------
    >>> RGB_o = np.array([318.32331631, 318.30352317, 318.23283482])
    >>> exponential_factors(RGB_o)  # doctest: +ELLIPSIS
    array([ 4.6106222...,  4.6105892...,  4.6520698...])
    """

    R_o, G_o, B_o = tsplit(RGB_o)

    bR_o = beta_1(R_o)
    bG_o = beta_1(G_o)
    bB_o = beta_2(B_o)

    bRGB_o = tstack((bR_o, bG_o, bB_o))

    return bRGB_o
    def test_range(self):
        """
        Tests :func:`colour.continuous.multi_signal.MultiSignal.range`
        property.
        """

        multi_signal = self._multi_signal.copy()

        np.testing.assert_almost_equal(
            multi_signal[np.array([0, 1, 2])],
            np.array([[10.0, 20.0, 30.0], [20.0, 30.0, 40.0],
                      [30.0, 40.0, 50.0]]),
            decimal=7)

        multi_signal.range = self._range_1 * 10

        np.testing.assert_array_equal(multi_signal.range,
                                      tstack([self._range_1] * 3) * 10)

        np.testing.assert_almost_equal(
            multi_signal[np.array([0, 1, 2])],
            np.array([[10.0, 10.0, 10.0], [20.0, 20.0, 20.0],
                      [30.0, 30.0, 30.0]]) * 10,
            decimal=7)

        multi_signal.range = self._range_2 * 10

        np.testing.assert_array_equal(multi_signal.range, self._range_2 * 10)

        np.testing.assert_almost_equal(
            multi_signal[np.array([0, 1, 2])],
            np.array([[10.0, 20.0, 30.0], [20.0, 30.0, 40.0],
                      [30.0, 40.0, 50.0]]) * 10,
            decimal=7)
Exemple #27
0
def LCHuv_to_Luv(LCHuv):
    """
    Converts from *CIE LCHuv* colourspace to *CIE Luv* colourspace.

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

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

    Notes
    -----
    -   Input / output :math:`L^*` is in domain / range [0, 100].

    References
    ----------
    .. [7]  Lindbloom, B. (2006). LCH(uv) to Luv. Retrieved February 24, 2014,
            from http://www.brucelindbloom.com/Eqn_LCH_to_Luv.html

    Examples
    --------
    >>> LCHuv = np.array([37.98562910, 28.83419279, 182.69946404])
    >>> LCHuv_to_Luv(LCHuv)  # doctest: +ELLIPSIS
    array([ 37.9856291..., -28.8021959...,  -1.3580070...])
    """

    L, C, H = tsplit(LCHuv)

    Luv = tstack((L, C * np.cos(np.radians(H)), C * np.sin(np.radians(H))))

    return Luv
Exemple #28
0
def spherical_to_cartesian(a):
    """
    Transforms given Spherical coordinates array to Cartesian coordinates.

    Parameters
    ----------
    a : array_like
        Spherical coordinates array (r, theta, phi) to transform.

    Returns
    -------
    ndarray
        Cartesian coordinates array (x, y, z).

    See Also
    --------
    cartesian_to_spherical, cartesian_to_cylindrical, cylindrical_to_cartesian

    Examples
    --------
    >>> a = np.array([6.78232998, 1.08574654, 0.32175055])
    >>> spherical_to_cartesian(a)  # doctest: +ELLIPSIS
    array([ 3.        ,  0.9999999...,  6.        ])
    """

    r, theta, phi = tsplit(a)

    x = r * np.cos(theta) * np.cos(phi)
    y = r * np.cos(theta) * np.sin(phi)
    z = r * np.sin(theta)

    xyz = tstack((x, y, z))

    return xyz
Exemple #29
0
def UCS_uv_to_xy(uv):
    """
    Returns the *xy* chromaticity coordinates from given *CIE 1960 UCS*
    colourspace *uv* chromaticity coordinates.

    Parameters
    ----------
    uv : array_like
        *CIE UCS uv* chromaticity coordinates.

    Returns
    -------
    ndarray
        *xy* chromaticity coordinates.

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

    Examples
    --------
    >>> uv = np.array([0.37720213, 0.33413508])
    >>> UCS_uv_to_xy(uv)  # doctest: +ELLIPSIS
    array([ 0.5436955...,  0.3210794...])
    """

    u, v = tsplit(uv)

    d = 2 * u - 8 * v + 4
    xy = tstack([3 * u / d, 2 * v / d])

    return xy
Exemple #30
0
def intermediate_values(xy_o):
    """
    Returns the intermediate values :math:`\\xi`, :math:`\eta`, :math:`\zeta`.

    Parameters
    ----------
    xy_o : array_like
        Chromaticity coordinates :math:`x_o` and :math:`y_o` of whitepoint.

    Returns
    -------
    ndarray
        Intermediate values :math:`\\xi`, :math:`\eta`, :math:`\zeta`.

    Examples
    --------
    >>> xy_o = np.array([0.4476, 0.4074])
    >>> intermediate_values(xy_o)  # doctest: +ELLIPSIS
    array([ 1.1185719...,  0.9329553...,  0.3268087...])
    """

    x_o, y_o = tsplit(xy_o)

    # Computing :math:`\xi`, :math:`\eta`, :math:`\zeta` values.
    xi = (0.48105 * x_o + 0.78841 * y_o - 0.08081) / y_o
    eta = (-0.27200 * x_o + 1.11962 * y_o + 0.04570) / y_o
    zeta = (0.91822 * (1 - x_o - y_o)) / y_o

    xez = tstack((xi, eta, zeta))

    return xez
Exemple #31
0
def UVW_to_XYZ(
        UVW,
        illuminant=ILLUMINANTS['CIE 1931 2 Degree Standard Observer']['D65']):
    """
    Converts *CIE 1964 U\\*V\\*W\\** colourspace to *CIE XYZ* tristimulus
    values.

    Parameters
    ----------
    UVW : array_like
        *CIE 1964 U\\*V\\*W\\** colourspace array.
    illuminant : array_like, optional
        Reference *illuminant* *xy* chromaticity coordinates or *CIE xyY*
        colourspace array.

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

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

    Notes
    -----

    +----------------+-----------------------+-----------------+
    | **Domain**     | **Scale - Reference** | **Scale - 1**   |
    +================+=======================+=================+
    | ``UVW``        | ``U`` : [-100, 100]   | ``U`` : [-1, 1] |
    |                |                       |                 |
    |                | ``V`` : [-100, 100]   | ``V`` : [-1, 1] |
    |                |                       |                 |
    |                | ``W`` : [0, 100]      | ``W`` : [0, 1]  |
    +----------------+-----------------------+-----------------+
    | ``illuminant`` | [0, 1]                | [0, 1]          |
    +----------------+-----------------------+-----------------+

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

    References
    ----------
    :cite:`Wikipedia2008a`

    Examples
    --------
    >>> import numpy as np
    >>> UVW = np.array([94.55035725, 11.55536523, 40.54757405])
    >>> UVW_to_XYZ(UVW)
    array([ 20.654008,  12.197225,   5.136952])
    """

    U, V, W = tsplit(to_domain_100(UVW))

    u_0, v_0 = tsplit(xy_to_UCS_uv(xyY_to_xy(illuminant)))

    Y = ((W + 17) / 25)**3
    u = U / (13 * W) + u_0
    v = V / (13 * W) + v_0

    x, y = tsplit(UCS_uv_to_xy(tstack([u, v])))

    XYZ = xyY_to_XYZ(tstack([x, y, Y]))

    return from_range_100(XYZ)
Exemple #32
0
def XYZ_to_Hunter_Rdab(
    XYZ,
    XYZ_n=HUNTERLAB_ILLUMINANTS['CIE 1931 2 Degree Standard Observer']
    ['D65'].XYZ_n,
    K_ab=HUNTERLAB_ILLUMINANTS['CIE 1931 2 Degree Standard Observer']
    ['D65'].K_ab):
    """
    Converts from *CIE XYZ* tristimulus values to *Hunter Rd,a,b* colour scale.

    Parameters
    ----------
    XYZ : array_like
        *CIE XYZ* tristimulus values.
    XYZ_n : array_like, optional
        Reference *illuminant* tristimulus values.
    K_ab : array_like, optional
        Reference *illuminant* chromaticity coefficients, if ``K_ab`` is set to
        *None* it will be computed using
        :func:`colour.XYZ_to_K_ab_HunterLab1966`.

    Returns
    -------
    ndarray
        *Hunter Rd,a,b* colour scale array.

    Notes
    -----

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

    +------------+------------------------+--------------------+
    | **Range**  | **Scale - Reference**  | **Scale - 1**      |
    +============+========================+====================+
    | ``R_d_ab`` | ``R_d``  : [0, 100]    | ``R_d`` : [0, 1]   |
    |            |                        |                    |
    |            | ``a_Rd`` : [-100, 100] | ``a_Rd`` : [-1, 1] |
    |            |                        |                    |
    |            | ``b_Rd`` : [-100, 100] | ``b_Rd`` : [-1, 1] |
    +------------+------------------------+--------------------+

    References
    ----------
    :cite:`HunterLab2012a`

    Examples
    --------
    >>> import numpy as np
    >>> XYZ = np.array([0.20654008, 0.12197225, 0.05136952]) * 100
    >>> D65 = HUNTERLAB_ILLUMINANTS[
    ...     'CIE 1931 2 Degree Standard Observer']['D65']
    >>> XYZ_to_Hunter_Rdab(XYZ, D65.XYZ_n, D65.K_ab)
    ... # doctest: +ELLIPSIS
    array([ 12.197225 ...,  57.1253787...,  17.4624134...])
    """

    X, Y, Z = tsplit(to_domain_100(XYZ))
    X_n, Y_n, Z_n = tsplit(to_domain_100(XYZ_n))
    K_a, K_b = (tsplit(XYZ_to_K_ab_HunterLab1966(XYZ_n))
                if K_ab is None else tsplit(K_ab))

    f = 0.51 * ((21 + 0.2 * Y) / (1 + 0.2 * Y))
    Y_Yn = Y / Y_n

    R_d = Y
    a_Rd = K_a * f * (X / X_n - Y_Yn)
    b_Rd = K_b * f * (Y_Yn - Z / Z_n)

    R_d_ab = tstack([R_d, a_Rd, b_Rd])

    return from_range_100(R_d_ab)
Exemple #33
0
def DIN99_to_Lab(Lab_99, k_E=1, k_CH=1):
    """
    Converts from *DIN99* colourspace to *CIE L\\*a\\*b\\** colourspace.

    Parameters
    ----------
    Lab_99 : array_like
        *DIN99* colourspace array.
    k_E : numeric, optional
        Parametric factor :math:`K_E` used to compensate for texture and other
        specimen presentation effects.
    k_CH : numeric, optional
        Parametric factor :math:`K_{CH}` used to compensate for texture and
        other specimen presentation effects.

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

    Notes
    -----

    +------------+------------------------+--------------------+
    | **Domain** | **Scale - Reference**  | **Scale - 1**      |
    +============+========================+====================+
    | ``Lab_99`` | ``L_99`` : [0, 100]    | ``L_99`` : [0, 1]  |
    |            |                        |                    |
    |            | ``a_99`` : [-100, 100] | ``a_99`` : [-1, 1] |
    |            |                        |                    |
    |            | ``b_99`` : [-100, 100] | ``b_99`` : [-1, 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:`ASTMInternational2007`

    Examples
    --------
    >>> import numpy as np
    >>> Lab_99 = np.array([53.22821988, 28.41634656, 3.89839552])
    >>> DIN99_to_Lab(Lab_99)  # doctest: +ELLIPSIS
    array([ 41.5278752...,  52.6385830...,  26.9231792...])
    """

    L_99, a_99, b_99 = tsplit(to_domain_100(Lab_99))

    cos_16 = np.cos(np.radians(16))
    sin_16 = np.sin(np.radians(16))

    h_99 = np.arctan2(b_99, a_99)

    C_99 = np.sqrt(a_99**2 + b_99**2)
    G = (np.exp(0.045 * C_99 * k_CH * k_E) - 1) / 0.045

    e = G * np.cos(h_99)
    f = G * np.sin(h_99)

    a = e * cos_16 - (f / 0.7) * sin_16
    b = e * sin_16 + (f / 0.7) * cos_16
    L = (np.exp(L_99 * k_E / 105.509) - 1) / 0.0158

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

    return from_range_100(Lab)
Exemple #34
0
def read_LUT_ResolveCube(path):
    """
    Reads given *Resolve* *.cube* *LUT* file.

    Parameters
    ----------
    path : unicode
        *LUT* path.

    Returns
    -------
    LUT2D or LUT3D or LUTSequence
        :class:`LUT2D` or :class:`LUT3D` or :class:`LUTSequence` class
        instance.

    References
    ----------
    :cite:`Chamberlain2015`

    Examples
    --------
    Reading a 2D *Resolve* *.cube* *LUT*:

    >>> import os
    >>> path = os.path.join(
    ...     os.path.dirname(__file__), 'tests', 'resources', 'resolve_cube',
    ...     'ACES_Proxy_10_to_ACES.cube')
    >>> print(read_LUT_ResolveCube(path))
    LUT2D - ACES Proxy 10 to ACES
    -----------------------------
    <BLANKLINE>
    Dimensions : 2
    Domain     : [[ 0.  0.  0.]
                  [ 1.  1.  1.]]
    Size       : (32, 3)

    Reading a 3D *Resolve* *.cube* *LUT*:

    >>> path = os.path.join(
    ...     os.path.dirname(__file__), 'tests', 'resources', 'resolve_cube',
    ...     'ColourCorrect.cube')
    >>> print(read_LUT_ResolveCube(path))
    LUT3D - Generated by Foundry::LUT
    ---------------------------------
    <BLANKLINE>
    Dimensions : 3
    Domain     : [[ 0.  0.  0.]
                  [ 1.  1.  1.]]
    Size       : (4, 4, 4, 3)

    Reading a 3D *Resolve* *.cube* *LUT* with comments:

    >>> path = os.path.join(
    ...     os.path.dirname(__file__), 'tests', 'resources', 'resolve_cube',
    ...     'Demo.cube')
    >>> print(read_LUT_ResolveCube(path))
    LUT2D - Demo
    ------------
    <BLANKLINE>
    Dimensions : 2
    Domain     : [[ 0.  0.  0.]
                  [ 3.  3.  3.]]
    Size       : (3, 3)
    Comment 01 : Comments can't go anywhere
    """

    title = path_to_title(path)
    size_2D = size_3D = 2
    table = []
    comments = []
    has_2D, has_3D = False, False

    with open(path) as cube_file:
        lines = cube_file.readlines()
        LUT = LUTSequence(LUT2D(), LUT3D())
        for line in lines:
            line = line.strip()

            if len(line) == 0:
                continue

            if line.startswith('#'):
                comments.append(line[1:].strip())
                continue

            tokens = line.split()
            if tokens[0] == 'TITLE':
                title = ' '.join(tokens[1:])[1:-1]
            elif tokens[0] == 'LUT_1D_INPUT_RANGE':
                domain = parse_array(tokens[1:])
                LUT[0].domain = tstack([domain, domain, domain])
            elif tokens[0] == 'LUT_3D_INPUT_RANGE':
                domain = parse_array(tokens[1:])
                LUT[1].domain = tstack([domain, domain, domain])
            elif tokens[0] == 'LUT_1D_SIZE':
                has_2D = True
                size_2D = np.int_(tokens[1])
            elif tokens[0] == 'LUT_3D_SIZE':
                has_3D = True
                size_3D = np.int_(tokens[1])
            else:
                table.append(parse_array(tokens))

    table = as_float_array(table)
    if has_2D and has_3D:
        LUT[0].name = '{0} - Shaper'.format(title)
        LUT[1].name = '{0} - Cube'.format(title)
        LUT[1].comments = comments
        LUT[0].table = table[:size_2D]
        # The lines of table data shall be in ascending index order,
        # with the first component index (Red) changing most rapidly,
        # and the last component index (Blue) changing least rapidly.
        LUT[1].table = table[size_2D:].reshape((size_3D, size_3D, size_3D, 3),
                                               order='F')
        return LUT
    elif has_2D:
        LUT[0].name = title
        LUT[0].comments = comments
        LUT[0].table = table
        return LUT[0]
    elif has_3D:
        LUT[1].name = title
        LUT[1].comments = comments
        # The lines of table data shall be in ascending index order,
        # with the first component index (Red) changing most rapidly,
        # and the last component index (Blue) changing least rapidly.
        table = table.reshape([size_3D, size_3D, size_3D, 3], order='F')
        LUT[1].table = table
        return LUT[1]
Exemple #35
0
def XYZ_to_JzAzBz(XYZ_D65, constants=CONSTANTS_JZAZBZ):
    """
    Converts from *CIE XYZ* tristimulus values to :math:`J_zA_zB_z`
    colourspace.

    Parameters
    ----------
    XYZ_D65 : array_like
        *CIE XYZ* tristimulus values under
        *CIE Standard Illuminant D Series D65*.
    constants : Structure, optional
        :math:`J_zA_zB_z` colourspace constants.

    Returns
    -------
    ndarray
        :math:`J_zA_zB_z` colourspace array where :math:`J_z` is Lightness,
        :math:`A_z` is redness-greenness and :math:`B_z` is
        yellowness-blueness.

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

    Notes
    -----

    -   The underlying *SMPTE ST 2084:2014* transfer function is an absolute
        transfer function, thus the domain and range values for the *Reference*
        and *1* scales are only indicative that the data is not affected by
        scale transformations. The effective domain of *SMPTE ST 2084:2014*
        inverse electro-optical transfer function (EOTF / EOCF) is
        [0.0001, 10000].

    +------------+-----------------------+------------------+
    | **Domain** | **Scale - Reference** | **Scale - 1**    |
    +============+=======================+==================+
    | ``XYZ``    | ``UN``                | ``UN``           |
    +------------+-----------------------+------------------+

    +------------+-----------------------+------------------+
    | **Range**  | **Scale - Reference** | **Scale - 1**    |
    +============+=======================+==================+
    | ``JzAzBz`` | ``Jz`` : [0, 1]       | ``Jz`` : [0, 1]  |
    |            |                       |                  |
    |            | ``Az`` : [-1, 1]      | ``Az`` : [-1, 1] |
    |            |                       |                  |
    |            | ``Bz`` : [-1, 1]      | ``Bz`` : [-1, 1] |
    +------------+-----------------------+------------------+

    References
    ----------
    :cite:`Safdar2017`

    Examples
    --------
    >>> XYZ = np.array([0.20654008, 0.12197225, 0.05136952])
    >>> XYZ_to_JzAzBz(XYZ)  # doctest: +ELLIPSIS
    array([ 0.0053504...,  0.0092430...,  0.0052600...])
    """

    X_D65, Y_D65, Z_D65 = tsplit(to_domain_1(XYZ_D65))

    X_p_D65 = constants.b * X_D65 - (constants.b - 1) * Z_D65
    Y_p_D65 = constants.g * Y_D65 - (constants.g - 1) * X_D65

    XYZ_p_D65 = tstack([X_p_D65, Y_p_D65, Z_D65])

    LMS = vector_dot(MATRIX_JZAZBZ_XYZ_TO_LMS, XYZ_p_D65)

    with domain_range_scale('ignore'):
        LMS_p = eotf_inverse_ST2084(LMS, 10000, constants)

    I_z, A_z, B_z = tsplit(vector_dot(MATRIX_JZAZBZ_LMS_P_TO_IZAZBZ, LMS_p))

    J_z = ((1 + constants.d) * I_z) / (1 + constants.d * I_z) - constants.d_0

    JzAzBz = tstack([J_z, A_z, B_z])

    return from_range_1(JzAzBz)
Exemple #36
0
def RGB_to_HSL(RGB):
    """
    Converts from *RGB* colourspace to *HSL* colourspace.

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

    Returns
    -------
    ndarray
        *HSL* array.

    Notes
    -----

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

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

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

    Examples
    --------
    >>> RGB = np.array([0.45620519, 0.03081071, 0.04091952])
    >>> RGB_to_HSL(RGB)  # doctest: +ELLIPSIS
    array([ 0.9960394...,  0.8734714...,  0.2435079...])
    """

    RGB = to_domain_1(RGB)

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

    R, G, B = tsplit(RGB)

    L = (maximum + minimum) / 2

    S = np.where(
        L < 0.5,
        delta / (maximum + minimum),
        delta / (2 - maximum - minimum),
    )
    S[np.asarray(delta == 0)] = 0

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

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

    HSL = tstack([H, S, L])

    return from_range_1(HSL)
Exemple #37
0
def Hunter_Rdab_to_XYZ(
    R_d_ab,
    XYZ_n=HUNTERLAB_ILLUMINANTS['CIE 1931 2 Degree Standard Observer']
    ['D65'].XYZ_n,
    K_ab=HUNTERLAB_ILLUMINANTS['CIE 1931 2 Degree Standard Observer']
    ['D65'].K_ab):
    """
    Converts from *Hunter Rd,a,b* colour scale to *CIE XYZ* tristimulus values.

    Parameters
    ----------
    R_d_ab : array_like
        *Hunter Rd,a,b* colour scale array.
    XYZ_n : array_like, optional
        Reference *illuminant* tristimulus values.
    K_ab : array_like, optional
        Reference *illuminant* chromaticity coefficients, if ``K_ab`` is set to
        *None* it will be computed using
        :func:`colour.XYZ_to_K_ab_HunterLab1966`.

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

    Notes
    -----

    +------------+------------------------+--------------------+
    | **Domain** | **Scale - Reference**  | **Scale - 1**      |
    +============+========================+====================+
    | ``R_d_ab`` | ``R_d``  : [0, 100]    | ``R_d`` : [0, 1]   |
    |            |                        |                    |
    |            | ``a_Rd`` : [-100, 100] | ``a_Rd`` : [-1, 1] |
    |            |                        |                    |
    |            | ``b_Rd`` : [-100, 100] | ``b_Rd`` : [-1, 1] |
    +------------+------------------------+--------------------+
    | ``XYZ_n``  | [0, 100]               | [0, 1]             |
    +------------+------------------------+--------------------+

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

    References
    ----------
    :cite:`HunterLab2012a`

    Examples
    --------
    >>> import numpy as np
    >>> R_d_ab = np.array([12.19722500, 57.12537874, 17.46241341])
    >>> D65 = HUNTERLAB_ILLUMINANTS[
    ...     'CIE 1931 2 Degree Standard Observer']['D65']
    >>> Hunter_Rdab_to_XYZ(R_d_ab, D65.XYZ_n, D65.K_ab)
    array([ 20.654008,  12.197225,   5.136952])
    """

    R_d, a_Rd, b_Rd = tsplit(to_domain_100(R_d_ab))
    X_n, Y_n, Z_n = tsplit(to_domain_100(XYZ_n))
    K_a, K_b = (tsplit(XYZ_to_K_ab_HunterLab1966(XYZ_n))
                if K_ab is None else tsplit(K_ab))

    f = 0.51 * ((21 + 0.2 * R_d) / (1 + 0.2 * R_d))
    Rd_Yn = R_d / Y_n
    X = (a_Rd / (K_a * f) + Rd_Yn) * X_n
    Z = -(b_Rd / (K_b * f) - Rd_Yn) * Z_n

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

    return from_range_100(XYZ)
Exemple #38
0
def HSV_to_RGB(HSV):
    """
    Converts from *HSV* colourspace to *RGB* colourspace.

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

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

    Notes
    -----

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

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

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

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

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

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

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

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

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

    return from_range_1(RGB)
Exemple #39
0
def XYZ_to_UVW(
        XYZ,
        illuminant=ILLUMINANTS['CIE 1931 2 Degree Standard Observer']['D65']):
    """
    Converts from *CIE XYZ* tristimulus values to *CIE 1964 U\\*V\\*W\\**
    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 1964 U\\*V\\*W\\** colourspace array.

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

    Notes
    -----

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

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

    References
    ----------
    :cite:`Wikipedia2008a`

    Examples
    --------
    >>> import numpy as np
    >>> XYZ = np.array([0.20654008, 0.12197225, 0.05136952]) * 100
    >>> XYZ_to_UVW(XYZ)  # doctest: +ELLIPSIS
    array([ 94.5503572...,  11.5553652...,  40.5475740...])
    """

    XYZ = to_domain_100(XYZ)

    xy = xyY_to_xy(illuminant)
    xyY = XYZ_to_xyY(XYZ, xy)
    _x, _y, Y = tsplit(xyY)

    u, v = tsplit(UCS_to_uv(XYZ_to_UCS(XYZ)))
    u_0, v_0 = tsplit(xy_to_UCS_uv(xy))

    W = 25 * spow(Y, 1 / 3) - 17
    U = 13 * W * (u - u_0)
    V = 13 * W * (v - v_0)

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

    return from_range_100(UVW)
Exemple #40
0
def XYZ_to_Luv(
        XYZ,
        illuminant=ILLUMINANTS['CIE 1931 2 Degree Standard Observer']['D65']):
    """
    Converts from *CIE XYZ* tristimulus values to *CIE L\\*u\\*v\\**
    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\\*u\\*v\\** colourspace array.

    Notes
    -----

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

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

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

    Examples
    --------
    >>> XYZ = np.array([0.20654008, 0.12197225, 0.05136952])
    >>> XYZ_to_Luv(XYZ)  # doctest: +ELLIPSIS
    array([ 41.5278752...,  96.8362605...,  17.7521014...])
    """

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

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

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

    u = (13 * L * ((4 * X / (X + 15 * Y + 3 * Z)) -
                   (4 * X_r / (X_r + 15 * Y_r + 3 * Z_r))))
    v = (13 * L * ((9 * Y / (X + 15 * Y + 3 * Z)) -
                   (9 * Y_r / (X_r + 15 * Y_r + 3 * Z_r))))

    Luv = tstack([L, u, v])

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

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

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

    Notes
    -----

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

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

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

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

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

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

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

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

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

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

    return from_range_1(XYZ)
Exemple #42
0
def JzAzBz_to_XYZ(JzAzBz, constants=CONSTANTS_JZAZBZ):
    """
    Converts from :math:`J_zA_zB_z` colourspace to *CIE XYZ* tristimulus
    values.

    Parameters
    ----------
    JzAzBz : array_like
        :math:`J_zA_zB_z` colourspace array  where :math:`J_z` is Lightness,
        :math:`A_z` is redness-greenness and :math:`B_z` is
        yellowness-blueness.
    constants : Structure, optional
        :math:`J_zA_zB_z` colourspace constants.

    Returns
    -------
    ndarray
        *CIE XYZ* tristimulus values under
        *CIE Standard Illuminant D Series D65*.

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

    Notes
    -----

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

    +------------+-----------------------+------------------+
    | **Domain** | **Scale - Reference** | **Scale - 1**    |
    +============+=======================+==================+
    | ``JzAzBz`` | ``Jz`` : [0, 1]       | ``Jz`` : [0, 1]  |
    |            |                       |                  |
    |            | ``Az`` : [-1, 1]      | ``Az`` : [-1, 1] |
    |            |                       |                  |
    |            | ``Bz`` : [-1, 1]      | ``Bz`` : [-1, 1] |
    +------------+-----------------------+------------------+

    +------------+-----------------------+------------------+
    | **Range**  | **Scale - Reference** | **Scale - 1**    |
    +============+=======================+==================+
    | ``XYZ``    | ``UN``                | ``UN``           |
    +------------+-----------------------+------------------+

    References
    ----------
    :cite:`Safdar2017`

    Examples
    --------
    >>> JzAzBz = np.array([0.00535048, 0.00924302, 0.00526007])
    >>> JzAzBz_to_XYZ(JzAzBz)  # doctest: +ELLIPSIS
    array([ 0.2065402...,  0.1219723...,  0.0513696...])
    """

    J_z, A_z, B_z = tsplit(to_domain_1(JzAzBz))

    I_z = ((J_z + constants.d_0) / (1 + constants.d - constants.d *
                                    (J_z + constants.d_0)))
    LMS_p = vector_dot(MATRIX_JZAZBZ_IZAZBZ_TO_LMS_P, tstack([I_z, A_z, B_z]))

    with domain_range_scale('ignore'):
        LMS = eotf_ST2084(LMS_p, 10000, constants)

    X_p_D65, Y_p_D65, Z_p_D65 = tsplit(
        vector_dot(MATRIX_JZAZBZ_LMS_TO_XYZ, LMS))

    X_D65 = (X_p_D65 + (constants.b - 1) * Z_p_D65) / constants.b
    Y_D65 = (Y_p_D65 + (constants.g - 1) * X_D65) / constants.g

    XYZ_D65 = tstack([X_D65, Y_D65, Z_p_D65])

    return from_range_1(XYZ_D65)
Exemple #43
0
def whiteness_CIE2004(
    xy: ArrayLike,
    Y: FloatingOrNDArray,
    xy_n: ArrayLike,
    observer: Literal["CIE 1931 2 Degree Standard Observer",
                      "CIE 1964 10 Degree Standard Observer", ] = (
                          "CIE 1931 2 Degree Standard Observer"),
) -> NDArray:
    """
    Return the *whiteness* :math:`W` or :math:`W_{10}` and *tint* :math:`T`
    or :math:`T_{10}` of given sample *CIE xy* chromaticity coordinates using
    *CIE 2004* method.

    Parameters
    ----------
    xy
        Chromaticity coordinates *CIE xy* of the sample.
    Y
        Tristimulus :math:`Y` value of the sample.
    xy_n
        Chromaticity coordinates *xy_n* of a perfect diffuser.
    observer
        *CIE Standard Observer* used for computations, *tint* :math:`T` or
        :math:`T_{10}` value is dependent on viewing field angular subtense.

    Returns
    -------
    :class:`numpy.ndarray`
        *Whiteness* :math:`W` or :math:`W_{10}` and *tint* :math:`T` or
        :math:`T_{10}` of given sample.

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

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

    -   This method may be used only for samples whose values of :math:`W` or
        :math:`W_{10}` lie within the following limits: greater than 40 and
        less than 5Y - 280, or 5Y10 - 280.
    -   This method may be used only for samples whose values of :math:`T` or
        :math:`T_{10}` lie within the following limits: greater than -4 and
        less than +2.
    -   Output *whiteness* :math:`W` or :math:`W_{10}` values larger than 100
        indicate a bluish white while values smaller than 100 indicate a
        yellowish white.
    -   Positive output *tint* :math:`T` or :math:`T_{10}` values indicate a
        greener tint while negative values indicate a redder tint.

    References
    ----------
    :cite:`CIETC1-482004k`

    Examples
    --------
    >>> import numpy as np
    >>> xy = np.array([0.3167, 0.3334])
    >>> xy_n = np.array([0.3139, 0.3311])
    >>> whiteness_CIE2004(xy, 100, xy_n)  # doctest: +ELLIPSIS
    array([ 93.85...,  -1.305...])
    """

    x, y = tsplit(xy)
    Y = to_domain_100(Y)
    x_n, y_n = tsplit(xy_n)

    W = Y + 800 * (x_n - x) + 1700 * (y_n - y)
    T = (1000 if "1931" in observer else 900) * (x_n - x) - 650 * (y_n - y)

    WT = tstack([W, T])

    return from_range_100(WT)
Exemple #44
0
def polynomial_expansion_Finlayson2015(RGB,
                                       degree=1,
                                       root_polynomial_expansion=True):
    """
    Performs polynomial expansion of given *RGB* colourspace array using
    *Finlayson et al. (2015)* method.

    Parameters
    ----------
    RGB : array_like
        *RGB* colourspace array to expand.
    degree : int, optional
        Expanded polynomial degree.
    root_polynomial_expansion : bool
        Whether to use the root-polynomials set for the expansion.

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

    References
    ----------
    :cite:`Finlayson2015`

    Examples
    --------
    >>> RGB = np.array([0.17224810, 0.09170660, 0.06416938])
    >>> polynomial_expansion_Finlayson2015(RGB, degree=2)  # doctest: +ELLIPSIS
    array([ 0.1722481...,  0.0917066...,  0.06416938...\
,  0.0078981...,  0.0029423...,
            0.0055265...])
    """

    R, G, B = tsplit(RGB)

    # TODO: Generalise polynomial expansion.
    existing_degrees = np.array([1, 2, 3, 4])
    closest_degree = as_int(closest(existing_degrees, degree))
    if closest_degree != degree:
        raise ValueError('"Finlayson et al. (2015)" method does not define '
                         'a polynomial expansion for {0} degree, '
                         'closest polynomial expansion is {1} degree!'.format(
                             degree, closest_degree))

    if degree == 1:
        return RGB
    elif degree == 2:
        if root_polynomial_expansion:
            return tstack([
                R, G, B, (R * G) ** 1 / 2, (G * B) ** 1 / 2, (R * B) ** 1 / 2
            ])

        else:
            return tstack(
                [R, G, B, R ** 2, G ** 2, B ** 2, R * G, G * B, R * B])
    elif degree == 3:
        if root_polynomial_expansion:
            return tstack([
                R, G, B, (R * G) ** 1 / 2, (G * B) ** 1 / 2, (R * B) ** 1 / 2,
                (R * G ** 2) ** 1 / 3, (G * B ** 2) ** 1 / 3,
                (R * B ** 2) ** 1 / 3, (G * R ** 2) ** 1 / 3,
                (B * G ** 2) ** 1 / 3, (B * R ** 2) ** 1 / 3,
                (R * G * B) ** 1 / 3
            ])
        else:
            return tstack([
                R, G, B, R ** 2, G ** 2, B ** 2, R * G, G * B, R * B, R ** 3, G
                ** 3, B ** 3, R * G ** 2, G * B ** 2, R * B ** 2, G * R ** 2,
                B * G ** 2, B * R ** 2, R * G * B
            ])
    elif degree == 4:
        if root_polynomial_expansion:
            return tstack([
                R, G, B, (R * G) ** 1 / 2, (G * B) ** 1 / 2, (R * B) ** 1 / 2,
                (R * G ** 2) ** 1 / 3, (G * B ** 2) ** 1 / 3,
                (R * B ** 2) ** 1 / 3, (G * R ** 2) ** 1 / 3,
                (B * G ** 2) ** 1 / 3, (B * R ** 2) ** 1 / 3,
                (R * G * B) ** 1 / 3, (R ** 3 * G) ** 1 / 4,
                (R ** 3 * B) ** 1 / 4, (G ** 3 * R) ** 1 / 4,
                (G ** 3 * B) ** 1 / 4, (B ** 3 * R) ** 1 / 4,
                (B ** 3 * G) ** 1 / 4, (R ** 2 * G * B) ** 1 / 4,
                (G ** 2 * R * B) ** 1 / 4, (B ** 2 * R * G) ** 1 / 4
            ])
        else:
            return tstack([
                R, G, B, R ** 2, G ** 2, B ** 2, R * G, G * B, R * B, R ** 3, G
                ** 3, B ** 3, R * G ** 2, G * B ** 2, R * B ** 2, G * R ** 2,
                B * G ** 2, B * R ** 2, R * G * B, R ** 4, G ** 4, B ** 4,
                R ** 3 * G, R ** 3 * B, G ** 3 * R, G ** 3 * B, B ** 3 * R,
                B ** 3 * G, R ** 2 * G ** 2, G ** 2 * B ** 2, R ** 2 * B ** 2,
                R ** 2 * G * B, G ** 2 * R * B, B ** 2 * R * G
            ])
Exemple #45
0
def HSL_to_RGB(HSL):
    """
    Converts from *HSL* colourspace to *RGB* colourspace.

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

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

    Notes
    -----

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

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

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

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

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

    def H_to_RGB(vi, vj, vH):
        """
        Converts *hue* value to *RGB* colourspace.
        """

        vH = as_float_array(vH)

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

        v = np.full(vi.shape, np.nan)

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

        return v

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

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

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

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

    return from_range_1(RGB)
Exemple #46
0
def augmented_matrix_Cheung2004(RGB, terms=3):
    """
    Performs polynomial expansion of given *RGB* colourspace array using
    *Cheung et al. (2004)* method.

    Parameters
    ----------
    RGB : array_like
        *RGB* colourspace array to expand.
    terms : int, optional
        Number of terms of the expanded polynomial, must be one of
        *[3, 5, 7, 8, 10, 11, 14, 16, 17, 19, 20, 22]*.

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

    Notes
    -----
    -   This definition combines the augmented matrices given in
        :cite:`Cheung2004` and :cite:`Westland2004`.

    References
    ----------
    :cite:`Cheung2004`, :cite:`Westland2004`

    Examples
    --------
    >>> RGB = np.array([0.17224810, 0.09170660, 0.06416938])
    >>> augmented_matrix_Cheung2004(RGB, terms=5)  # doctest: +ELLIPSIS
    array([ 0.1722481...,  0.0917066...,  0.0641693...,  0.0010136...,  1...])
    """

    R, G, B = tsplit(RGB)
    ones = np.ones(R.shape)

    existing_terms = np.array([3, 5, 7, 8, 10, 11, 14, 16, 17, 19, 20, 22])
    closest_terms = as_int(closest(existing_terms, terms))
    if closest_terms != terms:
        raise ValueError('"Cheung et al. (2004)" method does not define '
                         'an augmented matrix with {0} terms, '
                         'closest augmented matrix has {1} terms!'.format(
                             terms, closest_terms))

    if terms == 3:
        return RGB
    elif terms == 5:
        return tstack([R, G, B, R * G * B, ones])
    elif terms == 7:
        return tstack([R, G, B, R * G, R * B, G * B, ones])
    elif terms == 8:
        return tstack([R, G, B, R * G, R * B, G * B, R * G * B, ones])
    elif terms == 10:
        return tstack(
            [R, G, B, R * G, R * B, G * B, R ** 2, G ** 2, B ** 2, ones])
    elif terms == 11:
        return tstack([
            R, G, B, R * G, R * B, G * B, R ** 2, G ** 2, B ** 2, R * G * B,
            ones
        ])
    elif terms == 14:
        return tstack([
            R, G, B, R * G, R * B, G * B, R ** 2, G ** 2, B ** 2, R * G * B, R
            ** 3, G ** 3, B ** 3, ones
        ])
    elif terms == 16:
        return tstack([
            R, G, B, R * G, R * B, G * B, R ** 2, G ** 2, B ** 2, R * G * B,
            R ** 2 * G, G ** 2 * B, B ** 2 * R, R ** 3, G ** 3, B ** 3
        ])
    elif terms == 17:
        return tstack([
            R, G, B, R * G, R * B, G * B, R ** 2, G ** 2, B ** 2, R * G * B,
            R ** 2 * G, G ** 2 * B, B ** 2 * R, R ** 3, G ** 3, B ** 3, ones
        ])
    elif terms == 19:
        return tstack([
            R, G, B, R * G, R * B, G * B, R ** 2, G ** 2, B ** 2, R * G * B,
            R ** 2 * G, G ** 2 * B, B ** 2 * R, R ** 2 * B, G ** 2 * R,
            B ** 2 * G, R ** 3, G ** 3, B ** 3
        ])
    elif terms == 20:
        return tstack([
            R, G, B, R * G, R * B, G * B, R ** 2, G ** 2, B ** 2, R * G * B,
            R ** 2 * G, G ** 2 * B, B ** 2 * R, R ** 2 * B, G ** 2 * R,
            B ** 2 * G, R ** 3, G ** 3, B ** 3, ones
        ])
    elif terms == 22:
        return tstack([
            R, G, B, R * G, R * B, G * B, R ** 2, G ** 2, B ** 2, R * G * B,
            R ** 2 * G, G ** 2 * B, B ** 2 * R, R ** 2 * B, G ** 2 * R,
            B ** 2 * G, R ** 3, G ** 3, B ** 3, R ** 2 * G * B, R * G ** 2 * B,
            R * G * B ** 2
        ])
Exemple #47
0
def UCS_Luo2006_to_JMh_CIECAM02(Jpapbp, coefficients):
    """
    Converts from one of the *Luo et al. (2006)* *CAM02-LCD*, *CAM02-SCD*, or
    *CAM02-UCS* colourspaces :math:`J'a'b'` array to *CIECAM02* :math:`JMh`
    correlates array.

    Parameters
    ----------
    Jpapbp : array_like
        *Luo et al. (2006)* *CAM02-LCD*, *CAM02-SCD*, or *CAM02-UCS*
        colourspaces :math:`J'a'b'` array.
    coefficients : array_like
        Coefficients of one of the *Luo et al. (2006)* *CAM02-LCD*,
        *CAM02-SCD*, or *CAM02-UCS* colourspaces.

    Returns
    -------
    ndarray
        *CIECAM02* correlates array :math:`JMh`.

    Notes
    -----

    +------------+------------------------+--------------------+
    | **Domain** |  **Scale - Reference** | **Scale - 1**      |
    +============+========================+====================+
    | ``Jpapbp`` | ``Jp_1`` : [0, 100]    | ``Jp_1`` : [0, 1]  |
    |            |                        |                    |
    |            | ``ap_1`` : [-100, 100] | ``ap_1`` : [-1, 1] |
    |            |                        |                    |
    |            | ``bp_1`` : [-100, 100] | ``bp_1`` : [-1, 1] |
    +------------+------------------------+--------------------+

    +------------+------------------------+--------------------+
    | **Range**  |  **Scale - Reference** | **Scale - 1**      |
    +============+========================+====================+
    | ``JMh``    | ``J`` : [0, 100]       | ``J`` : [0, 1]     |
    |            |                        |                    |
    |            | ``M`` : [0, 100]       | ``M`` : [0, 1]     |
    |            |                        |                    |
    |            | ``h`` : [0, 360]       | ``h`` : [0, 1]     |
    +------------+------------------------+--------------------+

    Examples
    --------
    >>> Jpapbp = np.array([54.90433134, -0.08450395, -0.06854831])
    >>> UCS_Luo2006_to_JMh_CIECAM02(
    ...     Jpapbp, COEFFICIENTS_UCS_LUO2006['CAM02-LCD'])
    ... # doctest: +ELLIPSIS
    array([  4.1731091...e+01,   1.0884217...e-01,   2.1904843...e+02])
    """

    J_p, a_p, b_p = tsplit(to_domain_100(Jpapbp))
    _K_L, c_1, c_2 = tsplit(coefficients)

    J = -J_p / (c_1 * J_p - 1 - 100 * c_1)

    M_p, h = tsplit(cartesian_to_polar(tstack([a_p, b_p])))

    M = (np.exp(M_p / (1 / c_2)) - 1) / c_2

    JMh = tstack([
        from_range_100(J),
        from_range_100(M),
        from_range_degrees(np.degrees(h) % 360)
    ])

    return JMh
Exemple #48
0
def corresponding_colour(RGB_1, xez_1, xez_2, bRGB_o1, bRGB_o2, Y_o, K, n=1):
    """
    Computes the corresponding colour cone responses of given test sample cone
    responses :math:`RGB_1`.

    Parameters
    ----------
    RGB_1: array_like
        Test sample cone responses :math:`RGB_1`.
    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 in
        domain [18, 100].
    K : numeric or array_like
        Coefficient :math:`K`.
    n : numeric or array_like, optional
        Noise component in fundamental primary system.

    Returns
    -------
    ndarray
        Corresponding colour cone responses of given test sample cone
        responses.

    Examples
    --------
    >>> RGB_1 = np.array([25.82442730, 18.67914220, 4.83901940])
    >>> 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 = 1.0
    >>> corresponding_colour(  # doctest: +ELLIPSIS
    ...     RGB_1, xez_1, xez_2, bRGB_o1, bRGB_o2, Y_o, K)
    array([ 23.1636901...,  20.0211948...,  16.2001664...])
    """

    R_1, G_1, B_1 = tsplit(RGB_1)
    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 = np.asarray(Y_o)
    K = np.asarray(K)

    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) * K**(1 / y_2) *
                ((z + n) / (Y_o * x_1 + n))**(y_1 / y_2) - n)

    R_2 = RGB_c(xi_1, xi_2, bR_o1, bR_o2, R_1)
    G_2 = RGB_c(eta_1, eta_2, bG_o1, bG_o2, G_1)
    B_2 = RGB_c(zeta_1, zeta_2, bB_o1, bB_o2, B_1)

    RGB_2 = tstack((R_2, G_2, B_2))

    return RGB_2
Exemple #49
0
def whiteness_Ganz1979(xy: ArrayLike, Y: FloatingOrNDArray) -> NDArray:
    """
    Return the *whiteness* index :math:`W` and *tint* :math:`T` of given
    sample *CIE xy* chromaticity coordinates using *Ganz and Griesser (1979)*
    method.

    Parameters
    ----------
    xy
        Chromaticity coordinates *CIE xy* of the sample.
    Y
        Tristimulus :math:`Y` value of the sample.

    Returns
    -------
    :class:`numpy.ndarray`
        *Whiteness* :math:`W` and *tint* :math:`T`.

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

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

    -   The formula coefficients are valid for
        *CIE Standard Illuminant D Series* *D65* and
        *CIE 1964 10 Degree Standard Observer*.
    -   Positive output *tint* :math:`T` values indicate a greener tint while
        negative values indicate a redder tint.
    -   Whiteness differences of less than 5 Ganz units appear to be
        indistinguishable to the human eye.
    -   Tint differences of less than 0.5 Ganz units appear to be
        indistinguishable to the human eye.

    References
    ----------
    :cite:`X-Rite2012a`

    Examples
    --------
    >>> import numpy as np
    >>> xy = np.array([0.3167, 0.3334])
    >>> whiteness_Ganz1979(xy, 100)  # doctest: +ELLIPSIS
    array([ 85.6003766...,   0.6789003...])
    """

    x, y = tsplit(xy)
    Y = to_domain_100(Y)

    W = Y - 1868.322 * x - 3695.690 * y + 1809.441
    T = -1001.223 * x + 748.366 * y + 68.261

    WT = tstack([W, T])

    return from_range_100(WT)
Exemple #50
0
def JMh_CIECAM02_to_UCS_Luo2006(JMh, coefficients):
    """
    Converts from *CIECAM02* :math:`JMh` correlates array to one of the
    *Luo et al. (2006)* *CAM02-LCD*, *CAM02-SCD*, or *CAM02-UCS* colourspaces
    :math:`J'a'b'` array.

    The :math:`JMh` correlates array is constructed using the CIECAM02
    correlate of *Lightness* :math:`J`, the *CIECAM02* correlate of
    *colourfulness* :math:`M` and the *CIECAM02* *Hue* angle :math:`h` in
    degrees.

    Parameters
    ----------
    JMh : array_like
        *CIECAM02* correlates array :math:`JMh`.
    coefficients : array_like
        Coefficients of one of the *Luo et al. (2006)* *CAM02-LCD*,
        *CAM02-SCD*, or *CAM02-UCS* colourspaces.

    Returns
    -------
    ndarray
        *Luo et al. (2006)* *CAM02-LCD*, *CAM02-SCD*, or *CAM02-UCS*
        colourspaces :math:`J'a'b'` array.

    Notes
    -----

    +------------+------------------------+--------------------+
    | **Domain** |  **Scale - Reference** | **Scale - 1**      |
    +============+========================+====================+
    | ``JMh``    | ``J`` : [0, 100]       | ``J`` : [0, 1]     |
    |            |                        |                    |
    |            | ``M`` : [0, 100]       | ``M`` : [0, 1]     |
    |            |                        |                    |
    |            | ``h`` : [0, 360]       | ``h`` : [0, 1]     |
    +------------+------------------------+--------------------+

    +------------+------------------------+--------------------+
    | **Range**  |  **Scale - Reference** | **Scale - 1**      |
    +============+========================+====================+
    | ``Jpapbp`` | ``Jp_1`` : [0, 100]    | ``Jp_1`` : [0, 1]  |
    |            |                        |                    |
    |            | ``ap_1`` : [-100, 100] | ``ap_1`` : [-1, 1] |
    |            |                        |                    |
    |            | ``bp_1`` : [-100, 100] | ``bp_1`` : [-1, 1] |
    +------------+------------------------+--------------------+

    Examples
    --------
    >>> from colour.appearance import (
    ...     CIECAM02_VIEWING_CONDITIONS,
    ...     XYZ_to_CIECAM02)
    >>> XYZ = np.array([19.01, 20.00, 21.78])
    >>> XYZ_w = np.array([95.05, 100.00, 108.88])
    >>> L_A = 318.31
    >>> Y_b = 20.0
    >>> surround = CIECAM02_VIEWING_CONDITIONS['Average']
    >>> specification = XYZ_to_CIECAM02(
    ...     XYZ, XYZ_w, L_A, Y_b, surround)
    >>> JMh = (specification.J, specification.M, specification.h)
    >>> JMh_CIECAM02_to_UCS_Luo2006(JMh, COEFFICIENTS_UCS_LUO2006['CAM02-LCD'])
    ... # doctest: +ELLIPSIS
    array([ 54.9043313...,  -0.0845039...,  -0.0685483...])
    """

    J, M, h = tsplit(JMh)
    J = to_domain_100(J)
    M = to_domain_100(M)
    h = to_domain_degrees(h)

    _K_L, c_1, c_2 = tsplit(coefficients)

    J_p = ((1 + 100 * c_1) * J) / (1 + c_1 * J)
    M_p = (1 / c_2) * np.log(1 + c_2 * M)

    a_p, b_p = tsplit(polar_to_cartesian(tstack([M_p, np.radians(h)])))

    Jpapbp = tstack([J_p, a_p, b_p])

    return from_range_100(Jpapbp)
Exemple #51
0
def msds_cmfs_anomalous_trichromacy_Machado2009(
        cmfs: LMS_ConeFundamentals, d_LMS: ArrayLike) -> LMS_ConeFundamentals:
    """
    Shift given *LMS* cone fundamentals colour matching functions with given
    :math:`\\Delta_{LMS}` shift amount in nanometers to simulate anomalous
    trichromacy using *Machado et al. (2009)* method.

    Parameters
    ----------
    cmfs
        *LMS* cone fundamentals colour matching functions.
    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:`colour.LMS_ConeFundamentals`
        Anomalous trichromacy *LMS* cone fundamentals colour matching
        functions.

    Warnings
    --------
    *Machado et al. (2009)* simulation of tritanomaly is based on the shift
    paradigm as an approximation to the actual phenomenon and restrain the
    model from trying to model tritanopia.
    The pre-generated matrices are using a shift value in domain [5, 59]
    contrary to the domain [0, 20] used for protanomaly and deuteranomaly
    simulation.

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

    Examples
    --------
    >>> from colour.colorimetry import MSDS_CMFS_LMS
    >>> cmfs = MSDS_CMFS_LMS['Stockman & Sharpe 2 Degree Cone Fundamentals']
    >>> cmfs[450]
    array([ 0.0498639,  0.0870524,  0.955393 ])
    >>> msds_cmfs_anomalous_trichromacy_Machado2009(
    ...     cmfs, np.array([15, 0, 0]))[450]  # doctest: +ELLIPSIS
    array([ 0.0891288...,  0.0870524 ,  0.955393  ])
    """

    cmfs = cast(LMS_ConeFundamentals, cmfs.copy())
    if cmfs.shape.interval != 1:
        cmfs.interpolate(SpectralShape(cmfs.shape.start, cmfs.shape.end, 1))

    cmfs.extrapolator_kwargs = {"method": "Constant", "left": 0, "right": 0}

    L, M, _S = tsplit(cmfs.values)
    d_L, d_M, d_S = tsplit(d_LMS)

    if d_S != 0:
        usage_warning(
            '"Machado et al. (2009)" simulation of tritanomaly is based on '
            "the shift paradigm as an approximation to the actual phenomenon "
            "and restrain the model from trying to model tritanopia.\n"
            "The pre-generated matrices are using a shift value in domain "
            "[5, 59] contrary to the domain [0, 20] used for protanomaly and "
            "deuteranomaly simulation.")

    area_L = np.trapz(L, cmfs.wavelengths)
    area_M = np.trapz(M, cmfs.wavelengths)

    def alpha(x: NDArray) -> NDArray:
        """Compute :math:`alpha` factor."""

        return (20 - x) / 20

    # Corrected equations as per:
    # http://www.inf.ufrgs.br/~oliveira/pubs_files/
    # CVD_Simulation/CVD_Simulation.html#Errata
    L_a = alpha(d_L) * L + 0.96 * area_L / area_M * (1 - alpha(d_L)) * M
    M_a = alpha(d_M) * M + 1 / 0.96 * area_M / area_L * (1 - alpha(d_M)) * L
    S_a = as_float_array(cmfs[cmfs.wavelengths - d_S])[:, 2]

    LMS_a = tstack([L_a, M_a, S_a])
    cmfs[cmfs.wavelengths] = LMS_a

    severity = f"{d_L}, {d_M}, {d_S}"
    template = "{0} - Anomalous Trichromacy ({1})"
    cmfs.name = template.format(cmfs.name, severity)
    cmfs.strict_name = template.format(cmfs.strict_name, severity)

    return cmfs
Exemple #52
0
def plot_single_sd(
    sd: SpectralDistribution,
    cmfs: Union[MultiSpectralDistributions, str, Sequence[Union[
        MultiSpectralDistributions,
        str]], ] = "CIE 1931 2 Degree Standard Observer",
    out_of_gamut_clipping: Boolean = True,
    modulate_colours_with_sd_amplitude: Boolean = False,
    equalize_sd_amplitude: Boolean = False,
    **kwargs: Any,
) -> Tuple[plt.Figure, plt.Axes]:
    """
    Plot given spectral distribution.

    Parameters
    ----------
    sd
        Spectral distribution to plot.
    cmfs
        Standard observer colour matching functions used for computing the
        spectrum domain and colours. ``cmfs`` can be of any type or form
        supported by the :func:`colour.plotting.filter_cmfs` definition.
    out_of_gamut_clipping
        Whether to clip out of gamut colours otherwise, the colours will be
        offset by the absolute minimal colour leading to a rendering on
        gray background, less saturated and smoother.
    modulate_colours_with_sd_amplitude
        Whether to modulate the colours with the spectral distribution
        amplitude.
    equalize_sd_amplitude
        Whether to equalize the spectral distribution amplitude.
        Equalization occurs after the colours modulation thus setting both
        arguments to *True* will generate a spectrum strip where each
        wavelength colour is modulated by the spectral distribution amplitude.
        The usual 5% margin above the spectral distribution is also omitted.

    Other Parameters
    ----------------
    kwargs
        {:func:`colour.plotting.artist`, :func:`colour.plotting.render`},
        See the documentation of the previously listed definitions.

    Returns
    -------
    :class:`tuple`
        Current figure and axes.

    References
    ----------
    :cite:`Spiker2015a`

    Examples
    --------
    >>> from colour import SpectralDistribution
    >>> data = {
    ...     500: 0.0651,
    ...     520: 0.0705,
    ...     540: 0.0772,
    ...     560: 0.0870,
    ...     580: 0.1128,
    ...     600: 0.1360
    ... }
    >>> sd = SpectralDistribution(data, name='Custom')
    >>> plot_single_sd(sd)  # doctest: +ELLIPSIS
    (<Figure size ... with 1 Axes>, <...AxesSubplot...>)

    .. image:: ../_static/Plotting_Plot_Single_SD.png
        :align: center
        :alt: plot_single_sd
    """

    _figure, axes = artist(**kwargs)

    cmfs = cast(MultiSpectralDistributions,
                first_item(filter_cmfs(cmfs).values()))

    sd = cast(SpectralDistribution, sd.copy())
    sd.interpolator = LinearInterpolator
    wavelengths = cmfs.wavelengths[np.logical_and(
        cmfs.wavelengths >= max(min(cmfs.wavelengths), min(sd.wavelengths)),
        cmfs.wavelengths <= min(max(cmfs.wavelengths), max(sd.wavelengths)),
    )]
    values = as_float_array(sd[wavelengths])

    RGB = XYZ_to_plotting_colourspace(
        wavelength_to_XYZ(wavelengths, cmfs),
        CCS_ILLUMINANTS["CIE 1931 2 Degree Standard Observer"]["E"],
        apply_cctf_encoding=False,
    )

    if not out_of_gamut_clipping:
        RGB += np.abs(np.min(RGB))

    RGB = normalise_maximum(RGB)

    if modulate_colours_with_sd_amplitude:
        RGB *= (values / np.max(values))[..., np.newaxis]

    RGB = CONSTANTS_COLOUR_STYLE.colour.colourspace.cctf_encoding(RGB)

    if equalize_sd_amplitude:
        values = ones(values.shape)

    margin = 0 if equalize_sd_amplitude else 0.05

    x_min, x_max = min(wavelengths), max(wavelengths)
    y_min, y_max = 0, max(values) + max(values) * margin

    polygon = Polygon(
        np.vstack([
            (x_min, 0),
            tstack([wavelengths, values]),
            (x_max, 0),
        ]),
        facecolor="none",
        edgecolor="none",
        zorder=CONSTANTS_COLOUR_STYLE.zorder.background_polygon,
    )
    axes.add_patch(polygon)

    padding = 0.1
    axes.bar(
        x=wavelengths - padding,
        height=max(values),
        width=1 + padding,
        color=RGB,
        align="edge",
        clip_path=polygon,
        zorder=CONSTANTS_COLOUR_STYLE.zorder.background_polygon,
    )

    axes.plot(
        wavelengths,
        values,
        color=CONSTANTS_COLOUR_STYLE.colour.dark,
        zorder=CONSTANTS_COLOUR_STYLE.zorder.midground_line,
    )

    settings: Dict[str, Any] = {
        "axes": axes,
        "bounding_box": (x_min, x_max, y_min, y_max),
        "title": f"{sd.strict_name} - {cmfs.strict_name}",
        "x_label": "Wavelength $\\lambda$ (nm)",
        "y_label": "Spectral Distribution",
    }
    settings.update(kwargs)

    return render(**settings)
Exemple #53
0
def XYZ_to_Hunter_Lab(
    XYZ,
    XYZ_n=TVS_ILLUMINANTS_HUNTERLAB['CIE 1931 2 Degree Standard Observer']
    ['D65'].XYZ_n,
    K_ab=TVS_ILLUMINANTS_HUNTERLAB['CIE 1931 2 Degree Standard Observer']
    ['D65'].K_ab):
    """
    Converts from *CIE XYZ* tristimulus values to *Hunter L,a,b* colour scale.

    Parameters
    ----------
    XYZ : array_like
        *CIE XYZ* tristimulus values.
    XYZ_n : array_like, optional
        Reference *illuminant* tristimulus values.
    K_ab : array_like, optional
        Reference *illuminant* chromaticity coefficients, if ``K_ab`` is set to
        *None* it will be computed using
        :func:`colour.XYZ_to_K_ab_HunterLab1966`.

    Returns
    -------
    ndarray
        *Hunter L,a,b* colour scale array.

    Notes
    -----

    +------------+-----------------------+-----------------+
    | **Domain** | **Scale - Reference** | **Scale - 1**   |
    +============+=======================+=================+
    | ``XYZ``    | [0, 100]              | [0, 1]          |
    +------------+-----------------------+-----------------+
    | ``XYZ_n``  | [0, 100]              | [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:`HunterLab2008b`

    Examples
    --------
    >>> XYZ = np.array([0.20654008, 0.12197225, 0.05136952]) * 100
    >>> D65 = TVS_ILLUMINANTS_HUNTERLAB[
    ...     'CIE 1931 2 Degree Standard Observer']['D65']
    >>> XYZ_to_Hunter_Lab(XYZ, D65.XYZ_n, D65.K_ab)   # doctest: +ELLIPSIS
    array([ 34.9245257...,  47.0618985...,  14.3861510...])
    """

    X, Y, Z = tsplit(to_domain_100(XYZ))
    X_n, Y_n, Z_n = tsplit(to_domain_100(XYZ_n))
    K_a, K_b = (tsplit(XYZ_to_K_ab_HunterLab1966(XYZ_n))
                if K_ab is None else tsplit(K_ab))

    Y_Y_n = Y / Y_n
    sqrt_Y_Y_n = np.sqrt(Y_Y_n)

    L = 100 * sqrt_Y_Y_n
    a = K_a * ((X / X_n - Y_Y_n) / sqrt_Y_Y_n)
    b = K_b * ((Y_Y_n - Z / Z_n) / sqrt_Y_Y_n)

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

    return from_range_100(Lab)
Exemple #54
0
def XYZ_to_Hunt(XYZ,
                XYZ_w,
                XYZ_b,
                L_A,
                surround=HUNT_VIEWING_CONDITIONS['Normal Scenes'],
                L_AS=None,
                CCT_w=None,
                XYZ_p=None,
                p=None,
                S=None,
                S_w=None,
                helson_judd_effect=False,
                discount_illuminant=True):
    """
    Computes the *Hunt* colour appearance model correlates.

    Parameters
    ----------
    XYZ : array_like
        *CIE XYZ* tristimulus values of test sample / stimulus.
    XYZ_w : array_like
        *CIE XYZ* tristimulus values of reference white.
    XYZ_b : array_like
        *CIE XYZ* tristimulus values of background.
    L_A : numeric or array_like
        Adapting field *luminance* :math:`L_A` in :math:`cd/m^2`.
    surround : Hunt_InductionFactors, optional
         Surround viewing conditions induction factors.
    L_AS : numeric or array_like, optional
        Scotopic luminance :math:`L_{AS}` of the illuminant, approximated if
        not specified.
    CCT_w : numeric or array_like, optional
        Correlated color temperature :math:`T_{cp}`: of the illuminant, needed
        to approximate :math:`L_{AS}`.
    XYZ_p : array_like, optional
        *CIE XYZ* tristimulus values of proximal field, assumed to be equal to
        background if not specified.
    p : numeric or array_like, optional
        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.
    S : numeric or array_like, optional
        Scotopic response :math:`S` to the stimulus, approximated using
        tristimulus values :math:`Y` of the stimulus if not specified.
    S_w : numeric or array_like, optional
        Scotopic response :math:`S_w` for the reference white, approximated
        using the tristimulus values :math:`Y_w` of the reference white if not
        specified.
    helson_judd_effect : bool, optional
        Truth value indicating whether the *Helson-Judd* effect should be
        accounted for.
    discount_illuminant : bool, optional
       Truth value indicating if the illuminant should be discounted.

    Returns
    -------
    Hunt_Specification
        *Hunt* colour appearance model specification.

    Raises
    ------
    ValueError
        If an illegal arguments combination is specified.

    Notes
    -----

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

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

    References
    ----------
    :cite:`Fairchild2013u`, :cite:`Hunt2004b`

    Examples
    --------
    >>> XYZ = np.array([19.01, 20.00, 21.78])
    >>> XYZ_w = np.array([95.05, 100.00, 108.88])
    >>> XYZ_b = np.array([95.05, 100.00, 108.88])
    >>> L_A = 318.31
    >>> surround = HUNT_VIEWING_CONDITIONS['Normal Scenes']
    >>> CCT_w = 6504.0
    >>> XYZ_to_Hunt(XYZ, XYZ_w, XYZ_b, L_A, surround, CCT_w=CCT_w)
    ... # doctest: +ELLIPSIS
    Hunt_Specification(J=30.0462678..., C=0.1210508..., h=269.2737594..., \
s=0.0199093..., Q=22.2097654..., M=0.1238964..., H=None, HC=None)
    """
    XYZ = to_domain_100(XYZ)
    XYZ_w = to_domain_100(XYZ_w)
    XYZ_b = to_domain_100(XYZ_b)
    _X, Y, _Z = tsplit(XYZ)
    _X_w, Y_w, _Z_w = tsplit(XYZ_w)
    X_b, Y_b, _Z_b = tsplit(XYZ_b)

    # Arguments handling.
    if XYZ_p is not None:
        X_p, Y_p, Z_p = tsplit(to_domain_100(XYZ_p))
    else:
        X_p = X_b
        Y_p = Y_b
        Z_p = Y_b
        usage_warning('Unspecified proximal field "XYZ_p" argument, using '
                      'background "XYZ_b" as approximation!')

    if surround.N_cb is None:
        N_cb = 0.725 * spow(Y_w / Y_b, 0.2)
        usage_warning('Unspecified "N_cb" argument, using approximation: '
                      '"{0}"'.format(N_cb))
    if surround.N_bb is None:
        N_bb = 0.725 * spow(Y_w / Y_b, 0.2)
        usage_warning('Unspecified "N_bb" argument, using approximation: '
                      '"{0}"'.format(N_bb))

    if L_AS is None and CCT_w is None:
        raise ValueError('Either the scotopic luminance "L_AS" of the '
                         'illuminant or its correlated colour temperature '
                         '"CCT_w" must be specified!')
    if L_AS is None:
        L_AS = illuminant_scotopic_luminance(L_A, CCT_w)
        usage_warning(
            'Unspecified "L_AS" argument, using approximation from "CCT": '
            '"{0}"'.format(L_AS))

    if (S is None and S_w is not None) or (S is not None and S_w is None):
        raise ValueError('Either both stimulus scotopic response "S" and '
                         'reference white scotopic response "S_w" arguments '
                         'need to be specified or none of them!')
    elif S is None and S_w is None:
        S = Y
        S_w = Y_w
        usage_warning(
            'Unspecified stimulus scotopic response "S" and reference '
            'white scotopic response "S_w" arguments, using '
            'approximation: "{0}", "{1}"'.format(S, S_w))

    if p is None:
        usage_warning(
            'Unspecified simultaneous contrast / assimilation "p" '
            'argument, model will not account for simultaneous chromatic '
            'contrast!')

    XYZ_p = tstack([X_p, Y_p, Z_p])

    # Computing luminance level adaptation factor :math:`F_L`.
    F_L = luminance_level_adaptation_factor(L_A)

    # Computing test sample chromatic adaptation.
    rgb_a = chromatic_adaptation(XYZ, XYZ_w, XYZ_b, L_A, F_L, XYZ_p, p,
                                 helson_judd_effect, discount_illuminant)

    # Computing reference white chromatic adaptation.
    rgb_aw = chromatic_adaptation(XYZ_w, XYZ_w, XYZ_b, L_A, F_L, XYZ_p, p,
                                  helson_judd_effect, discount_illuminant)

    # Computing opponent colour dimensions.
    # Computing achromatic post adaptation signals.
    A_a = achromatic_post_adaptation_signal(rgb_a)
    A_aw = achromatic_post_adaptation_signal(rgb_aw)

    # Computing colour difference signals.
    C = colour_difference_signals(rgb_a)
    C_w = colour_difference_signals(rgb_aw)

    # -------------------------------------------------------------------------
    # Computing the *hue* angle :math:`h_s`.
    # -------------------------------------------------------------------------
    h = hue_angle(C)
    # hue_w = hue_angle(C_w)
    # TODO: Implement hue quadrature & composition computation.

    # -------------------------------------------------------------------------
    # Computing the correlate of *saturation* :math:`s`.
    # -------------------------------------------------------------------------
    # Computing eccentricity factors.
    e_s = eccentricity_factor(h)

    # Computing low luminance tritanopia factor :math:`F_t`.
    F_t = low_luminance_tritanopia_factor(L_A)

    M_yb = yellowness_blueness_response(C, e_s, surround.N_c, N_cb, F_t)
    M_rg = redness_greenness_response(C, e_s, surround.N_c, N_cb)
    M_yb_w = yellowness_blueness_response(C_w, e_s, surround.N_c, N_cb, F_t)
    M_rg_w = redness_greenness_response(C_w, e_s, surround.N_c, N_cb)

    # Computing overall chromatic response.
    M = overall_chromatic_response(M_yb, M_rg)
    M_w = overall_chromatic_response(M_yb_w, M_rg_w)

    s = saturation_correlate(M, rgb_a)

    # -------------------------------------------------------------------------
    # Computing the correlate of *brightness* :math:`Q`.
    # -------------------------------------------------------------------------
    # Computing achromatic signal :math:`A`.
    A = achromatic_signal(L_AS, S, S_w, N_bb, A_a)
    A_w = achromatic_signal(L_AS, S_w, S_w, N_bb, A_aw)

    Q = brightness_correlate(A, A_w, M, surround.N_b)
    brightness_w = brightness_correlate(A_w, A_w, M_w, surround.N_b)
    # TODO: Implement whiteness-blackness :math:`Q_{wb}` computation.

    # -------------------------------------------------------------------------
    # Computing the correlate of *Lightness* :math:`J`.
    # -------------------------------------------------------------------------
    J = lightness_correlate(Y_b, Y_w, Q, brightness_w)

    # -------------------------------------------------------------------------
    # Computing the correlate of *chroma* :math:`C_{94}`.
    # -------------------------------------------------------------------------
    C_94 = chroma_correlate(s, Y_b, Y_w, Q, brightness_w)

    # -------------------------------------------------------------------------
    # Computing the correlate of *colourfulness* :math:`M_{94}`.
    # -------------------------------------------------------------------------
    M_94 = colourfulness_correlate(F_L, C_94)

    return Hunt_Specification(J, C_94, from_range_degrees(h), s, Q, M_94, None,
                              None)
Exemple #55
0
def Hunter_Lab_to_XYZ(
    Lab,
    XYZ_n=TVS_ILLUMINANTS_HUNTERLAB['CIE 1931 2 Degree Standard Observer']
    ['D65'].XYZ_n,
    K_ab=TVS_ILLUMINANTS_HUNTERLAB['CIE 1931 2 Degree Standard Observer']
    ['D65'].K_ab):
    """
    Converts from *Hunter L,a,b* colour scale to *CIE XYZ* tristimulus values.

    Parameters
    ----------
    Lab : array_like
        *Hunter L,a,b* colour scale array.
    XYZ_n : array_like, optional
        Reference *illuminant* tristimulus values.
    K_ab : array_like, optional
        Reference *illuminant* chromaticity coefficients, if ``K_ab`` is set to
        *None* it will be computed using
        :func:`colour.XYZ_to_K_ab_HunterLab1966`.

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

    Notes
    -----

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

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

    References
    ----------
    :cite:`HunterLab2008b`

    Examples
    --------
    >>> Lab = np.array([34.92452577, 47.06189858, 14.38615107])
    >>> D65 = TVS_ILLUMINANTS_HUNTERLAB[
    ...     'CIE 1931 2 Degree Standard Observer']['D65']
    >>> Hunter_Lab_to_XYZ(Lab, D65.XYZ_n, D65.K_ab)
    array([ 20.654008,  12.197225,   5.136952])
    """

    L, a, b = tsplit(to_domain_100(Lab))
    X_n, Y_n, Z_n = tsplit(to_domain_100(XYZ_n))
    K_a, K_b = (tsplit(XYZ_to_K_ab_HunterLab1966(XYZ_n))
                if K_ab is None else tsplit(K_ab))

    L_100 = L / 100
    L_100_2 = L_100**2

    Y = L_100_2 * Y_n
    X = ((a / K_a) * L_100 + L_100_2) * X_n
    Z = -((b / K_b) * L_100 - L_100_2) * Z_n

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

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

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

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

    Notes
    -----

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

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

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

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

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

    return XYZ_to_Luv(from_range_1(tstack([X, Y, Z])), illuminant)
Exemple #57
0
def plot_chromaticity_diagram_colours(
    samples: Integer = 256,
    diagram_colours: Optional[Union[ArrayLike, str]] = None,
    diagram_opacity: Floating = 1,
    diagram_clipping_path: Optional[ArrayLike] = None,
    cmfs: Union[MultiSpectralDistributions, str, Sequence[Union[
        MultiSpectralDistributions,
        str]], ] = "CIE 1931 2 Degree Standard Observer",
    method: Union[Literal["CIE 1931", "CIE 1960 UCS", "CIE 1976 UCS"],
                  str] = "CIE 1931",
    **kwargs: Any,
) -> Tuple[plt.Figure, plt.Axes]:
    """
    Plot the *Chromaticity Diagram* colours according to given method.

    Parameters
    ----------
    samples
        Samples count on one axis when computing the *Chromaticity Diagram*
        colours.
    diagram_colours
        Colours of the *Chromaticity Diagram*, if ``diagram_colours`` is set
        to *RGB*, the colours will be computed according to the corresponding
        coordinates.
    diagram_opacity
        Opacity of the *Chromaticity Diagram*.
    diagram_clipping_path
        Path of points used to clip the *Chromaticity Diagram* colours.
    cmfs
        Standard observer colour matching functions used for computing the
        spectral locus boundaries. ``cmfs`` can be of any type or form
        supported by the :func:`colour.plotting.filter_cmfs` definition.
    method
        *Chromaticity Diagram* method.

    Other Parameters
    ----------------
    kwargs
        {:func:`colour.plotting.artist`, :func:`colour.plotting.render`},
        See the documentation of the previously listed definitions.

    Returns
    -------
    :class:`tuple`
        Current figure and axes.

    Examples
    --------
    >>> plot_chromaticity_diagram_colours(diagram_colours='RGB')
    ... # doctest: +ELLIPSIS
    (<Figure size ... with 1 Axes>, <...AxesSubplot...>)

    .. image:: ../_static/Plotting_Plot_Chromaticity_Diagram_Colours.png
        :align: center
        :alt: plot_chromaticity_diagram_colours
    """

    method = validate_method(method,
                             ["CIE 1931", "CIE 1960 UCS", "CIE 1976 UCS"])

    settings: Dict[str, Any] = {"uniform": True}
    settings.update(kwargs)

    _figure, axes = artist(**settings)

    diagram_colours = cast(
        ArrayLike,
        optional(diagram_colours,
                 HEX_to_RGB(CONSTANTS_COLOUR_STYLE.colour.average)),
    )

    cmfs = cast(MultiSpectralDistributions,
                first_item(filter_cmfs(cmfs).values()))

    illuminant = CONSTANTS_COLOUR_STYLE.colour.colourspace.whitepoint

    if method == "cie 1931":
        spectral_locus = XYZ_to_xy(cmfs.values, illuminant)
    elif method == "cie 1960 ucs":
        spectral_locus = UCS_to_uv(XYZ_to_UCS(cmfs.values))
    elif method == "cie 1976 ucs":
        spectral_locus = Luv_to_uv(XYZ_to_Luv(cmfs.values, illuminant),
                                   illuminant)

    use_RGB_diagram_colours = str(diagram_colours).upper() == "RGB"
    if use_RGB_diagram_colours:
        ii, jj = np.meshgrid(np.linspace(0, 1, samples),
                             np.linspace(1, 0, samples))
        ij = tstack([ii, jj])

        # NOTE: Various values in the grid have potential to generate
        # zero-divisions, they could be avoided by perturbing the grid, e.g.
        # adding a small epsilon. It was decided instead to disable warnings.
        with suppress_warnings(python_warnings=True):
            if method == "cie 1931":
                XYZ = xy_to_XYZ(ij)
            elif method == "cie 1960 ucs":
                XYZ = xy_to_XYZ(UCS_uv_to_xy(ij))
            elif method == "cie 1976 ucs":
                XYZ = xy_to_XYZ(Luv_uv_to_xy(ij))

        diagram_colours = normalise_maximum(XYZ_to_plotting_colourspace(
            XYZ, illuminant),
                                            axis=-1)

    polygon = Polygon(
        spectral_locus
        if diagram_clipping_path is None else diagram_clipping_path,
        facecolor="none" if use_RGB_diagram_colours else np.hstack(
            [diagram_colours, diagram_opacity]),
        edgecolor="none" if use_RGB_diagram_colours else np.hstack(
            [diagram_colours, diagram_opacity]),
        zorder=CONSTANTS_COLOUR_STYLE.zorder.background_polygon,
    )
    axes.add_patch(polygon)

    if use_RGB_diagram_colours:
        # Preventing bounding box related issues as per
        # https://github.com/matplotlib/matplotlib/issues/10529
        image = axes.imshow(
            diagram_colours,
            interpolation="bilinear",
            extent=(0, 1, 0, 1),
            clip_path=None,
            alpha=diagram_opacity,
            zorder=CONSTANTS_COLOUR_STYLE.zorder.background_polygon,
        )
        image.set_clip_path(polygon)

    settings = {"axes": axes}
    settings.update(kwargs)

    return render(**kwargs)
Exemple #58
0
def Lab_to_DIN99(Lab, k_E=1, k_CH=1):
    """
    Converts from *CIE L\\*a\\*b\\** colourspace to *DIN99* colourspace.

    Parameters
    ----------
    Lab : array_like
        *CIE L\\*a\\*b\\** colourspace array.
    k_E : numeric, optional
        Parametric factor :math:`K_E` used to compensate for texture and other
        specimen presentation effects.
    k_CH : numeric, optional
        Parametric factor :math:`K_{CH}` used to compensate for texture and
        other specimen presentation effects.

    Returns
    -------
    ndarray
        *DIN99* colourspace array.

    Notes
    -----

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

    +------------+------------------------+--------------------+
    | **Range**  | **Scale - Reference**  | **Scale - 1**      |
    +============+========================+====================+
    | ``Lab_99`` | ``L_99`` : [0, 100]    | ``L_99`` : [0, 1]  |
    |            |                        |                    |
    |            | ``a_99`` : [-100, 100] | ``a_99`` : [-1, 1] |
    |            |                        |                    |
    |            | ``b_99`` : [-100, 100] | ``b_99`` : [-1, 1] |
    +------------+------------------------+--------------------+

    References
    ----------
    :cite:`ASTMInternational2007`

    Examples
    --------
    >>> import numpy as np
    >>> Lab = np.array([41.52787529, 52.63858304, 26.92317922])
    >>> Lab_to_DIN99(Lab)  # doctest: +ELLIPSIS
    array([ 53.2282198...,  28.4163465...,   3.8983955...])
    """

    L, a, b = tsplit(to_domain_100(Lab))

    cos_16 = np.cos(np.radians(16))
    sin_16 = np.sin(np.radians(16))

    e = cos_16 * a + sin_16 * b
    f = 0.7 * (-sin_16 * a + cos_16 * b)
    G = spow(e**2 + f**2, 0.5)
    h_ef = np.arctan2(f, e)

    C_99 = (np.log(1 + 0.045 * G)) / (0.045 * k_CH * k_E)
    # Hue angle is unused currently.
    # h_99 = np.degrees(h_ef)
    a_99 = C_99 * np.cos(h_ef)
    b_99 = C_99 * np.sin(h_ef)
    L_99 = 105.509 * (np.log(1 + 0.0158 * L)) * k_E

    Lab_99 = tstack([L_99, a_99, b_99])

    return from_range_100(Lab_99)
Exemple #59
0
def demosaicing_CFA_Bayer_bilinear(CFA, pattern='RGGB'):
    """
    Returns the demosaiced *RGB* colourspace array from given *Bayer* CFA using
    bilinear interpolation.

    Parameters
    ----------
    CFA : array_like
        *Bayer* CFA.
    pattern : unicode, optional
        **{'RGGB', 'BGGR', 'GRBG', 'GBRG'}**,
        Arrangement of the colour filters on the pixel array.

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

    Notes
    -----
    -   The definition output is not clipped in range [0, 1] : this allows for
        direct HDRI / radiance image generation on *Bayer* CFA data and post
        demosaicing of the high dynamic range data as showcased in this
        `Jupyter Notebook <https://github.com/colour-science/colour-hdri/\
blob/develop/colour_hdri/examples/\
examples_merge_from_raw_files_with_post_demosaicing.ipynb>`_.

    References
    ----------
    :cite:`Losson2010c`

    Examples
    --------
    >>> import numpy as np
    >>> CFA = np.array(
    ...     [[0.30980393, 0.36078432, 0.30588236, 0.3764706],
    ...      [0.35686275, 0.39607844, 0.36078432, 0.40000001]])
    >>> demosaicing_CFA_Bayer_bilinear(CFA)
    array([[[ 0.69705884,  0.17941177,  0.09901961],
            [ 0.46176472,  0.4509804 ,  0.19803922],
            [ 0.45882354,  0.27450981,  0.19901961],
            [ 0.22941177,  0.5647059 ,  0.30000001]],
    <BLANKLINE>
           [[ 0.23235295,  0.53529412,  0.29705883],
            [ 0.15392157,  0.26960785,  0.59411766],
            [ 0.15294118,  0.4509804 ,  0.59705884],
            [ 0.07647059,  0.18431373,  0.90000002]]])
    >>> CFA = np.array(
    ...     [[0.3764706, 0.360784320, 0.40784314, 0.3764706],
    ...      [0.35686275, 0.30980393, 0.36078432, 0.29803923]])
    >>> demosaicing_CFA_Bayer_bilinear(CFA, 'BGGR')
    array([[[ 0.07745098,  0.17941177,  0.84705885],
            [ 0.15490197,  0.4509804 ,  0.5882353 ],
            [ 0.15196079,  0.27450981,  0.61176471],
            [ 0.22352942,  0.5647059 ,  0.30588235]],
    <BLANKLINE>
           [[ 0.23235295,  0.53529412,  0.28235295],
            [ 0.4647059 ,  0.26960785,  0.19607843],
            [ 0.45588237,  0.4509804 ,  0.20392157],
            [ 0.67058827,  0.18431373,  0.10196078]]])
    """

    CFA = as_float_array(CFA)
    R_m, G_m, B_m = masks_CFA_Bayer(CFA.shape, pattern)

    H_G = as_float_array(
        [[0, 1, 0],
         [1, 4, 1],
         [0, 1, 0]]) / 4  # yapf: disable

    H_RB = as_float_array(
        [[1, 2, 1],
         [2, 4, 2],
         [1, 2, 1]]) / 4  # yapf: disable

    R = convolve(CFA * R_m, H_RB)
    G = convolve(CFA * G_m, H_G)
    B = convolve(CFA * B_m, H_RB)

    del R_m, G_m, B_m, H_RB, H_G

    return tstack([R, G, B])
Exemple #60
0
def xy_to_xyY(xy, Y=1):
    """
    Converts from *CIE xy* chromaticity coordinates to *CIE xyY* colourspace by
    extending the array last dimension with given :math:`Y` *luminance*.

    ``xy`` argument with last dimension being equal to 3 will be assumed to be
    a *CIE xyY* colourspace array argument and will be returned directly by the
    definition.

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

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

    Notes
    -----

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

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

    -   This definition is a convenient object provided to implement support of
        illuminant argument *luminance* value in various :mod:`colour.models`
        package objects such as :func:`colour.Lab_to_XYZ` or
        :func:`colour.Luv_to_XYZ`.

    References
    ----------
    :cite:`Wikipedia2005`

    Examples
    --------
    >>> xy = np.array([0.54369557, 0.32107944])
    >>> xy_to_xyY(xy)  # doctest: +ELLIPSIS
    array([ 0.5436955...,  0.3210794...,  1.        ])
    >>> xy = np.array([0.54369557, 0.32107944, 1.00000000])
    >>> xy_to_xyY(xy)  # doctest: +ELLIPSIS
    array([ 0.5436955...,  0.3210794...,  1.        ])
    >>> xy = np.array([0.54369557, 0.32107944])
    >>> xy_to_xyY(xy, 100)  # doctest: +ELLIPSIS
    array([   0.5436955...,    0.3210794...,  100.        ])
    """

    xy = as_float_array(xy)
    Y = to_domain_1(Y)

    shape = xy.shape
    # Assuming ``xy`` is actually a *CIE xyY* colourspace array argument and
    # returning it directly.
    if shape[-1] == 3:
        return xy

    x, y = tsplit(xy)

    Y = full(x.shape, from_range_1(Y))
    xyY = tstack([x, y, Y])

    return xyY