Ejemplo n.º 1
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
Ejemplo n.º 2
0
def CCT_to_uv_Ohno2013(
    CCT,
    D_uv=0,
    cmfs=STANDARD_OBSERVERS_CMFS.get('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
    ----------
    .. [4]  Ohno, Y. (2014). Practical Use and Calculation of CCT and Duv.
            LEUKOS, 10(1), 47–55. doi:10.1080/15502724.2014.839020

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

    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.sqrt(du**2 + dv**2))
        v = v0 + D_uv * (du / np.sqrt(du**2 + dv**2))

        return np.array([u, v])
Ejemplo n.º 3
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* *CIE 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)
Ejemplo n.º 4
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 : unicode
        *RGB* colourspace of the *RGB* array.

    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
    --------
    >>> RGB = np.random.random((10, 10, 3))
    >>> c = 'Rec. 709'
    >>> RGB_chromaticity_coordinates_CIE_1960_UCS_chromaticity_diagram_plot(
    ...     RGB, c)  # doctest: +SKIP
    """

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

    colourspace, name = get_RGB_colourspace(colourspace), colourspace
    settings['colourspaces'] = ([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})
    settings.update(kwargs)

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

    return display(**settings)
Ejemplo n.º 5
0
def camera_space_to_XYZ_matrix(xy,
                               CCT_calibration_illuminant_1,
                               CCT_calibration_illuminant_2,
                               M_color_matrix_1,
                               M_color_matrix_2,
                               M_camera_calibration_1,
                               M_camera_calibration_2,
                               analog_balance,
                               M_forward_matrix_1,
                               M_forward_matrix_2,
                               chromatic_adaptation_transform='Bradford'):
    """
    Returns the *Camera Space* to *CIE XYZ* matrix for given *xy* white
    balance chromaticity coordinates.

    Parameters
    ----------
    xy : array_like
        *xy* white balance chromaticity coordinates.
    CCT_calibration_illuminant_1 : numeric
        Correlated colour temperature of *CalibrationIlluminant1*.
    CCT_calibration_illuminant_2 : numeric
        Correlated colour temperature of *CalibrationIlluminant2*.
    M_color_matrix_1 : array_like
        *ColorMatrix1* tag matrix.
    M_color_matrix_2 : array_like
        *ColorMatrix2* tag matrix.
    M_camera_calibration_1 : array_like
        *CameraCalibration1* tag matrix.
    M_camera_calibration_2 : array_like
        *CameraCalibration2* tag matrix.
    analog_balance : array_like
        *AnalogBalance* tag vector.
    M_forward_matrix_1 : array_like
        *ForwardMatrix1* tag matrix.
    M_forward_matrix_2 : array_like
        *ForwardMatrix2* tag matrix.
    chromatic_adaptation_transform : unicode, optional
        **{'CAT02', 'XYZ Scaling', 'Von Kries', 'Bradford', 'Sharp',
        'Fairchild', 'CMCCAT97', 'CMCCAT2000', 'CAT02_BRILL_CAT', 'Bianco',
        'Bianco PC'}**,
        Chromatic adaptation transform.

    Returns
    -------
    ndarray
        *Camera Space* to *CIE XYZ* matrix.

    Notes
    -----
    -   The reference illuminant is D50 as defined per
        :attr:`colour_hdri.models.dataset.dng.ADOBE_DNG_XYZ_ILLUMINANT`
        attribute.

    References
    ----------
    -   :cite:`AdobeSystems2012f`
    -   :cite:`AdobeSystems2012g`
    -   :cite:`AdobeSystems2015d`
    -   :cite:`McGuffog2012a`

    Examples
    --------
    >>> M_color_matrix_1 = np.array(
    ...     [[0.5309, -0.0229, -0.0336],
    ...      [-0.6241, 1.3265, 0.3337],
    ...      [-0.0817, 0.1215, 0.6664]])
    >>> M_color_matrix_2 = np.array(
    ...     [[0.4716, 0.0603, -0.0830],
    ...      [-0.7798, 1.5474, 0.2480],
    ...      [-0.1496, 0.1937, 0.6651]])
    >>> M_camera_calibration_1 = np.identity(3)
    >>> M_camera_calibration_2 = np.identity(3)
    >>> analog_balance = np.ones(3)
    >>> M_forward_matrix_1 = np.array(
    ...     [[0.8924, -0.1041, 0.1760],
    ...      [0.4351, 0.6621, -0.0972],
    ...      [0.0505, -0.1562, 0.9308]])
    >>> M_forward_matrix_2 = np.array(
    ...     [[0.8924, -0.1041, 0.1760],
    ...      [0.4351, 0.6621, -0.0972],
    ...      [0.0505, -0.1562, 0.9308]])
    >>> camera_space_to_XYZ_matrix(  # doctest: +ELLIPSIS
    ...     np.array([0.32816244, 0.34698169]),
    ...     2850,
    ...     6500,
    ...     M_color_matrix_1,
    ...     M_color_matrix_2,
    ...     M_camera_calibration_1,
    ...     M_camera_calibration_2,
    ...     analog_balance,
    ...     M_forward_matrix_1,
    ...     M_forward_matrix_2)
    array([[ 2.1604087..., -0.1041...    ,  0.2722498...],
           [ 1.0533324...,  0.6621...    , -0.1503561...],
           [ 0.1222553..., -0.1562...    ,  1.4398304...]])
    """

    # *ForwardMatrix1* and *ForwardMatrix2* are not included in the camera
    # profile.
    if is_identity(M_forward_matrix_1) and is_identity(M_forward_matrix_2):
        M_camera_to_XYZ = np.linalg.inv(
            XYZ_to_camera_space_matrix(xy, CCT_calibration_illuminant_1,
                                       CCT_calibration_illuminant_2,
                                       M_color_matrix_1, M_color_matrix_2,
                                       M_camera_calibration_1,
                                       M_camera_calibration_2, analog_balance))
        M_CAT = chromatic_adaptation_matrix_VonKries(
            xy_to_XYZ(xy), xy_to_XYZ(ADOBE_DNG_XYZ_ILLUMINANT),
            chromatic_adaptation_transform)
        M_camera_space_to_XYZ = dot_matrix(M_CAT, M_camera_to_XYZ)
    else:
        uv = UCS_to_uv(XYZ_to_UCS(xy_to_XYZ(xy)))
        CCT, _D_uv = uv_to_CCT_Robertson1968(uv)

        M_CC = interpolated_matrix(CCT, CCT_calibration_illuminant_1,
                                   CCT_calibration_illuminant_2,
                                   M_camera_calibration_1,
                                   M_camera_calibration_2)

        # The reference implementation :cite:`AdobeSystems2015d` diverges from
        # the white-paper :cite:`AdobeSystems2012f`:
        # The reference implementation directly computes the camera neutral by
        # multiplying directly the interpolated colour matrix :math:`CM` with
        # the tristimulus values of the *xy* white balance chromaticity
        # coordinates.
        # The current implementation is based on the white-paper so that the
        # interpolated camera calibration matrix :math:`CC` and the
        # analog balance matrix :math:`AB` are accounted for.
        camera_neutral = xy_to_camera_neutral(
            xy, CCT_calibration_illuminant_1, CCT_calibration_illuminant_2,
            M_color_matrix_1, M_color_matrix_2, M_camera_calibration_1,
            M_camera_calibration_2, analog_balance)

        M_AB = np.diagflat(analog_balance)

        M_reference_neutral = dot_vector(np.linalg.inv(dot_matrix(M_AB, M_CC)),
                                         camera_neutral)
        M_D = np.linalg.inv(np.diagflat(M_reference_neutral))
        M_FM = interpolated_matrix(CCT, CCT_calibration_illuminant_1,
                                   CCT_calibration_illuminant_2,
                                   M_forward_matrix_1, M_forward_matrix_2)
        M_camera_space_to_XYZ = dot_matrix(
            dot_matrix(M_FM, M_D), np.linalg.inv(dot_matrix(M_AB, M_CC)))

    return M_camera_space_to_XYZ
Ejemplo n.º 6
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['F2']
    >>> colour_rendering_index(sd)  # doctest: +ELLIPSIS
    64.1515202...
    """

    cmfs = STANDARD_OBSERVERS_CMFS['CIE 1931 2 Degree Standard Observer'].copy(
    ).trim(ASTME30815_PRACTISE_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
Ejemplo n.º 7
0
def plot_RGB_chromaticities_in_chromaticity_diagram(
        RGB,
        colourspace='sRGB',
        chromaticity_diagram_callable=(
            plot_RGB_colourspaces_in_chromaticity_diagram),
        method='CIE 1931',
        scatter_parameters=None,
        **kwargs):
    """
    Plots given *RGB* colourspace array in the *Chromaticity Diagram* according
    to given method.

    Parameters
    ----------
    RGB : array_like
        *RGB* colourspace array.
    colourspace : optional, unicode
        *RGB* colourspace of the *RGB* array.
    chromaticity_diagram_callable : callable, optional
        Callable responsible for drawing the *Chromaticity Diagram*.
    method : unicode, optional
        **{'CIE 1931', 'CIE 1960 UCS', 'CIE 1976 UCS'}**,
        *Chromaticity Diagram* method.
    scatter_parameters : dict, optional
        Parameters for the :func:`plt.scatter` definition, if ``c`` is set to
        *RGB*, the scatter will use given ``RGB`` colours.

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

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

    Examples
    --------
    >>> RGB = np.random.random((128, 128, 3))
    >>> plot_RGB_chromaticities_in_chromaticity_diagram(
    ...     RGB, 'ITU-R BT.709')
    ... # doctest: +SKIP

    .. image:: ../_static/Plotting_\
Plot_RGB_Chromaticities_In_Chromaticity_Diagram_Plot.png
        :align: center
        :alt: plot_RGB_chromaticities_in_chromaticity_diagram
    """

    RGB = as_float_array(RGB).reshape(-1, 3)

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

    figure, axes = artist(**settings)

    method = method.upper()

    scatter_settings = {
        's': 40,
        'c': 'RGB',
        'marker': 'o',
        'alpha': 0.85,
    }
    if scatter_parameters is not None:
        scatter_settings.update(scatter_parameters)

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

    colourspace = first_item(filter_RGB_colourspaces(colourspace).values())
    settings['colourspaces'] = (['^{0}$'.format(colourspace.name)] +
                                settings.get('colourspaces', []))

    chromaticity_diagram_callable(**settings)

    use_RGB_colours = scatter_settings['c'].upper() == 'RGB'
    if use_RGB_colours:
        RGB = RGB[RGB[:, 1].argsort()]
        scatter_settings['c'] = np.clip(
            RGB_to_RGB(RGB,
                       colourspace,
                       COLOUR_STYLE_CONSTANTS.colour.colourspace,
                       apply_encoding_cctf=True).reshape(-1, 3), 0, 1)

    XYZ = RGB_to_XYZ(RGB, colourspace.whitepoint, colourspace.whitepoint,
                     colourspace.RGB_to_XYZ_matrix)

    if method == 'CIE 1931':
        ij = XYZ_to_xy(XYZ, colourspace.whitepoint)
    elif method == 'CIE 1960 UCS':
        ij = UCS_to_uv(XYZ_to_UCS(XYZ))

    elif method == 'CIE 1976 UCS':
        ij = Luv_to_uv(XYZ_to_Luv(XYZ, colourspace.whitepoint),
                       colourspace.whitepoint)

    axes.scatter(ij[..., 0], ij[..., 1], **scatter_settings)

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

    return render(**settings)
Ejemplo n.º 8
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

    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])
