Esempio n. 1
0
    def test_n_dimensional_UCS_to_uv(self):
        """
        Tests :func:`colour.models.cie_ucs.UCS_to_uv` definition n-dimensions
        support.
        """

        UCS = np.array([0.04699689, 0.10080000, 0.16374390])
        uv = np.array([0.15085309, 0.32355314])
        np.testing.assert_almost_equal(
            UCS_to_uv(UCS),
            uv,
            decimal=7)

        UCS = np.tile(UCS, (6, 1))
        uv = np.tile(uv, (6, 1))
        np.testing.assert_almost_equal(
            UCS_to_uv(UCS),
            uv,
            decimal=7)

        UCS = np.reshape(UCS, (2, 3, 3))
        uv = np.reshape(uv, (2, 3, 2))
        np.testing.assert_almost_equal(
            UCS_to_uv(UCS),
            uv,
            decimal=7)
Esempio n. 2
0
def XYZ_to_UVW(
        XYZ,
        illuminant=ILLUMINANTS['CIE 1931 2 Degree Standard Observer']['D50']):
    """
    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
    -----
    -   Input *CIE XYZ* tristimulus values are in domain [0, 100].
    -   Input *illuminant* *xy* chromaticity coordinates or *CIE xyY*
        colourspace array are in domain [0, :math:`\infty`].
    -   Output *CIE 1964 U\*V\*W\** colourspace array is in range [0, 100].

    References
    ----------
    -   :cite:`Wikipediacj`

    Examples
    --------
    >>> import numpy as np
    >>> XYZ = np.array([0.07049534, 0.10080000, 0.09558313]) * 100
    >>> XYZ_to_UVW(XYZ)  # doctest: +ELLIPSIS
    array([-28.0579733...,  -0.8819449...,  37.0041149...])
    """

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

    u, v = tsplit(UCS_to_uv(XYZ_to_UCS(XYZ)))
    u_0, v_0 = tsplit(UCS_to_uv(XYZ_to_UCS(xyY_to_XYZ(xy_to_xyY(illuminant)))))

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

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

    return UVW