Ejemplo n.º 9
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.colorimetry import (
    ...     SPECTRAL_SHAPE_DEFAULT, MSDS_CMFS_STANDARD_OBSERVER)
    >>> from pprint import pprint
    >>> cmfs = (
    ...     MSDS_CMFS_STANDARD_OBSERVER['CIE 1931 2 Degree Standard Observer'].
    ...     copy().align(SPECTRAL_SHAPE_DEFAULT)
    ... )
    >>> 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(SPECTRAL_SHAPE_DEFAULT)

    shape = cmfs.shape

    table = []
    for Ti in np.linspace(start, end, count):
        sd = sd_blackbody(Ti, shape)
        XYZ = sd_to_XYZ(sd, 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
Ejemplo n.º 10
0
def RGB_chromaticity_coordinates_chromaticity_diagram_plot_CIE1960UCS(
        RGB,
        colourspace='sRGB',
        chromaticity_diagram_callable_CIE1960UCS=(
            RGB_colourspaces_chromaticity_diagram_plot_CIE1960UCS),
        **kwargs):
    """
    Plots given *RGB* colourspace array in *CIE 1960 UCS Chromaticity Diagram*.

    Parameters
    ----------
    RGB : array_like
        *RGB* colourspace array.
    colourspace : optional, unicode
        *RGB* colourspace of the *RGB* array.
    chromaticity_diagram_callable_CIE1960UCS : callable, optional
        Callable responsible for drawing the
        *CIE 1960 UCS Chromaticity Diagram*.

    Other Parameters
    ----------------
    \**kwargs : dict, optional
        {:func:`colour.plotting.render`},
        Please refer to the documentation of the previously listed definition.
    show_diagram_colours : bool, optional
        {:func:`colour.plotting.chromaticity_diagram_plot_CIE1960UCS`},
        Whether to display the chromaticity diagram background colours.
    use_cached_diagram_colours : bool, optional
        {:func:`colour.plotting.chromaticity_diagram_plot_CIE1960UCS`},
        Whether to used the cached chromaticity diagram background colours
        image.

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

    Examples
    --------
    >>> RGB = np.random.random((10, 10, 3))
    >>> c = 'ITU-R BT.709'
    >>> RGB_chromaticity_coordinates_chromaticity_diagram_plot_CIE1960UCS(
    ...     RGB, c)  # doctest: +SKIP
    """

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

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

    chromaticity_diagram_callable_CIE1960UCS(**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})
    settings.update(kwargs)

    return render(**settings)