Esempio n. 3
0
def _CCT_to_uv_Ohno2013(
        CCT_D_uv,
        cmfs=MSDS_CMFS_STANDARD_OBSERVER['CIE 1931 2 Degree Standard Observer']
):
    """
    Returns the *CIE UCS* colourspace *uv* chromaticity coordinates from given
    correlated colour temperature :math:`T_{cp}`, :math:`\\Delta_{uv}` and
    colour matching functions using *Ohno (2013)* method.

    Parameters
    ----------
    CCT_D_uv : ndarray
        Correlated colour temperature :math:`T_{cp}`, :math:`\\Delta_{uv}`.
    cmfs : XYZ_ColourMatchingFunctions, optional
        Standard observer colour matching functions.

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

    CCT, D_uv = tsplit(CCT_D_uv)

    cmfs = cmfs.copy().trim(SPECTRAL_SHAPE_DEFAULT)

    shape = cmfs.shape

    delta = 0.01

    sd = sd_blackbody(CCT, shape)
    XYZ = sd_to_XYZ(sd, cmfs)
    XYZ *= 1 / np.max(XYZ)
    UVW = XYZ_to_UCS(XYZ)
    u0, v0 = UCS_to_uv(UVW)

    if D_uv == 0:
        return np.array([u0, v0])
    else:
        sd = sd_blackbody(CCT + delta, shape)
        XYZ = sd_to_XYZ(sd, cmfs)
        XYZ *= 1 / np.max(XYZ)
        UVW = XYZ_to_UCS(XYZ)
        u1, v1 = UCS_to_uv(UVW)

        du = u0 - u1
        dv = v0 - v1

        u = u0 - D_uv * (dv / np.hypot(du, dv))
        v = v0 + D_uv * (du / np.hypot(du, dv))

        return np.array([u, v])
Esempio n. 4
0
def _CCT_to_uv_Ohno2013(
        CCT_D_uv: ArrayLike,
        cmfs: Optional[MultiSpectralDistributions] = None) -> NDArray:
    """
    Return the *CIE UCS* colourspace *uv* chromaticity coordinates from given
    correlated colour temperature :math:`T_{cp}`, :math:`\\Delta_{uv}` and
    colour matching functions using *Ohno (2013)* method.

    Parameters
    ----------
    CCT_D_uv
        Correlated colour temperature :math:`T_{cp}`, :math:`\\Delta_{uv}`.
    cmfs
        Standard observer colour matching functions, default to the
        *CIE 1931 2 Degree Standard Observer*.

    Returns
    -------
    :class:`numpy.ndarray`
        *CIE UCS* colourspace *uv* chromaticity coordinates.
    """

    CCT, D_uv = tsplit(CCT_D_uv)

    cmfs, _illuminant = handle_spectral_arguments(cmfs)

    delta = 0.01

    sd = sd_blackbody(CCT, cmfs.shape)
    XYZ = sd_to_XYZ(sd, cmfs)
    XYZ *= 1 / np.max(XYZ)
    UVW = XYZ_to_UCS(XYZ)
    u0, v0 = UCS_to_uv(UVW)

    if D_uv == 0:
        return np.array([u0, v0])
    else:
        sd = sd_blackbody(CCT + delta, cmfs.shape)
        XYZ = sd_to_XYZ(sd, cmfs)
        XYZ *= 1 / np.max(XYZ)
        UVW = XYZ_to_UCS(XYZ)
        u1, v1 = UCS_to_uv(UVW)

        du = u0 - u1
        dv = v0 - v1

        u = u0 - D_uv * (dv / np.hypot(du, dv))
        v = v0 + D_uv * (du / np.hypot(du, dv))

        return np.array([u, v])
Esempio n. 5
0
    def test_domain_range_scale_UCS_to_uv(self):
        """
        Tests :func:`colour.models.cie_ucs.UCS_to_uv` definition domain and
        range scale support.
        """

        UCS = np.array([0.0469968933, 0.1008000000, 0.1637438950])
        uv = UCS_to_uv(UCS)

        d_r = (('reference', 1), (1, 1), (100, 100))
        for scale, factor in d_r:
            with domain_range_scale(scale):
                np.testing.assert_almost_equal(UCS_to_uv(UCS * factor),
                                               uv,
                                               decimal=7)
Esempio n. 6
0
    def test_n_dimensional_UCS_to_uv(self):
        """
        Tests :func:`colour.models.cie_ucs.UCS_to_uv` definition n-dimensional
        support.
        """

        UCS = np.array([0.13769339, 0.12197225, 0.10537310])
        uv = UCS_to_uv(UCS)

        UCS = np.tile(UCS, (6, 1))
        uv = np.tile(uv, (6, 1))
        np.testing.assert_almost_equal(UCS_to_uv(UCS), uv, decimal=7)

        UCS = np.reshape(UCS, (2, 3, 3))
        uv = np.reshape(uv, (2, 3, 2))
        np.testing.assert_almost_equal(UCS_to_uv(UCS), uv, decimal=7)
Esempio n. 7
0
        def XYZ_to_ij(XYZ):
            """
            Converts given *CIE XYZ* tristimulus values to *ij* chromaticity
            coordinates.
            """

            return UCS_to_uv(XYZ_to_UCS(XYZ))
Esempio n. 8
0
        def xy_to_ij(xy):
            """
            Converts given *CIE xy* chromaticity coordinates to *ij*
            chromaticity coordinates.
            """

            return UCS_to_uv(XYZ_to_UCS(xy_to_XYZ(xy)))
Esempio n. 9
0
def CCT_reference_illuminant(sd: SpectralDistribution) -> NDArray:
    """
    Compute the reference illuminant correlated colour temperature
    :math:`T_{cp}` and :math:`\\Delta_{uv}` for given test spectral
    distribution using *Ohno (2013)* method.

    Parameters
    ----------
    sd
        Test spectral distribution.

    Returns
    -------
    :class:`numpy.ndarray`
        Correlated colour temperature :math:`T_{cp}`, :math:`\\Delta_{uv}`.

    Examples
    --------
    >>> from colour import SDS_ILLUMINANTS
    >>> sd = SDS_ILLUMINANTS['FL2']
    >>> CCT_reference_illuminant(sd)  # doctest: +ELLIPSIS
    array([  4.2244697...e+03,   1.7871111...e-03])
    """

    XYZ = sd_to_XYZ(sd)

    return uv_to_CCT_Ohno2013(UCS_to_uv(XYZ_to_UCS(XYZ)))
Esempio n. 10
0
def CCT_reference_illuminant(sd):
    """
    Computes the reference illuminant correlated colour temperature
    :math:`T_{cp}` and :math:`\\Delta_{uv}` for given test spectral
    distribution using *Ohno (2013)* method.

    Parameters
    ----------
    sd : SpectralDistribution
        Test spectral distribution.

    Returns
    -------
    ndarray
        Correlated colour temperature :math:`T_{cp}`, :math:`\\Delta_{uv}`.

    Examples
    --------
    >>> from colour import SDS_ILLUMINANTS
    >>> sd = SDS_ILLUMINANTS['FL2']
    >>> CCT_reference_illuminant(sd)  # doctest: +ELLIPSIS
    (4224.4697052..., 0.0017871...)
    """

    XYZ = sd_to_XYZ(sd)

    CCT, D_uv = uv_to_CCT_Ohno2013(UCS_to_uv(XYZ_to_UCS(XYZ)))

    return CCT, D_uv
Esempio n. 11
0
    def test_nan_UCS_to_uv(self):
        """Test :func:`colour.models.cie_ucs.UCS_to_uv` definition nan support."""

        cases = [-1.0, 0.0, 1.0, -np.inf, np.inf, np.nan]
        cases = set(permutations(cases * 3, r=3))
        for case in cases:
            UCS = np.array(case)
            UCS_to_uv(UCS)
Esempio n. 12
0
def chromaticity_diagram_colours_CIE1960UCS(
        samples=4096,
        cmfs='CIE 1931 2 Degree Standard Observer',
        antialiasing=True):
    """
    Plots the *CIE 1960 UCS Chromaticity Diagram* colours.

    Parameters
    ----------
    samples : numeric, optional
        Samples count on one axis.
    cmfs : unicode, optional
        Standard observer colour matching functions used for diagram bounds.
    antialiasing : bool, optional
        Whether to apply anti-aliasing to the image.

    Other Parameters
    ----------------
    \**kwargs : dict, optional
        {:func:`colour.plotting.render`},
        Please refer to the documentation of the previously listed definition.

    Returns
    -------
    Figure
        Current figure or None.

    Examples
    --------
    >>> chromaticity_diagram_colours_CIE1960UCS()  # doctest: +SKIP
    """

    cmfs = get_cmfs(cmfs)

    illuminant = DEFAULT_PLOTTING_ILLUMINANT

    triangulation = Delaunay(UCS_to_uv(XYZ_to_UCS(cmfs.values)),
                             qhull_options='Qu QJ')
    xx, yy = np.meshgrid(np.linspace(0, 1, samples),
                         np.linspace(1, 0, samples))
    xy = tstack((xx, yy))
    mask = (triangulation.find_simplex(xy) < 0).astype(DEFAULT_FLOAT_DTYPE)
    if antialiasing:
        kernel = np.array([
            [0, 1, 0],
            [1, 2, 1],
            [0, 1, 0],
        ]).astype(DEFAULT_FLOAT_DTYPE)
        kernel /= np.sum(kernel)
        mask = convolve(mask, kernel)

    mask = 1 - mask[:, :, np.newaxis]

    XYZ = xy_to_XYZ(UCS_uv_to_xy(xy))

    RGB = normalise_maximum(XYZ_to_sRGB(XYZ, illuminant), axis=-1)

    return np.dstack([RGB, mask])
Esempio n. 13
0
def planckian_table(uv, cmfs, start, end, count):
    """
    Returns a planckian table from given *CIE UCS* colourspace *uv*
    chromaticity coordinates, colour matching functions and temperature range
    using *Yoshi Ohno (2013)* method.

    Parameters
    ----------
    uv : array_like
        *uv* chromaticity coordinates.
    cmfs : XYZ_ColourMatchingFunctions
        Standard observer colour matching functions.
    start : numeric
        Temperature range start in kelvins.
    end : numeric
        Temperature range end in kelvins.
    count : int
        Temperatures count in the planckian table.

    Returns
    -------
    list
        Planckian table.

    Examples
    --------
    >>> from colour import STANDARD_OBSERVERS_CMFS
    >>> from pprint import pprint
    >>> cmfs = 'CIE 1931 2 Degree Standard Observer'
    >>> cmfs = STANDARD_OBSERVERS_CMFS.get(cmfs)
    >>> pprint(planckian_table((0.1978, 0.3122), cmfs, 1000, 1010, 10))  # noqa  # doctest: +ELLIPSIS
    [PlanckianTable_Tuvdi(Ti=1000.0, ui=0.4480108..., vi=0.3546249..., di=0.2537821...),
     PlanckianTable_Tuvdi(Ti=1001.1111111..., ui=0.4477508..., vi=0.3546475..., di=0.2535294...),
     PlanckianTable_Tuvdi(Ti=1002.2222222..., ui=0.4474910..., vi=0.3546700..., di=0.2532771...),
     PlanckianTable_Tuvdi(Ti=1003.3333333..., ui=0.4472316..., vi=0.3546924..., di=0.2530251...),
     PlanckianTable_Tuvdi(Ti=1004.4444444..., ui=0.4469724..., vi=0.3547148..., di=0.2527734...),
     PlanckianTable_Tuvdi(Ti=1005.5555555..., ui=0.4467136..., vi=0.3547372..., di=0.2525220...),
     PlanckianTable_Tuvdi(Ti=1006.6666666..., ui=0.4464550..., vi=0.3547595..., di=0.2522710...),
     PlanckianTable_Tuvdi(Ti=1007.7777777..., ui=0.4461968..., vi=0.3547817..., di=0.2520202...),
     PlanckianTable_Tuvdi(Ti=1008.8888888..., ui=0.4459389..., vi=0.3548040..., di=0.2517697...),
     PlanckianTable_Tuvdi(Ti=1010.0, ui=0.4456812..., vi=0.3548261..., di=0.2515196...)]
    """

    ux, vx = uv

    shape = cmfs.shape

    table = []
    for Ti in np.linspace(start, end, count):
        spd = blackbody_spd(Ti, shape)
        XYZ = spectral_to_XYZ(spd, cmfs)
        XYZ *= 1 / np.max(XYZ)
        UVW = XYZ_to_UCS(XYZ)
        ui, vi = UCS_to_uv(UVW)
        di = math.sqrt((ux - ui)**2 + (vx - vi)**2)
        table.append(PLANCKIAN_TABLE_TUVD(Ti, ui, vi, di))

    return table
Esempio n. 14
0
def RGB_chromaticity_coordinates_CIE_1960_UCS_chromaticity_diagram_plot(
        RGB,
        colourspace,
        **kwargs):
    """
    Plots given *RGB* colourspace array in *CIE 1960 UCS Chromaticity Diagram*.

    Parameters
    ----------
    RGB : array_like
        *RGB* colourspace array.
    colourspace : RGB_Colourspace
        *RGB* colourspace of the *RGB* array.
    \**kwargs : dict, optional
        Keywords arguments.

    Returns
    -------
    bool
        Definition success.

    Examples
    --------
    >>> RGB = np.random.random((10, 10, 3))
    >>> c = 'Rec. 709'
    >>> RGB_chromaticity_coordinates_CIE_1960_UCS_chromaticity_diagram_plot(
    ...     RGB, c)  # doctest: +SKIP
    True
    """

    settings = {}
    settings.update(kwargs)
    settings.update({'standalone': False})

    settings['colourspaces'] = (
        [colourspace.name] + settings.get('colourspaces', []))

    RGB_colourspaces_CIE_1960_UCS_chromaticity_diagram_plot(**settings)

    alpha_p, colour_p = 0.85, 'black'

    uv = UCS_to_uv(XYZ_to_UCS(RGB_to_XYZ(RGB,
                                         colourspace.whitepoint,
                                         colourspace.whitepoint,
                                         colourspace.RGB_to_XYZ_matrix)))

    pylab.scatter(uv[..., 0],
                  uv[..., 1],
                  alpha=alpha_p / 2,
                  color=colour_p,
                  marker='+')

    settings.update({'standalone': True})

    boundaries(**settings)
    decorate(**settings)

    return display(**settings)
Esempio n. 15
0
    def test_UCS_to_uv(self):
        """
        Tests :func:`colour.models.cie_ucs.UCS_to_uv` definition.
        """

        np.testing.assert_almost_equal(UCS_to_uv(
            np.array([0.13769339, 0.12197225, 0.10537310])),
                                       np.array([0.37720213, 0.33413508]),
                                       decimal=7)

        np.testing.assert_almost_equal(UCS_to_uv(
            np.array([0.09481340, 0.23042768, 0.32701033])),
                                       np.array([0.14536327, 0.35328046]),
                                       decimal=7)

        np.testing.assert_almost_equal(UCS_to_uv(
            np.array([0.05212520, 0.06157201, 0.19376075])),
                                       np.array([0.16953602, 0.20026156]),
                                       decimal=7)
Esempio n. 16
0
def XYZ_to_UVW(XYZ,
               illuminant=ILLUMINANTS.get(
                   'CIE 1931 2 Degree Standard Observer').get('D50')):
    """
    Converts from *CIE XYZ* colourspace to *CIE 1964 U\*V*\W\** colourspace.

    Parameters
    ----------
    XYZ : array_like, (3,)
        *CIE XYZ* colourspace matrix.
    illuminant : array_like, optional
        Reference *illuminant* chromaticity coordinates.

    Returns
    -------
    ndarray, (3,)
        *CIE 1964 U\*V*\W\** colourspace matrix.

    Notes
    -----
    -   Input *CIE XYZ* colourspace matrix is in domain [0, 100].
    -   Output *CIE UVW* colourspace matrix is in domain [0, 100].

    Warning
    -------
    The input / output domains of that definition are non standard!

    Examples
    --------
    >>> XYZ = np.array([11.80583421, 10.34, 5.15089229])
    >>> XYZ_to_UVW(XYZ)  # doctest: +ELLIPSIS
    array([ 24.2543371...,   7.2205484...,  37.4645000...])
    """

    x, y, Y = np.ravel(XYZ_to_xyY(XYZ, illuminant))
    u, v = np.ravel(UCS_to_uv(XYZ_to_UCS(XYZ)))
    u0, v0 = np.ravel(UCS_to_uv(XYZ_to_UCS(xy_to_XYZ(illuminant))))

    W = 25 * Y**(1 / 3) - 17
    U = 13 * W * (u - u0)
    V = 13 * W * (v - v0)

    return np.array([U, V, W])
Esempio n. 17
0
    def test_UCS_to_uv(self):
        """
        Tests :func:`colour.models.cie_ucs.UCS_to_uv` definition.
        """

        np.testing.assert_almost_equal(
            UCS_to_uv(np.array([7.87055614, 10.34, 12.18252904])),
            (0.25895877609618834, 0.34020896328103534),
            decimal=7)

        np.testing.assert_almost_equal(
            UCS_to_uv(np.array([2.05793361, 3.2, 4.60117812])),
            (0.20873418076173886, 0.32457285074301517),
            decimal=7)

        np.testing.assert_almost_equal(
            UCS_to_uv(np.array([0.64604821, 1, 1.57635992])),
            (0.20048615319251942, 0.31032692311386395),
            decimal=7)
Esempio n. 18
0
    def test_UCS_to_uv(self):
        """
        Tests :func:`colour.models.cie_ucs.UCS_to_uv` definition.
        """

        np.testing.assert_almost_equal(
            UCS_to_uv(np.array([0.04699689, 0.10080000, 0.16374390])),
            np.array([0.15085309, 0.32355314]),
            decimal=7)

        np.testing.assert_almost_equal(
            UCS_to_uv(np.array([0.31398473, 0.34950000, 0.34526969])),
            np.array([0.31125983, 0.34646688]),
            decimal=7)

        np.testing.assert_almost_equal(
            UCS_to_uv(np.array([0.17004543, 0.19150000, 0.20396469])),
            np.array([0.30069388, 0.33863231]),
            decimal=7)
Esempio n. 19
0
def plot_spectral_locus(cmfs='CIE 1931 2 Degree Standard Observer',
                        spectral_locus_colours=None,
                        spectral_locus_labels=None,
                        method='CIE 1931',
                        **kwargs):
    """
    Plots the *Spectral Locus* according to given method.

    Parameters
    ----------
    cmfs : unicode, optional
        Standard observer colour matching functions defining the
        *Spectral Locus*.
    spectral_locus_colours : array_like or unicode, optional
        *Spectral Locus* colours, if ``spectral_locus_colours`` is set to
        *RGB*, the colours will be computed according to the corresponding
        chromaticity coordinates.
    spectral_locus_labels : array_like, optional
        Array of wavelength labels used to customise which labels will be drawn
        around the spectral locus. Passing an empty array will result in no
        wavelength labels being drawn.
    method : unicode, optional
        **{'CIE 1931', 'CIE 1960 UCS', 'CIE 1976 UCS'}**,
        *Chromaticity Diagram* method.

    Other Parameters
    ----------------
    \\**kwargs : dict, optional
        {:func:`colour.plotting.artist`, :func:`colour.plotting.render`},
        Please refer to the documentation of the previously listed definitions.

    Returns
    -------
    tuple
        Current figure and axes.

    Examples
    --------
    >>> plot_spectral_locus(spectral_locus_colours='RGB')  # doctest: +SKIP

    .. image:: ../_static/Plotting_Plot_Spectral_Locus.png
        :align: center
        :alt: plot_spectral_locus
    """

    if spectral_locus_colours is None:
        spectral_locus_colours = COLOUR_STYLE_CONSTANTS.colour.dark

    settings = {'uniform': True}
    settings.update(kwargs)

    figure, axes = artist(**settings)

    method = method.upper()

    cmfs = first_item(filter_cmfs(cmfs).values())

    illuminant = COLOUR_STYLE_CONSTANTS.colour.colourspace.whitepoint

    wavelengths = cmfs.wavelengths
    equal_energy = np.array([1 / 3] * 2)

    if method == 'CIE 1931':
        ij = XYZ_to_xy(cmfs.values, illuminant)
        labels = ((390, 460, 470, 480, 490, 500, 510, 520, 540, 560, 580, 600,
                   620, 700)
                  if spectral_locus_labels is None else spectral_locus_labels)
    elif method == 'CIE 1960 UCS':
        ij = UCS_to_uv(XYZ_to_UCS(cmfs.values))
        labels = ((420, 440, 450, 460, 470, 480, 490, 500, 510, 520, 530, 540,
                   550, 560, 570, 580, 590, 600, 610, 620, 630, 645, 680)
                  if spectral_locus_labels is None else spectral_locus_labels)
    elif method == 'CIE 1976 UCS':
        ij = Luv_to_uv(XYZ_to_Luv(cmfs.values, illuminant), illuminant)
        labels = ((420, 440, 450, 460, 470, 480, 490, 500, 510, 520, 530, 540,
                   550, 560, 570, 580, 590, 600, 610, 620, 630, 645, 680)
                  if spectral_locus_labels is None else spectral_locus_labels)
    else:
        raise ValueError(
            'Invalid method: "{0}", must be one of '
            '{\'CIE 1931\', \'CIE 1960 UCS\', \'CIE 1976 UCS\'}'.format(
                method))

    pl_ij = tstack([
        np.linspace(ij[0][0], ij[-1][0], 20),
        np.linspace(ij[0][1], ij[-1][1], 20)
    ]).reshape(-1, 1, 2)
    sl_ij = np.copy(ij).reshape(-1, 1, 2)

    if spectral_locus_colours.upper() == 'RGB':
        spectral_locus_colours = normalise_maximum(
            XYZ_to_plotting_colourspace(cmfs.values), axis=-1)

        if method == 'CIE 1931':
            XYZ = xy_to_XYZ(pl_ij)
        elif method == 'CIE 1960 UCS':
            XYZ = xy_to_XYZ(UCS_uv_to_xy(pl_ij))
        elif method == 'CIE 1976 UCS':
            XYZ = xy_to_XYZ(Luv_uv_to_xy(pl_ij))
        purple_line_colours = normalise_maximum(
            XYZ_to_plotting_colourspace(XYZ.reshape(-1, 3)), axis=-1)
    else:
        purple_line_colours = spectral_locus_colours

    for slp_ij, slp_colours in ((pl_ij, purple_line_colours),
                                (sl_ij, spectral_locus_colours)):
        line_collection = LineCollection(
            np.concatenate([slp_ij[:-1], slp_ij[1:]], axis=1),
            colors=slp_colours)
        axes.add_collection(line_collection)

    wl_ij = dict(tuple(zip(wavelengths, ij)))
    for label in labels:
        i, j = wl_ij[label]

        index = bisect.bisect(wavelengths, label)
        left = wavelengths[index - 1] if index >= 0 else wavelengths[index]
        right = (wavelengths[index]
                 if index < len(wavelengths) else wavelengths[-1])

        dx = wl_ij[right][0] - wl_ij[left][0]
        dy = wl_ij[right][1] - wl_ij[left][1]

        ij = np.array([i, j])
        direction = np.array([-dy, dx])

        normal = (np.array([-dy, dx]) if np.dot(
            normalise_vector(ij - equal_energy), normalise_vector(direction)) >
                  0 else np.array([dy, -dx]))
        normal = normalise_vector(normal) / 30

        label_colour = (spectral_locus_colours
                        if is_string(spectral_locus_colours) else
                        spectral_locus_colours[index])
        axes.plot(
            (i, i + normal[0] * 0.75), (j, j + normal[1] * 0.75),
            color=label_colour)

        axes.plot(i, j, 'o', color=label_colour)

        axes.text(
            i + normal[0],
            j + normal[1],
            label,
            clip_on=True,
            ha='left' if normal[0] >= 0 else 'right',
            va='center',
            fontdict={'size': 'small'})

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

    return render(**kwargs)
Esempio n. 20
0
def plot_chromaticity_diagram_colours(
        samples=256,
        diagram_opacity=1.0,
        diagram_clipping_path=None,
        cmfs='CIE 1931 2 Degree Standard Observer',
        method='CIE 1931',
        **kwargs):
    """
    Plots the *Chromaticity Diagram* colours according to given method.

    Parameters
    ----------
    samples : numeric, optional
        Samples count on one axis.
    diagram_opacity : numeric, optional
        Opacity of the *Chromaticity Diagram* colours.
    diagram_clipping_path : array_like, optional
        Path of points used to clip the *Chromaticity Diagram* colours.
    cmfs : unicode, optional
        Standard observer colour matching functions used for
        *Chromaticity Diagram* bounds.
    method : unicode, optional
        **{'CIE 1931', 'CIE 1960 UCS', 'CIE 1976 UCS'}**,
        *Chromaticity Diagram* method.

    Other Parameters
    ----------------
    \\**kwargs : dict, optional
        {:func:`colour.plotting.artist`, :func:`colour.plotting.render`},
        Please refer to the documentation of the previously listed definitions.

    Returns
    -------
    tuple
        Current figure and axes.

    Examples
    --------
    >>> plot_chromaticity_diagram_colours()  # doctest: +SKIP

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

    settings = {'uniform': True}
    settings.update(kwargs)

    figure, axes = artist(**settings)

    method = method.upper()

    cmfs = first_item(filter_cmfs(cmfs).values())

    illuminant = COLOUR_STYLE_CONSTANTS.colour.colourspace.whitepoint

    ii, jj = np.meshgrid(
        np.linspace(0, 1, samples), np.linspace(1, 0, samples))
    ij = tstack([ii, jj])

    if method == 'CIE 1931':
        XYZ = xy_to_XYZ(ij)
        spectral_locus = XYZ_to_xy(cmfs.values, illuminant)
    elif method == 'CIE 1960 UCS':
        XYZ = xy_to_XYZ(UCS_uv_to_xy(ij))
        spectral_locus = UCS_to_uv(XYZ_to_UCS(cmfs.values))
    elif method == 'CIE 1976 UCS':
        XYZ = xy_to_XYZ(Luv_uv_to_xy(ij))
        spectral_locus = Luv_to_uv(
            XYZ_to_Luv(cmfs.values, illuminant), illuminant)
    else:
        raise ValueError(
            'Invalid method: "{0}", must be one of '
            '{\'CIE 1931\', \'CIE 1960 UCS\', \'CIE 1976 UCS\'}'.format(
                method))

    RGB = 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',
        edgecolor='none')
    axes.add_patch(polygon)
    # Preventing bounding box related issues as per
    # https://github.com/matplotlib/matplotlib/issues/10529
    image = axes.imshow(
        RGB,
        interpolation='bilinear',
        extent=(0, 1, 0, 1),
        clip_path=None,
        alpha=diagram_opacity)
    image.set_clip_path(polygon)

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

    return render(**kwargs)
Esempio n. 21
0
def planckian_locus_CIE_1960_UCS_chromaticity_diagram_plot(
        illuminants=None,
        **kwargs):
    """
    Plots the planckian locus and given illuminants in
    *CIE 1960 UCS Chromaticity Diagram*.

    Parameters
    ----------
    illuminants : array_like, optional
        Factory illuminants to plot.
    \**kwargs : dict, optional
        Keywords arguments.

    Returns
    -------
    Figure
        Current figure or None.

    Raises
    ------
    KeyError
        If one of the given illuminant is not found in the factory illuminants.

    Examples
    --------
    >>> ils = ['A', 'C', 'E']
    >>> planckian_locus_CIE_1960_UCS_chromaticity_diagram_plot(
    ...     ils)  # doctest: +SKIP
    """

    if illuminants is None:
        illuminants = ('A', 'C', 'E')

    cmfs = CMFS.get('CIE 1931 2 Degree Standard Observer')

    settings = {
        'title': ('{0} Illuminants - Planckian Locus\n'
                  'CIE 1960 UCS Chromaticity Diagram - '
                  'CIE 1931 2 Degree Standard Observer').format(
            ', '.join(illuminants))
        if illuminants else
        ('Planckian Locus\nCIE 1960 UCS Chromaticity Diagram - '
         'CIE 1931 2 Degree Standard Observer'),
        'standalone': False}
    settings.update(kwargs)

    CIE_1960_UCS_chromaticity_diagram_plot(**settings)

    start, end = 1667, 100000
    uv = np.array([CCT_to_uv(x, 0, method='Robertson 1968')
                   for x in np.arange(start, end + 250, 250)])

    pylab.plot(uv[..., 0], uv[..., 1], color='black', linewidth=2)

    for i in (1667, 2000, 2500, 3000, 4000, 6000, 10000):
        u0, v0 = CCT_to_uv(i, -0.05, method='Robertson 1968')
        u1, v1 = CCT_to_uv(i, 0.05, method='Robertson 1968')
        pylab.plot((u0, u1), (v0, v1), color='black', linewidth=2)
        pylab.annotate('{0}K'.format(i),
                       xy=(u0, v0),
                       xytext=(0, -10),
                       color='black',
                       textcoords='offset points',
                       size='x-small')

    for illuminant in illuminants:
        xy = ILLUMINANTS.get(cmfs.name).get(illuminant)
        if xy is None:
            raise KeyError(
                ('Illuminant "{0}" not found in factory illuminants: '
                 '"{1}".').format(illuminant,
                                  sorted(ILLUMINANTS.get(cmfs.name).keys())))

        uv = UCS_to_uv(XYZ_to_UCS(xy_to_XYZ(xy)))

        pylab.plot(uv[0], uv[1], 'o', color='white', linewidth=2)

        pylab.annotate(illuminant,
                       xy=(uv[0], uv[1]),
                       xytext=(-50, 30),
                       color='black',
                       textcoords='offset points',
                       arrowprops=dict(arrowstyle='->',
                                       connectionstyle='arc3, rad=-0.2'))

    settings.update({
        'x_tighten': True,
        'y_tighten': True,
        'limits': (-0.1, 0.7, -0.2, 0.6),
        'standalone': True})
    settings.update(kwargs)

    boundaries(**settings)
    decorate(**settings)

    return display(**settings)