Ejemplo n.º 11
0
def colour_quality_scale(sd_test,
                         additional_data=False,
                         method='NIST CQS 9.0'):
    """
    Returns the *Colour Quality Scale* (CQS) of given spectral distribution
    using given method.

    Parameters
    ----------
    sd_test : SpectralDistribution
        Test spectral distribution.
    additional_data : bool, optional
        Whether to output additional data.
    method : unicode, optional
        **{'NIST CQS 9.0', 'NIST CQS 7.4'}**,
        Computation method.

    Returns
    -------
    numeric or CQS_Specification
        Color quality scale.

    References
    ----------
    :cite:`Davis2010a`, :cite:`Ohno2008a`, :cite:`Ohno2013`

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

    method = method.lower()
    assert method.lower() in [
        m.lower() for m in COLOUR_QUALITY_SCALE_METHODS
    ], ('"{0}" method is invalid, must be one of {1}!'.format(
        method, COLOUR_QUALITY_SCALE_METHODS))

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

    shape = cmfs.shape
    sd_test = sd_test.copy().align(shape)
    vs_sds = {
        sd.name: sd.copy().align(shape)
        for sd in VS_SDS[method].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_Ohno2013(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_vs_colorimetry_data = vs_colorimetry_data(sd_test,
                                                   sd_reference,
                                                   vs_sds,
                                                   cmfs,
                                                   chromatic_adaptation=True)

    reference_vs_colorimetry_data = vs_colorimetry_data(
        sd_reference, sd_reference, vs_sds, cmfs)

    if method == 'nist cqs 9.0':
        CCT_f = 1
        scaling_f = 3.2
    else:
        XYZ_r = sd_to_XYZ(sd_reference, cmfs)
        XYZ_r /= XYZ_r[1]
        CCT_f = CCT_factor(reference_vs_colorimetry_data, XYZ_r)
        scaling_f = 3.104

    Q_as = colour_quality_scales(test_vs_colorimetry_data,
                                 reference_vs_colorimetry_data, scaling_f,
                                 CCT_f)

    D_E_RMS = delta_E_RMS(Q_as, 'D_E_ab')
    D_Ep_RMS = delta_E_RMS(Q_as, 'D_Ep_ab')

    Q_a = scale_conversion(D_Ep_RMS, CCT_f, scaling_f)

    if method == 'nist cqs 9.0':
        scaling_f = 2.93 * 1.0343
    else:
        scaling_f = 2.928

    Q_f = scale_conversion(D_E_RMS, CCT_f, scaling_f)

    G_t = gamut_area(
        [vs_CQS_data.Lab for vs_CQS_data in test_vs_colorimetry_data])
    G_r = gamut_area(
        [vs_CQS_data.Lab for vs_CQS_data in reference_vs_colorimetry_data])

    Q_g = G_t / D65_GAMUT_AREA * 100

    if method == 'nist cqs 9.0':
        Q_d = Q_p = None
    else:
        p_delta_C = np.average([
            sample_data.D_C_ab if sample_data.D_C_ab > 0 else 0
            for sample_data in Q_as.values()
        ])
        Q_p = 100 - 3.6 * (D_Ep_RMS - p_delta_C)
        Q_d = G_t / G_r * CCT_f * 100

    if additional_data:
        return CQS_Specification(
            sd_test.name, Q_a, Q_f, Q_p, Q_g, Q_d, Q_as,
            (test_vs_colorimetry_data, reference_vs_colorimetry_data))
    else:
        return Q_a
Ejemplo n.º 12
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.
    \**kwargs : dict, optional
        Keywords arguments.

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

    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
    True
    """

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

    CIE_1960_UCS_chromaticity_diagram_plot(**settings)

    cmfs = get_cmfs(cmfs)
    cmfs_shape = cmfs.shape

    for spd in spds:
        spd = spd.clone().align(cmfs_shape)
        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),
                           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)
Ejemplo n.º 13
0
def CIE_1960_UCS_chromaticity_diagram_plot(
        cmfs='CIE 1931 2 Degree Standard Observer', **kwargs):
    """
    Plots the *CIE 1960 UCS Chromaticity Diagram*.

    Parameters
    ----------
    cmfs : unicode, optional
        Standard observer colour matching functions used for diagram bounds.
    \**kwargs : dict, optional
        Keywords arguments.

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

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

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

    canvas(**settings)

    cmfs = get_cmfs(cmfs)

    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.get(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.get(right)[0] -
              wavelengths_chromaticity_coordinates.get(left)[0])
        dy = (wavelengths_chromaticity_coordinates.get(right)[1] -
              wavelengths_chromaticity_coordinates.get(left)[1])

        norme = lambda x: x / np.linalg.norm(x)

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

        normal = (np.array([-dy, dx])
                  if np.dot(norme(uv - equal_energy), norme(direction)) > 0
                  else np.array([dy, -dx]))
        normal = norme(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,
                   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),
        'bbox_inches':
        'tight',
        'pad_inches':
        0
    })
    settings.update(kwargs)

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

    return display(**settings)
Ejemplo n.º 14
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.
    \**kwargs : dict, optional
        Keywords arguments.

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

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

    if is_scipy_installed(raise_exception=True):
        from scipy.spatial import Delaunay

        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(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),
            'bbox_inches': 'tight',
            'pad_inches': 0
        })
        settings.update(kwargs)

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

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

        return display(**settings)
Ejemplo n.º 15
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)
Ejemplo n.º 16
0
def colour_rendering_index(test_spd, additional_data=False):
    """
    Returns the *colour rendering index* of given spectral power distribution.

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

    Returns
    -------
    numeric or (numeric, dict)
        Colour rendering index, Tsc data.

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

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

    shape = cmfs.shape
    test_spd = test_spd.clone().align(shape)

    tcs_spds = {}
    for index, tcs_spd in sorted(TCS_SPDS.items()):
        tcs_spds[index] = tcs_spd.clone().align(shape)

    XYZ = spectral_to_XYZ(test_spd, cmfs)
    uv = UCS_to_uv(XYZ_to_UCS(XYZ))
    CCT, Duv = uv_to_CCT_robertson1968(uv)

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

    test_tcs_colorimetry_data = _tcs_colorimetry_data(
        test_spd, reference_spd, tcs_spds, cmfs, chromatic_adaptation=True)
    reference_tcs_colorimetry_data = _tcs_colorimetry_data(
        reference_spd, reference_spd, tcs_spds, cmfs)

    colour_rendering_indexes = _colour_rendering_indexes(
        test_tcs_colorimetry_data, reference_tcs_colorimetry_data)

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

    if additional_data:
        return (colour_rendering_index, colour_rendering_indexes,
                [test_tcs_colorimetry_data, reference_tcs_colorimetry_data])
    else:
        return colour_rendering_index