Esempio n. 22
0
def colour_rendering_index(sd_test, additional_data=False):
    """
    Returns the *Colour Rendering Index* (CRI) :math:`Q_a` of given spectral
    distribution.

    Parameters
    ----------
    sd_test : SpectralDistribution
        Test spectral distribution.
    additional_data : bool, optional
        Whether to output additional data.

    Returns
    -------
    numeric or CRI_Specification
        *Colour Rendering Index* (CRI).

    References
    ----------
    :cite:`Ohno2008a`

    Examples
    --------
    >>> from colour import ILLUMINANTS_SDS
    >>> sd = ILLUMINANTS_SDS['FL2']
    >>> colour_rendering_index(sd)  # doctest: +ELLIPSIS
    64.1515202...
    """

    cmfs = STANDARD_OBSERVERS_CMFS['CIE 1931 2 Degree Standard Observer'].copy(
    ).trim(DEFAULT_SPECTRAL_SHAPE)

    shape = cmfs.shape
    sd_test = sd_test.copy().align(shape)
    tcs_sds = {sd.name: sd.copy().align(shape) for sd in TCS_SDS.values()}

    with domain_range_scale('1'):
        XYZ = sd_to_XYZ(sd_test, cmfs)

    uv = UCS_to_uv(XYZ_to_UCS(XYZ))
    CCT, _D_uv = uv_to_CCT_Robertson1968(uv)

    if CCT < 5000:
        sd_reference = sd_blackbody(CCT, shape)
    else:
        xy = CCT_to_xy_CIE_D(CCT)
        sd_reference = sd_CIE_illuminant_D_series(xy)
        sd_reference.align(shape)

    test_tcs_colorimetry_data = tcs_colorimetry_data(sd_test,
                                                     sd_reference,
                                                     tcs_sds,
                                                     cmfs,
                                                     chromatic_adaptation=True)

    reference_tcs_colorimetry_data = tcs_colorimetry_data(
        sd_reference, sd_reference, tcs_sds, cmfs)

    Q_as = colour_rendering_indexes(test_tcs_colorimetry_data,
                                    reference_tcs_colorimetry_data)

    Q_a = np.average(
        [v.Q_a for k, v in Q_as.items() if k in (1, 2, 3, 4, 5, 6, 7, 8)])

    if additional_data:
        return CRI_Specification(
            sd_test.name, Q_a, Q_as,
            (test_tcs_colorimetry_data, reference_tcs_colorimetry_data))
    else:
        return Q_a
Esempio n. 23
0
def tcs_colorimetry_data(sd_t,
                         sd_r,
                         sds_tcs,
                         cmfs,
                         chromatic_adaptation=False):
    """
    Returns the *test colour samples* colorimetry data.

    Parameters
    ----------
    sd_t : SpectralDistribution
        Test spectral distribution.
    sd_r : SpectralDistribution
        Reference spectral distribution.
    sds_tcs : dict
        *Test colour samples* spectral distributions.
    cmfs : XYZ_ColourMatchingFunctions
        Standard observer colour matching functions.
    chromatic_adaptation : bool, optional
        Perform chromatic adaptation.

    Returns
    -------
    list
        *Test colour samples* colorimetry data.
    """

    XYZ_t = sd_to_XYZ(sd_t, cmfs)
    uv_t = UCS_to_uv(XYZ_to_UCS(XYZ_t))
    u_t, v_t = uv_t[0], uv_t[1]

    XYZ_r = sd_to_XYZ(sd_r, cmfs)
    uv_r = UCS_to_uv(XYZ_to_UCS(XYZ_r))
    u_r, v_r = uv_r[0], uv_r[1]

    tcs_data = []
    for _key, value in sorted(TCS_INDEXES_TO_NAMES.items()):
        sd_tcs = sds_tcs[value]
        XYZ_tcs = sd_to_XYZ(sd_tcs, cmfs, sd_t)
        xyY_tcs = XYZ_to_xyY(XYZ_tcs)
        uv_tcs = UCS_to_uv(XYZ_to_UCS(XYZ_tcs))
        u_tcs, v_tcs = uv_tcs[0], uv_tcs[1]

        if chromatic_adaptation:

            def c(x, y):
                """
                Computes the :math:`c` term.
                """

                return (4 - x - 10 * y) / y

            def d(x, y):
                """
                Computes the :math:`d` term.
                """

                return (1.708 * y + 0.404 - 1.481 * x) / y

            c_t, d_t = c(u_t, v_t), d(u_t, v_t)
            c_r, d_r = c(u_r, v_r), d(u_r, v_r)
            tcs_c, tcs_d = c(u_tcs, v_tcs), d(u_tcs, v_tcs)
            u_tcs = (
                (10.872 + 0.404 * c_r / c_t * tcs_c - 4 * d_r / d_t * tcs_d) /
                (16.518 + 1.481 * c_r / c_t * tcs_c - d_r / d_t * tcs_d))
            v_tcs = (5.52 /
                     (16.518 + 1.481 * c_r / c_t * tcs_c - d_r / d_t * tcs_d))

        W_tcs = 25 * spow(xyY_tcs[-1], 1 / 3) - 17
        U_tcs = 13 * W_tcs * (u_tcs - u_r)
        V_tcs = 13 * W_tcs * (v_tcs - v_r)

        tcs_data.append(
            TCS_ColorimetryData(sd_tcs.name, XYZ_tcs, uv_tcs,
                                np.array([U_tcs, V_tcs, W_tcs])))

    return tcs_data