Ejemplo n.º 17
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
Ejemplo n.º 18
0
def _tcs_colorimetry_data(test_spd,
                          reference_spd,
                          tsc_spds,
                          cmfs,
                          chromatic_adaptation=False):
    """
    Returns the *test colour samples* colorimetry data.

    Parameters
    ----------
    test_spd : SpectralPowerDistribution
        Test spectral power distribution.
    reference_spd : SpectralPowerDistribution
        Reference spectral power distribution.
    tsc_spds : dict
        Test colour samples.
    cmfs : XYZ_ColourMatchingFunctions
        Standard observer colour matching functions.
    chromatic_adaptation : bool, optional
        Perform chromatic adaptation.

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

    test_XYZ = spectral_to_XYZ(test_spd, cmfs)
    test_uv = np.ravel(UCS_to_uv(XYZ_to_UCS(test_XYZ)))
    test_u, test_v = test_uv[0], test_uv[1]

    reference_XYZ = spectral_to_XYZ(reference_spd, cmfs)
    reference_uv = np.ravel(UCS_to_uv(XYZ_to_UCS(reference_XYZ)))
    reference_u, reference_v = reference_uv[0], reference_uv[1]

    tcs_data = []
    for key, value in sorted(TCS_INDEXES_TO_NAMES.items()):
        tcs_spd = tsc_spds.get(value)
        tcs_XYZ = spectral_to_XYZ(tcs_spd, cmfs, test_spd)
        tcs_xyY = np.ravel(XYZ_to_xyY(tcs_XYZ))
        tcs_uv = np.ravel(UCS_to_uv(XYZ_to_UCS(tcs_XYZ)))
        tcs_u, tcs_v = tcs_uv[0], tcs_uv[1]

        if chromatic_adaptation:
            c = lambda x, y: (4 - x - 10 * y) / y
            d = lambda x, y: (1.708 * y + 0.404 - 1.481 * x) / y

            test_c, test_d = c(test_u, test_v), d(test_u, test_v)
            reference_c, reference_d = (c(reference_u, reference_v),
                                        d(reference_u, reference_v))
            tcs_c, tcs_d = c(tcs_u, tcs_v), d(tcs_u, tcs_v)
            tcs_u = ((10.872 + 0.404 * reference_c / test_c * tcs_c -
                      4 * reference_d / test_d * tcs_d) /
                     (16.518 + 1.481 * reference_c / test_c * tcs_c -
                      reference_d / test_d * tcs_d))
            tcs_v = (5.52 / (16.518 + 1.481 * reference_c / test_c * tcs_c -
                             reference_d / test_d * tcs_d))

        tcs_W = 25 * tcs_xyY[-1]**(1 / 3) - 17
        tcs_U = 13 * tcs_W * (tcs_u - reference_u)
        tcs_V = 13 * tcs_W * (tcs_v - reference_v)

        tcs_data.append(
            TSC_COLORIMETRY_DATA_NXYZUVUVW(tcs_spd.name, tcs_XYZ, tcs_uv,
                                           np.array([tcs_U, tcs_V, tcs_W])))

    return tcs_data
Ejemplo n.º 19
0
def planckian_table(
    uv: ArrayLike,
    cmfs: MultiSpectralDistributions,
    start: Floating,
    end: Floating,
    count: Integer,
) -> List[PlanckianTableRow]:
    """
    Return a planckian table from given *CIE UCS* colourspace *uv*
    chromaticity coordinates, colour matching functions and temperature range
    using *Ohno (2013)* method.

    Parameters
    ----------
    uv
        *uv* chromaticity coordinates.
    cmfs
        Standard observer colour matching functions.
    start
        Temperature range start in kelvin degrees.
    end
        Temperature range end in kelvin degrees.
    count
        Temperatures count in the planckian table.

    Returns
    -------
    :class:`list`
        Planckian table.

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

    ux, vx = tsplit(uv)

    table = []
    for Ti in np.linspace(start, end, count):
        sd = sd_blackbody(Ti, cmfs.shape)
        XYZ = sd_to_XYZ(sd, 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(PlanckianTableRow(Ti, ui, vi, di))

    return table
Ejemplo n.º 20
0
def plot_spectral_locus(
    cmfs: Union[MultiSpectralDistributions, str, Sequence[Union[
        MultiSpectralDistributions,
        str]], ] = "CIE 1931 2 Degree Standard Observer",
    spectral_locus_colours: Optional[Union[ArrayLike, str]] = None,
    spectral_locus_opacity: Floating = 1,
    spectral_locus_labels: Optional[Sequence] = None,
    method: Union[Literal["CIE 1931", "CIE 1960 UCS", "CIE 1976 UCS"],
                  str] = "CIE 1931",
    **kwargs: Any,
) -> Tuple[plt.Figure, plt.Axes]:
    """
    Plot the *Spectral Locus* according to given method.

    Parameters
    ----------
    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.
    spectral_locus_colours
        Colours of the *Spectral Locus*, if ``spectral_locus_colours`` is set
        to *RGB*, the colours will be computed according to the corresponding
        chromaticity coordinates.
    spectral_locus_opacity
        Opacity of the *Spectral Locus*.
    spectral_locus_labels
        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
        *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_spectral_locus(spectral_locus_colours='RGB')  # doctest: +ELLIPSIS
    (<Figure size ... with 1 Axes>, <...AxesSubplot...>)

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

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

    spectral_locus_colours = optional(spectral_locus_colours,
                                      CONSTANTS_COLOUR_STYLE.colour.dark)

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

    _figure, axes = artist(**settings)

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

    illuminant = CONSTANTS_COLOUR_STYLE.colour.colourspace.whitepoint

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

    if method == "cie 1931":
        ij = XYZ_to_xy(cmfs.values, illuminant)
        labels = cast(
            Tuple,
            optional(
                spectral_locus_labels,
                (
                    390,
                    460,
                    470,
                    480,
                    490,
                    500,
                    510,
                    520,
                    540,
                    560,
                    580,
                    600,
                    620,
                    700,
                ),
            ),
        )
    elif method == "cie 1960 ucs":
        ij = UCS_to_uv(XYZ_to_UCS(cmfs.values))
        labels = cast(
            Tuple,
            optional(
                spectral_locus_labels,
                (
                    420,
                    440,
                    450,
                    460,
                    470,
                    480,
                    490,
                    500,
                    510,
                    520,
                    530,
                    540,
                    550,
                    560,
                    570,
                    580,
                    590,
                    600,
                    610,
                    620,
                    630,
                    645,
                    680,
                ),
            ),
        )
    elif method == "cie 1976 ucs":
        ij = Luv_to_uv(XYZ_to_Luv(cmfs.values, illuminant), illuminant)
        labels = cast(
            Tuple,
            optional(
                spectral_locus_labels,
                (
                    420,
                    440,
                    450,
                    460,
                    470,
                    480,
                    490,
                    500,
                    510,
                    520,
                    530,
                    540,
                    550,
                    560,
                    570,
                    580,
                    590,
                    600,
                    610,
                    620,
                    630,
                    645,
                    680,
                ),
            ),
        )

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

    purple_line_colours: Optional[Union[ArrayLike, str]]
    if str(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(
            np.reshape(XYZ, (-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,
            alpha=spectral_locus_opacity,
            zorder=CONSTANTS_COLOUR_STYLE.zorder.midground_scatter,
        )
        axes.add_collection(line_collection)

    wl_ij = dict(zip(wavelengths, ij))
    for label in labels:
        ij_l = wl_ij.get(label)

        if ij_l is None:
            continue

        ij_l = as_float_array([ij_l])
        i, j = tsplit(ij_l)

        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]

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

        normal = (np.array([-dy, dx]) if np.dot(
            normalise_vector(ij_l - 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]  # type: ignore[index]
        )
        axes.plot(
            (i, i + normal[0] * 0.75),
            (j, j + normal[1] * 0.75),
            color=label_colour,
            alpha=spectral_locus_opacity,
            zorder=CONSTANTS_COLOUR_STYLE.zorder.background_line,
        )

        axes.plot(
            i,
            j,
            "o",
            color=label_colour,
            alpha=spectral_locus_opacity,
            zorder=CONSTANTS_COLOUR_STYLE.zorder.background_line,
        )

        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"},
            zorder=CONSTANTS_COLOUR_STYLE.zorder.background_label,
        )

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

    return render(**kwargs)
Ejemplo n.º 21
0
def RGB_colourspaces_CIE_1960_UCS_chromaticity_diagram_plot(
        colourspaces=None,
        cmfs='CIE 1931 2 Degree Standard Observer',
        **kwargs):
    """
    Plots given *RGB* colourspaces in *CIE 1960 UCS Chromaticity Diagram*.

    Parameters
    ----------
    colourspaces : array_like, optional
        *RGB* colourspaces to plot.
    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.
    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
    --------
    >>> c = ['Rec. 709', 'ACEScg', 'S-Gamut']
    >>> RGB_colourspaces_CIE_1960_UCS_chromaticity_diagram_plot(
    ...     c)  # doctest: +SKIP
    """

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

    canvas(**settings)

    if colourspaces is None:
        colourspaces = ('Rec. 709', 'ACEScg', 'S-Gamut', 'Pointer Gamut')

    cmfs, name = get_cmfs(cmfs), cmfs

    settings = {
        'title':
        '{0} - {1} - CIE 1960 UCS Chromaticity Diagram'.format(
            ', '.join(colourspaces), name),
        'standalone':
        False
    }
    settings.update(kwargs)

    CIE_1960_UCS_chromaticity_diagram_plot(**settings)

    x_limit_min, x_limit_max = [-0.1], [0.7]
    y_limit_min, y_limit_max = [-0.2], [0.6]

    settings = {
        'colour_cycle_map': 'rainbow',
        'colour_cycle_count': len(colourspaces)
    }
    settings.update(kwargs)

    cycle = colour_cycle(**settings)

    for colourspace in colourspaces:
        if colourspace == 'Pointer Gamut':
            uv = UCS_to_uv(XYZ_to_UCS(xy_to_XYZ(POINTER_GAMUT_BOUNDARIES)))
            alpha_p, colour_p = 0.85, '0.95'
            pylab.plot(uv[..., 0],
                       uv[..., 1],
                       label='Pointer\'s Gamut',
                       color=colour_p,
                       alpha=alpha_p,
                       linewidth=2)
            pylab.plot((uv[-1][0], uv[0][0]), (uv[-1][1], uv[0][1]),
                       color=colour_p,
                       alpha=alpha_p,
                       linewidth=2)

            XYZ = Lab_to_XYZ(LCHab_to_Lab(POINTER_GAMUT_DATA),
                             POINTER_GAMUT_ILLUMINANT)
            uv = UCS_to_uv(XYZ_to_UCS(XYZ))
            pylab.scatter(uv[..., 0],
                          uv[..., 1],
                          alpha=alpha_p / 2,
                          color=colour_p,
                          marker='+')

        else:
            colourspace, name = get_RGB_colourspace(colourspace), colourspace

            r, g, b, _a = next(cycle)

            # RGB colourspaces such as *ACES2065-1* have primaries with
            # chromaticity coordinates set to 0 thus we prevent nan from being
            # yield by zero division in later colour transformations.
            P = np.where(colourspace.primaries == 0, EPSILON,
                         colourspace.primaries)

            P = UCS_to_uv(XYZ_to_UCS(xy_to_XYZ(P)))
            W = UCS_to_uv(XYZ_to_UCS(xy_to_XYZ(colourspace.whitepoint)))

            pylab.plot((W[0], W[0]), (W[1], W[1]),
                       color=(r, g, b),
                       label=colourspace.name,
                       linewidth=2)
            pylab.plot((W[0], W[0]), (W[1], W[1]),
                       'o',
                       color=(r, g, b),
                       linewidth=2)
            pylab.plot((P[0, 0], P[1, 0]), (P[0, 1], P[1, 1]),
                       'o-',
                       color=(r, g, b),
                       linewidth=2)
            pylab.plot((P[1, 0], P[2, 0]), (P[1, 1], P[2, 1]),
                       'o-',
                       color=(r, g, b),
                       linewidth=2)
            pylab.plot((P[2, 0], P[0, 0]), (P[2, 1], P[0, 1]),
                       'o-',
                       color=(r, g, b),
                       linewidth=2)

            x_limit_min.append(np.amin(P[..., 0]) - 0.1)
            y_limit_min.append(np.amin(P[..., 1]) - 0.1)
            x_limit_max.append(np.amax(P[..., 0]) + 0.1)
            y_limit_max.append(np.amax(P[..., 1]) + 0.1)

    settings.update({
        'legend':
        True,
        'legend_location':
        'upper right',
        'x_tighten':
        True,
        'y_tighten':
        True,
        'limits': (min(x_limit_min), max(x_limit_max), min(y_limit_min),
                   max(y_limit_max)),
        'standalone':
        True
    })
    settings.update(kwargs)

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

    return display(**settings)
Ejemplo n.º 22
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)
Ejemplo n.º 23
0
def XYZ_to_camera_space_matrix(xy, CCT_calibration_illuminant_1,
                               CCT_calibration_illuminant_2, M_color_matrix_1,
                               M_color_matrix_2, M_camera_calibration_1,
                               M_camera_calibration_2, analog_balance):
    """
    Returns the *CIE XYZ* to *Camera Space* matrix for given *xy* white balance
    chromaticity coordinates.

    Parameters
    ----------
    xy : array_like
        *xy* white balance chromaticity coordinates.
    CCT_calibration_illuminant_1 : numeric
        Correlated colour temperature of *CalibrationIlluminant1*.
    CCT_calibration_illuminant_2 : numeric
        Correlated colour temperature of *CalibrationIlluminant2*.
    M_color_matrix_1 : array_like
        *ColorMatrix1* tag matrix.
    M_color_matrix_2 : array_like
        *ColorMatrix2* tag matrix.
    M_camera_calibration_1 : array_like
        *CameraCalibration1* tag matrix.
    M_camera_calibration_2 : array_like
        *CameraCalibration2* tag matrix.
    analog_balance : array_like
        *AnalogBalance* tag vector.

    Returns
    -------
    ndarray
        *CIE XYZ* to *Camera Space* matrix.

    Notes
    -----
    -   The reference illuminant is D50 as defined per
        :attr:`colour_hdri.models.dataset.dng.ADOBE_DNG_XYZ_ILLUMINANT`
        attribute.

    References
    ----------
    -   :cite:`AdobeSystems2012f`
    -   :cite:`AdobeSystems2015d`
    -   :cite:`McGuffog2012a`

    Examples
    --------
    >>> M_color_matrix_1 = np.array(
    ...     [[0.5309, -0.0229, -0.0336],
    ...      [-0.6241, 1.3265, 0.3337],
    ...      [-0.0817, 0.1215, 0.6664]])
    >>> M_color_matrix_2 = np.array(
    ...     [[0.4716, 0.0603, -0.0830],
    ...      [-0.7798, 1.5474, 0.2480],
    ...      [-0.1496, 0.1937, 0.6651]])
    >>> M_camera_calibration_1 = np.identity(3)
    >>> M_camera_calibration_2 = np.identity(3)
    >>> analog_balance = np.ones(3)
    >>> XYZ_to_camera_space_matrix(  # doctest: +ELLIPSIS
    ...     np.array([0.34510414, 0.35162252]),
    ...     2850,
    ...     6500,
    ...     M_color_matrix_1,
    ...     M_color_matrix_2,
    ...     M_camera_calibration_1,
    ...     M_camera_calibration_2,
    ...     analog_balance)
    array([[ 0.4854908...,  0.0408106..., -0.0714282...],
           [-0.7433278...,  1.4956549...,  0.2680749...],
           [-0.1336946...,  0.1767874...,  0.6654045...]])
    """

    M_AB = np.diagflat(analog_balance)

    uv = UCS_to_uv(XYZ_to_UCS(xy_to_XYZ(xy)))
    CCT, _D_uv = uv_to_CCT_Robertson1968(uv)

    if is_identity(M_color_matrix_1) or is_identity(M_color_matrix_2):
        M_CM = (M_color_matrix_1
                if is_identity(M_color_matrix_2) else M_color_matrix_2)
    else:
        M_CM = interpolated_matrix(CCT, CCT_calibration_illuminant_1,
                                   CCT_calibration_illuminant_2,
                                   M_color_matrix_1, M_color_matrix_2)

    M_CC = interpolated_matrix(CCT, CCT_calibration_illuminant_1,
                               CCT_calibration_illuminant_2,
                               M_camera_calibration_1, M_camera_calibration_2)

    M_XYZ_to_camera_space = dot_matrix(dot_matrix(M_AB, M_CC), M_CM)

    return M_XYZ_to_camera_space
Ejemplo n.º 24
0
def XYZ_to_colourspace_model(XYZ, illuminant, model):
    """
    Converts from *CIE XYZ* tristimulus values to given colourspace model.

    Parameters
    ----------
    XYZ : array_like
        *CIE XYZ* tristimulus values.
    illuminant : array_like
        *CIE XYZ* tristimulus values *illuminant* *xy* chromaticity
        coordinates.
    model : unicode
        **{'CIE XYZ', 'CIE xyY', 'CIE xy', 'CIE Lab', 'CIE LCHab', 'CIE Luv',
        'CIE Luv uv', 'CIE LCHuv', 'CIE UCS', 'CIE UCS uv', 'CIE UVW', 'IPT',
        'Hunter Lab', 'Hunter Rdab'}**,
        Colourspace model to convert the *CIE XYZ* tristimulus values to.

    Returns
    -------
    ndarray
        Colourspace model values.

    Examples
    --------
    >>> import numpy as np
    >>> XYZ = np.array([0.07049534, 0.10080000, 0.09558313])
    >>> W = np.array([0.34570, 0.35850])
    >>> XYZ_to_colourspace_model(  # doctest: +ELLIPSIS
    ... XYZ, W, 'CIE XYZ')
    array([ 0.0704953...,  0.1008    ,  0.0955831...])
    >>> XYZ_to_colourspace_model(  # doctest: +ELLIPSIS
    ... XYZ, W, 'CIE xyY')
    array([ 0.2641477...,  0.3777000...,  0.1008    ])
    >>> XYZ_to_colourspace_model(  # doctest: +ELLIPSIS
    ... XYZ, W, 'CIE xy')
    array([ 0.2641477...,  0.3777000...])
    >>> XYZ_to_colourspace_model(  # doctest: +ELLIPSIS
    ... XYZ, W, 'CIE Lab')
    array([ 37.9856291..., -23.6290768...,  -4.4174661...])
    >>> XYZ_to_colourspace_model(  # doctest: +ELLIPSIS
    ... XYZ, W, 'CIE LCHab')
    array([  37.9856291...,   24.0384542...,  190.5892337...])
    >>> XYZ_to_colourspace_model(  # doctest: +ELLIPSIS
    ... XYZ, W, 'CIE Luv')
    array([ 37.9856291..., -28.8021959...,  -1.3580070...])
    >>> XYZ_to_colourspace_model(  # doctest: +ELLIPSIS
    ... XYZ, W, 'CIE Luv uv')
    array([ 0.1508531...,  0.4853297...])
    >>> XYZ_to_colourspace_model(  # doctest: +ELLIPSIS
    ... XYZ, W, 'CIE LCHuv')
    array([  37.9856291...,   28.8341927...,  182.6994640...])
    >>> XYZ_to_colourspace_model(  # doctest: +ELLIPSIS
    ... XYZ, W, 'CIE UCS uv')
    array([ 0.1508531...,  0.32355314...])
    >>> XYZ_to_colourspace_model(  # doctest: +ELLIPSIS
    ... XYZ, W, 'CIE UVW')
    array([-28.0579733...,  -0.8819449...,  37.0041149...])
    >>> XYZ_to_colourspace_model(  # doctest: +ELLIPSIS
    ... XYZ, W, 'IPT')
    array([ 0.3657112..., -0.1111479...,  0.0159474...])
    >>> XYZ_to_colourspace_model(  # doctest: +ELLIPSIS
    ... XYZ, W, 'Hunter Lab')
    array([ 31.7490157..., -15.1351736...,  -2.7709606...])
    >>> XYZ_to_colourspace_model(  # doctest: +ELLIPSIS
    ... XYZ, W, 'Hunter Rdab')
    array([ 10.08..., -18.7019271...,  -3.4239649...])
    """

    values = None
    if model == 'CIE XYZ':
        values = XYZ
    elif model == 'CIE xyY':
        values = XYZ_to_xyY(XYZ, illuminant)
    elif model == 'CIE xy':
        values = XYZ_to_xy(XYZ, illuminant)
    elif model == 'CIE Lab':
        values = XYZ_to_Lab(XYZ, illuminant)
    elif model == 'CIE LCHab':
        values = Lab_to_LCHab(XYZ_to_Lab(XYZ, illuminant))
    elif model == 'CIE Luv':
        values = XYZ_to_Luv(XYZ, illuminant)
    elif model == 'CIE Luv uv':
        values = Luv_to_uv(XYZ_to_Luv(XYZ, illuminant), illuminant)
    elif model == 'CIE LCHuv':
        values = Luv_to_LCHuv(XYZ_to_Luv(XYZ, illuminant))
    elif model == 'CIE UCS':
        values = XYZ_to_UCS(XYZ)
    elif model == 'CIE UCS uv':
        values = UCS_to_uv(XYZ_to_UCS(XYZ))
    elif model == 'CIE UVW':
        values = XYZ_to_UVW(XYZ * 100, illuminant)
    elif model == 'IPT':
        values = XYZ_to_IPT(XYZ)
    elif model == 'Hunter Lab':
        values = XYZ_to_Hunter_Lab(XYZ * 100, xy_to_XYZ(illuminant) * 100)
    elif model == 'Hunter Rdab':
        values = XYZ_to_Hunter_Rdab(XYZ * 100, xy_to_XYZ(illuminant) * 100)

    if values is None:
        raise ValueError(
            '"{0}" not found in colourspace models: "{1}".'.format(
                model, ', '.join(COLOURSPACE_MODELS)))

    return values
Ejemplo n.º 25
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 = 'CIE 1931 2 Degree Standard Observer'
    >>> cmfs = STANDARD_OBSERVERS_CMFS.get(cmfs)
    >>> uv = np.array([0.1978, 0.3122])
    >>> pprint(planckian_table(  # doctest: +ELLIPSIS
    ...     uv, cmfs, 1000, 1010, 10))
    [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 = np.sqrt((ux - ui)**2 + (vx - vi)**2)
        table.append(PLANCKIAN_TABLE_TUVD(Ti, ui, vi, di))

    return table
Ejemplo n.º 26
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
    -------
    bool
        Definition success.

    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
    True
    """

    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)

    xy_to_uv = lambda x: UCS_to_uv(XYZ_to_UCS(xy_to_XYZ(x)))

    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),
                       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 = xy_to_uv(xy)

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

        pylab.annotate(illuminant,
                       xy=(uv[0], uv[1]),
                       xytext=(-50, 30),
                       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)