Esempio n. 24
0
def CIE_1960_UCS_chromaticity_diagram_plot(
        cmfs='CIE 1931 2 Degree Standard Observer',
        show_diagram_colours=True,
        **kwargs):
    """
    Plots the *CIE 1960 UCS Chromaticity Diagram*.

    Parameters
    ----------
    cmfs : unicode, optional
        Standard observer colour matching functions used for diagram bounds.
    show_diagram_colours : bool, optional
        Whether to display the chromaticity diagram background colours.

    Other Parameters
    ----------------
    \**kwargs : dict, optional
        {:func:`boundaries`, :func:`canvas`, :func:`decorate`,
        :func:`display`},
        Please refer to the documentation of the previously listed definitions.

    Returns
    -------
    Figure
        Current figure or None.

    Examples
    --------
    >>> CIE_1960_UCS_chromaticity_diagram_plot()  # doctest: +SKIP
    """

    settings = {'figure_size': (DEFAULT_FIGURE_WIDTH, DEFAULT_FIGURE_WIDTH)}
    settings.update(kwargs)

    canvas(**settings)

    cmfs = get_cmfs(cmfs)

    if show_diagram_colours:
        image = matplotlib.image.imread(
            os.path.join(
                PLOTTING_RESOURCES_DIRECTORY,
                'CIE_1960_UCS_Chromaticity_Diagram_{0}.png'.format(
                    cmfs.name.replace(' ', '_'))))
        pylab.imshow(image, interpolation=None, extent=(0, 1, 0, 1))

    labels = (420, 430, 440, 450, 460, 470, 480, 490, 500, 510, 520, 530, 540,
              550, 560, 570, 580, 590, 600, 610, 620, 630, 640, 680)

    wavelengths = cmfs.wavelengths
    equal_energy = np.array([1 / 3] * 2)

    uv = UCS_to_uv(XYZ_to_UCS(cmfs.values))

    wavelengths_chromaticity_coordinates = dict(tuple(zip(wavelengths, uv)))

    pylab.plot(uv[..., 0], uv[..., 1], color='black', linewidth=2)
    pylab.plot((uv[-1][0], uv[0][0]), (uv[-1][1], uv[0][1]),
               color='black',
               linewidth=2)

    for label in labels:
        u, v = wavelengths_chromaticity_coordinates[label]
        pylab.plot(u, v, 'o', color='black', linewidth=2)

        index = bisect.bisect(wavelengths, label)
        left = wavelengths[index - 1] if index >= 0 else wavelengths[index]
        right = (wavelengths[index]
                 if index < len(wavelengths) else wavelengths[-1])

        dx = (wavelengths_chromaticity_coordinates[right][0] -
              wavelengths_chromaticity_coordinates[left][0])
        dy = (wavelengths_chromaticity_coordinates[right][1] -
              wavelengths_chromaticity_coordinates[left][1])

        uv = np.array([u, v])
        direction = np.array([-dy, dx])

        normal = (np.array([
            -dy, dx
        ]) if np.dot(normalise_vector(uv - equal_energy),
                     normalise_vector(direction)) > 0 else np.array([dy, -dx]))
        normal = normalise_vector(normal)
        normal /= 25

        pylab.plot((u, u + normal[0] * 0.75), (v, v + normal[1] * 0.75),
                   color='black',
                   linewidth=1.5)
        pylab.text(u + normal[0],
                   v + normal[1],
                   label,
                   color='black',
                   clip_on=True,
                   ha='left' if normal[0] >= 0 else 'right',
                   va='center',
                   fontdict={'size': 'small'})

    ticks = np.arange(-10, 10, 0.1)

    pylab.xticks(ticks)
    pylab.yticks(ticks)

    settings.update({
        'title':
        'CIE 1960 UCS Chromaticity Diagram - {0}'.format(cmfs.title),
        'x_label':
        'CIE u',
        'y_label':
        'CIE v',
        'grid':
        True,
        'bounding_box': (0, 1, 0, 1)
    })
    settings.update(kwargs)

    boundaries(**settings)
    decorate(**settings)

    return display(**settings)
Esempio n. 25
0
def colour_rendering_index(spd_test, additional_data=False):
    """
    Returns the *Colour Rendering Index* (CRI) :math:`Q_a` of given spectral
    power distribution.

    Parameters
    ----------
    spd_test : SpectralPowerDistribution
        Test spectral power distribution.
    additional_data : bool, optional
        Output additional data.

    Returns
    -------
    numeric or CRI_Specification
        *Colour Rendering Index* (CRI).

    Examples
    --------
    >>> from colour import ILLUMINANTS_RELATIVE_SPDS
    >>> spd = ILLUMINANTS_RELATIVE_SPDS['F2']
    >>> colour_rendering_index(spd)  # doctest: +ELLIPSIS
    64.1515202...
    """

    cmfs = STANDARD_OBSERVERS_CMFS[
        'CIE 1931 2 Degree Standard Observer'].clone().trim_wavelengths(
            ASTME30815_PRACTISE_SHAPE)

    shape = cmfs.shape
    spd_test = spd_test.clone().align(shape)
    tcs_spds = {
        spd.name: spd.clone().align(shape)
        for spd in TCS_SPDS.values()
    }

    XYZ = spectral_to_XYZ(spd_test, cmfs)
    uv = UCS_to_uv(XYZ_to_UCS(XYZ))
    CCT, _D_uv = uv_to_CCT_Robertson1968(uv)

    if CCT < 5000:
        spd_reference = blackbody_spd(CCT, shape)
    else:
        xy = CCT_to_xy_CIE_D(CCT)
        spd_reference = D_illuminant_relative_spd(xy)
        spd_reference.align(shape)

    test_tcs_colorimetry_data = tcs_colorimetry_data(
        spd_test, spd_reference, tcs_spds, cmfs, chromatic_adaptation=True)

    reference_tcs_colorimetry_data = tcs_colorimetry_data(
        spd_reference, spd_reference, tcs_spds, cmfs)

    Q_as = colour_rendering_indexes(test_tcs_colorimetry_data,
                                    reference_tcs_colorimetry_data)

    Q_a = np.average(
        [v.Q_a for k, v in Q_as.items() if k in (1, 2, 3, 4, 5, 6, 7, 8)])

    if additional_data:
        return CRI_Specification(spd_test.name, Q_a, Q_as,
                                 (test_tcs_colorimetry_data,
                                  reference_tcs_colorimetry_data))
    else:
        return Q_a
Esempio n. 26
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)
Esempio n. 27
0
def CIE_1960_UCS_chromaticity_diagram_colours_plot(
        surface=1,
        samples=4096,
        cmfs='CIE 1931 2 Degree Standard Observer',
        **kwargs):
    """
    Plots the *CIE 1960 UCS Chromaticity Diagram* colours.

    Parameters
    ----------
    surface : numeric, optional
        Generated markers surface.
    samples : numeric, optional
        Samples count on one axis.
    cmfs : unicode, optional
        Standard observer colour matching functions used for diagram bounds.

    Other Parameters
    ----------------
    \**kwargs : dict, optional
        {:func:`boundaries`, :func:`canvas`, :func:`decorate`,
        :func:`display`},
        Please refer to the documentation of the previously listed definitions.

    Returns
    -------
    Figure
        Current figure or None.

    Examples
    --------
    >>> CIE_1960_UCS_chromaticity_diagram_colours_plot()  # doctest: +SKIP
    """

    settings = {'figure_size': (64, 64)}
    settings.update(kwargs)

    canvas(**settings)

    cmfs = get_cmfs(cmfs)

    illuminant = DEFAULT_PLOTTING_ILLUMINANT

    triangulation = Delaunay(UCS_to_uv(XYZ_to_UCS(cmfs.values)),
                             qhull_options='QJ')
    xx, yy = np.meshgrid(np.linspace(0, 1, samples),
                         np.linspace(0, 1, samples))
    xy = tstack((xx, yy))
    xy = xy[triangulation.find_simplex(xy) > 0]

    XYZ = xy_to_XYZ(UCS_uv_to_xy(xy))

    RGB = normalise_maximum(XYZ_to_sRGB(XYZ, illuminant), axis=-1)

    x_dot, y_dot = tsplit(xy)

    pylab.scatter(x_dot, y_dot, color=RGB, s=surface)

    settings.update({
        'x_ticker': False,
        'y_ticker': False,
        'bounding_box': (0, 1, 0, 1)
    })
    settings.update(kwargs)

    ax = matplotlib.pyplot.gca()
    matplotlib.pyplot.setp(ax, frame_on=False)

    boundaries(**settings)
    decorate(**settings)

    return display(**settings)