Ejemplo n.º 27
0
def colour_quality_scale(spd_test, additional_data=False):
    """
    Returns the *Colour Quality Scale* (CQS) of given spectral power
    distribution.

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

    Returns
    -------
    numeric or CQS_Specification
        Color quality scale.

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

    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)
    vs_spds = {spd.name: spd.clone().align(shape) for spd in VS_SPDS.values()}

    XYZ = spectral_to_XYZ(spd_test, cmfs)
    uv = UCS_to_uv(XYZ_to_UCS(XYZ))
    CCT, _D_uv = uv_to_CCT_Ohno2013(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_vs_colorimetry_data = vs_colorimetry_data(spd_test,
                                                   spd_reference,
                                                   vs_spds,
                                                   cmfs,
                                                   chromatic_adaptation=True)

    reference_vs_colorimetry_data = vs_colorimetry_data(
        spd_reference, spd_reference, vs_spds, cmfs)

    XYZ_r = spectral_to_XYZ(spd_reference, cmfs)
    XYZ_r /= XYZ_r[1]
    CCT_f = CCT_factor(reference_vs_colorimetry_data, XYZ_r)

    Q_as = colour_quality_scales(test_vs_colorimetry_data,
                                 reference_vs_colorimetry_data, CCT_f)

    D_E_RMS = delta_E_RMS(Q_as, 'D_E_ab')
    D_Ep_RMS = delta_E_RMS(Q_as, 'D_Ep_ab')

    Q_a = scale_conversion(D_Ep_RMS, CCT_f)
    Q_f = scale_conversion(D_E_RMS, CCT_f, 2.928)

    p_delta_C = np.average(
        [sample_data.D_C_ab if sample_data.D_C_ab > 0 else 0
         for sample_data in Q_as.values()])  # yapf: disable
    Q_p = 100 - 3.6 * (D_Ep_RMS - p_delta_C)

    G_t = gamut_area(
        [vs_CQS_data.Lab for vs_CQS_data in test_vs_colorimetry_data])
    G_r = gamut_area(
        [vs_CQS_data.Lab for vs_CQS_data in reference_vs_colorimetry_data])

    Q_g = G_t / D65_GAMUT_AREA * 100
    Q_d = G_t / G_r * CCT_f * 100

    if additional_data:
        return CQS_Specification(
            spd_test.name, Q_a, Q_f, Q_p, Q_g, Q_d, Q_as,
            (test_vs_colorimetry_data, reference_vs_colorimetry_data))
    else:
        return Q_a
Ejemplo n.º 28
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])
    # Avoiding zero division in later colour transformations.
    ij = np.where(ij == 0, EPSILON, ij)

    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)
Ejemplo n.º 29
0
def XYZ_to_colourspace_model(XYZ, illuminant, model, **kwargs):
    """
    Converts from *CIE XYZ* tristimulus values to given colourspace model.

    Parameters
    ----------
    XYZ : array_like
        *CIE XYZ* tristimulus values.
    illuminant : array_like
        *CIE XYZ* tristimulus values *illuminant* *xy* chromaticity
        coordinates.
    model : unicode
        **{'CIE XYZ', 'CIE xyY', 'CIE xy', 'CIE Lab', 'CIE LCHab', 'CIE Luv',
        'CIE Luv uv', 'CIE LCHuv', 'CIE UCS', 'CIE UCS uv', 'CIE UVW',
        'DIN 99', 'Hunter Lab', 'Hunter Rdab', 'IPT', 'JzAzBz, 'OSA UCS',
        'hdr-CIELAB', 'hdr-IPT'}**,
        Colourspace model to convert the *CIE XYZ* tristimulus values to.

    Other Parameters
    ----------------
    \\**kwargs : dict, optional
        Keywords arguments.

    Returns
    -------
    ndarray
        Colourspace model values.

    Examples
    --------
    >>> import numpy as np
    >>> XYZ = np.array([0.20654008, 0.12197225, 0.05136952])
    >>> W = np.array([0.31270, 0.32900])
    >>> XYZ_to_colourspace_model(  # doctest: +ELLIPSIS
    ... XYZ, W, 'CIE XYZ')
    array([ 0.2065400...,  0.1219722...,  0.0513695...])
    >>> XYZ_to_colourspace_model(  # doctest: +ELLIPSIS
    ... XYZ, W, 'CIE xyY')
    array([ 0.5436955...,  0.3210794...,  0.1219722...])
    >>> XYZ_to_colourspace_model(  # doctest: +ELLIPSIS
    ... XYZ, W, 'CIE xy')
    array([ 0.5436955...,  0.3210794...])
    >>> XYZ_to_colourspace_model(  # doctest: +ELLIPSIS
    ... XYZ, W, 'CIE Lab')
    array([ 0.4152787...,  0.5263858...,  0.2692317...])
    >>> XYZ_to_colourspace_model(  # doctest: +ELLIPSIS
    ... XYZ, W, 'CIE LCHab')
    array([ 0.4152787...,  0.5912425...,  0.0752458...])
    >>> XYZ_to_colourspace_model(  # doctest: +ELLIPSIS
    ... XYZ, W, 'CIE Luv')
    array([ 0.4152787...,  0.9683626...,  0.1775210...])
    >>> XYZ_to_colourspace_model(  # doctest: +ELLIPSIS
    ... XYZ, W, 'CIE Luv uv')
    array([ 0.3772021...,  0.5012026...])
    >>> XYZ_to_colourspace_model(  # doctest: +ELLIPSIS
    ... XYZ, W, 'CIE LCHuv')
    array([ 0.4152787...,  0.9844997...,  0.0288560...])
    >>> XYZ_to_colourspace_model(  # doctest: +ELLIPSIS
    ... XYZ, W, 'CIE UCS uv')
    array([ 0.3772021...,  0.3341350...])
    >>> XYZ_to_colourspace_model(  # doctest: +ELLIPSIS
    ... XYZ, W, 'CIE UVW')
    array([ 0.9455035...,  0.1155536...,  0.4054757...])
    >>> XYZ_to_colourspace_model(  # doctest: +ELLIPSIS
    ... XYZ, W, 'DIN 99')
    array([ 0.5322822...,  0.2841634...,  0.0389839...])
    >>> XYZ_to_colourspace_model(  # doctest: +ELLIPSIS
    ... XYZ, W, 'Hunter Lab')
    array([ 0.3492452...,  0.4703302...,  0.1439330...])
    >>> XYZ_to_colourspace_model(  # doctest: +ELLIPSIS
    ... XYZ, W, 'Hunter Rdab')
    array([ 0.1219722...,  0.5709032...,  0.1747109...])
    >>> XYZ_to_colourspace_model(  # doctest: +ELLIPSIS
    ... XYZ, W, 'IPT')
    array([ 0.3842619...,  0.3848730...,  0.1888683...])
    >>> XYZ_to_colourspace_model(  # doctest: +ELLIPSIS
    ... XYZ, W, 'JzAzBz')
    array([ 0.0053504...,  0.0092430...,  0.0052600...])
    >>> XYZ_to_colourspace_model(  # doctest: +ELLIPSIS
    ... XYZ, W, 'OSA UCS')
    array([-0.0300499...,  0.0299713..., -0.0966784...])
    >>> XYZ_to_colourspace_model(  # doctest: +ELLIPSIS
    ... XYZ, W, 'hdr-CIELAB')
    array([ 0.5187002...,  0.6047633...,  0.3214551...])
    >>> XYZ_to_colourspace_model(  # doctest: +ELLIPSIS
    ... XYZ, W, 'hdr-IPT')
    array([ 0.4839376...,  0.4244990...,  0.2201954...])
    """

    with domain_range_scale(1):
        values = None
        if model == 'CIE XYZ':
            values = XYZ
        elif model == 'CIE xyY':
            values = XYZ_to_xyY(XYZ, illuminant)
        elif model == 'CIE xy':
            values = XYZ_to_xy(XYZ, illuminant)
        elif model == 'CIE Lab':
            values = XYZ_to_Lab(XYZ, illuminant)
        elif model == 'CIE LCHab':
            values = Lab_to_LCHab(XYZ_to_Lab(XYZ, illuminant))
        elif model == 'CIE Luv':
            values = XYZ_to_Luv(XYZ, illuminant)
        elif model == 'CIE Luv uv':
            values = Luv_to_uv(XYZ_to_Luv(XYZ, illuminant), illuminant)
        elif model == 'CIE LCHuv':
            values = Luv_to_LCHuv(XYZ_to_Luv(XYZ, illuminant))
        elif model == 'CIE UCS':
            values = XYZ_to_UCS(XYZ)
        elif model == 'CIE UCS uv':
            values = UCS_to_uv(XYZ_to_UCS(XYZ))
        elif model == 'CIE UVW':
            values = XYZ_to_UVW(XYZ, illuminant)
        elif model == 'DIN 99':
            values = Lab_to_DIN99(XYZ_to_Lab(XYZ, illuminant))
        elif model == 'Hunter Lab':
            values = XYZ_to_Hunter_Lab(XYZ, xy_to_XYZ(illuminant))
        elif model == 'Hunter Rdab':
            values = XYZ_to_Hunter_Rdab(XYZ, xy_to_XYZ(illuminant))
        elif model == 'IPT':
            values = XYZ_to_IPT(XYZ)
        elif model == 'JzAzBz':
            values = XYZ_to_JzAzBz(XYZ)
        elif model == 'OSA UCS':
            values = XYZ_to_OSA_UCS(XYZ)
        elif model == 'hdr-CIELAB':
            values = XYZ_to_hdr_CIELab(XYZ, illuminant, **kwargs)
        elif model == 'hdr-IPT':
            values = XYZ_to_hdr_IPT(XYZ, **kwargs)

        if values is None:
            raise ValueError(
                '"{0}" not found in colourspace models: "{1}".'.format(
                    model, ', '.join(COLOURSPACE_MODELS)))

        return values
Ejemplo n.º 30
0
def tcs_colorimetry_data(
    sd_t: SpectralDistribution,
    sd_r: SpectralDistribution,
    sds_tcs: Dict[str, SpectralDistribution],
    cmfs: MultiSpectralDistributions,
    chromatic_adaptation: Boolean = False,
) -> Tuple[TCS_ColorimetryData, ...]:
    """
    Return the *test colour samples* colorimetry data.

    Parameters
    ----------
    sd_t
        Test spectral distribution.
    sd_r
        Reference spectral distribution.
    sds_tcs
        *Test colour samples* spectral distributions.
    cmfs
        Standard observer colour matching functions.
    chromatic_adaptation
        Perform chromatic adaptation.

    Returns
    -------
    :class:`tuple`
        *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(INDEXES_TO_NAMES_TCS.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: FloatingOrNDArray, y: FloatingOrNDArray
            ) -> FloatingOrNDArray:
                """Compute the :math:`c` term."""

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

            def d(
                x: FloatingOrNDArray, y: FloatingOrNDArray
            ) -> FloatingOrNDArray:
                """Compute 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 tuple(tcs_data)