Esempio n. 28
0
def CCT_to_uv_Ohno2013(
        CCT,
        D_uv=0,
        cmfs=STANDARD_OBSERVERS_CMFS['CIE 1931 2 Degree Standard Observer']):
    """
    Returns the *CIE UCS* colourspace *uv* chromaticity coordinates from given
    correlated colour temperature :math:`T_{cp}`, :math:`\Delta_{uv}` and
    colour matching functions using *Ohno (2013)* method.

    Parameters
    ----------
    CCT : numeric
        Correlated colour temperature :math:`T_{cp}`.
    D_uv : numeric, optional
        :math:`\Delta_{uv}`.
    cmfs : XYZ_ColourMatchingFunctions, optional
        Standard observer colour matching functions.

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

    References
    ----------
    -   :cite:`Ohno2014a`

    Examples
    --------
    >>> from colour import STANDARD_OBSERVERS_CMFS
    >>> cmfs = STANDARD_OBSERVERS_CMFS['CIE 1931 2 Degree Standard Observer']
    >>> CCT = 6507.4342201047066
    >>> D_uv = 0.003223690901513
    >>> CCT_to_uv_Ohno2013(CCT, D_uv, cmfs)  # doctest: +ELLIPSIS
    array([ 0.1977999...,  0.3122004...])
    """

    cmfs = cmfs.copy().trim(ASTME30815_PRACTISE_SHAPE)

    shape = cmfs.shape

    delta = 0.01

    spd = blackbody_spd(CCT, shape)
    XYZ = spectral_to_XYZ(spd, cmfs)
    XYZ *= 1 / np.max(XYZ)
    UVW = XYZ_to_UCS(XYZ)
    u0, v0 = UCS_to_uv(UVW)

    if D_uv == 0:
        return np.array([u0, v0])
    else:
        spd = blackbody_spd(CCT + delta, shape)
        XYZ = spectral_to_XYZ(spd, cmfs)
        XYZ *= 1 / np.max(XYZ)
        UVW = XYZ_to_UCS(XYZ)
        u1, v1 = UCS_to_uv(UVW)

        du = u0 - u1
        dv = v0 - v1

        u = u0 - D_uv * (dv / np.hypot(du, dv))
        v = v0 + D_uv * (du / np.hypot(du, dv))

        return np.array([u, v])
Esempio n. 29
0
def spds_CIE_1960_UCS_chromaticity_diagram_plot(
        spds,
        cmfs='CIE 1931 2 Degree Standard Observer',
        annotate=True,
        **kwargs):
    """
    Plots given spectral power distribution chromaticity coordinates into the
    *CIE 1960 UCS Chromaticity Diagram*.

    Parameters
    ----------
    spds : array_like, optional
        Spectral power distributions to plot.
    cmfs : unicode, optional
        Standard observer colour matching functions used for diagram bounds.
    annotate : bool
        Should resulting chromaticity coordinates annotated with their
        respective spectral power distribution names.

    Other Parameters
    ----------------
    \**kwargs : dict, optional
        {:func:`boundaries`, :func:`canvas`, :func:`decorate`,
        :func:`display`},
        Please refer to the documentation of the previously listed definitions.
    show_diagram_colours : bool, optional
        {:func:`CIE_1960_UCS_chromaticity_diagram_plot`},
        Whether to display the chromaticity diagram background colours.

    Returns
    -------
    Figure
        Current figure or None.

    Examples
    --------
    >>> from colour import ILLUMINANTS_RELATIVE_SPDS
    >>> A = ILLUMINANTS_RELATIVE_SPDS['A']
    >>> D65 = ILLUMINANTS_RELATIVE_SPDS['D65']
    >>> spds_CIE_1960_UCS_chromaticity_diagram_plot([A, D65])  # doctest: +SKIP
    """

    settings = {}
    settings.update(kwargs)
    settings.update({'standalone': False})

    CIE_1960_UCS_chromaticity_diagram_plot(cmfs=cmfs, **settings)

    for spd in spds:
        XYZ = spectral_to_XYZ(spd) / 100
        uv = UCS_to_uv(XYZ_to_UCS(XYZ))

        pylab.plot(uv[0], uv[1], 'o', color='white')

        if spd.name is not None and annotate:
            pylab.annotate(spd.name,
                           xy=uv,
                           xytext=(50, 30),
                           color='black',
                           textcoords='offset points',
                           arrowprops=dict(arrowstyle='->',
                                           connectionstyle='arc3, rad=0.2'))

    settings.update({
        'x_tighten': True,
        'y_tighten': True,
        'limits': (-0.1, 0.7, -0.2, 0.6),
        'standalone': True
    })
    settings.update(kwargs)

    boundaries(**settings)
    decorate(**settings)

    return display(**settings)
Esempio n. 30
0
def planckian_table(uv, cmfs, start, end, count):
    """
    Returns a planckian table from given *CIE UCS* colourspace *uv*
    chromaticity coordinates, colour matching functions and temperature range
    using *Ohno (2013)* method.

    Parameters
    ----------
    uv : array_like
        *uv* chromaticity coordinates.
    cmfs : XYZ_ColourMatchingFunctions
        Standard observer colour matching functions.
    start : numeric
        Temperature range start in kelvins.
    end : numeric
        Temperature range end in kelvins.
    count : int
        Temperatures count in the planckian table.

    Returns
    -------
    list
        Planckian table.

    Examples
    --------
    >>> from colour import STANDARD_OBSERVERS_CMFS
    >>> from pprint import pprint
    >>> cmfs = STANDARD_OBSERVERS_CMFS['CIE 1931 2 Degree Standard Observer']
    >>> uv = np.array([0.1978, 0.3122])
    >>> pprint(planckian_table(uv, cmfs, 1000, 1010, 10))
    ... # doctest: +ELLIPSIS
    [PlanckianTable_Tuvdi(Ti=1000.0, \
ui=0.4479628..., vi=0.3546296..., di=0.2537355...),
     PlanckianTable_Tuvdi(Ti=1001.1111111..., \
ui=0.4477030..., vi=0.3546521..., di=0.2534831...),
     PlanckianTable_Tuvdi(Ti=1002.2222222..., \
ui=0.4474434..., vi=0.3546746..., di=0.2532310...),
     PlanckianTable_Tuvdi(Ti=1003.3333333..., \
ui=0.4471842..., vi=0.3546970..., di=0.2529792...),
     PlanckianTable_Tuvdi(Ti=1004.4444444..., \
ui=0.4469252..., vi=0.3547194..., di=0.2527277...),
     PlanckianTable_Tuvdi(Ti=1005.5555555..., \
ui=0.4466666..., vi=0.3547417..., di=0.2524765...),
     PlanckianTable_Tuvdi(Ti=1006.6666666..., \
ui=0.4464083..., vi=0.3547640..., di=0.2522256...),
     PlanckianTable_Tuvdi(Ti=1007.7777777..., \
ui=0.4461502..., vi=0.3547862..., di=0.2519751...),
     PlanckianTable_Tuvdi(Ti=1008.8888888..., \
ui=0.4458925..., vi=0.3548084..., di=0.2517248...),
     PlanckianTable_Tuvdi(Ti=1010.0, \
ui=0.4456351..., vi=0.3548306..., di=0.2514749...)]
    """

    ux, vx = uv

    cmfs = cmfs.copy().trim(ASTME30815_PRACTISE_SHAPE)

    shape = cmfs.shape

    table = []
    for Ti in np.linspace(start, end, count):
        spd = blackbody_spd(Ti, shape)
        XYZ = spectral_to_XYZ(spd, cmfs)
        XYZ /= np.max(XYZ)
        UVW = XYZ_to_UCS(XYZ)
        ui, vi = UCS_to_uv(UVW)
        di = np.hypot(ux - ui, vx - vi)
        table.append(PLANCKIAN_TABLE_TUVD(Ti, ui, vi, di))

    return table