Пример #1
0
def vs_colorimetry_data(spd_test,
                        spd_reference,
                        spds_vs,
                        cmfs,
                        chromatic_adaptation=False):

    XYZ_t = spectral_to_XYZ(spd_test, cmfs)
    XYZ_t /= XYZ_t[1]

    XYZ_r = spectral_to_XYZ(spd_reference, cmfs)
    XYZ_r /= XYZ_r[1]
    xy_r = XYZ_to_xy(XYZ_r)

    vs_data = []
    for _key, value in sorted(VS_INDEXES_TO_NAMES.items()):
        spd_vs = spds_vs.get(value)
        XYZ_vs = spectral_to_XYZ(spd_vs, cmfs, spd_test)
        XYZ_vs /= 100

        if chromatic_adaptation:
            XYZ_vs = chromatic_adaptation_VonKries(XYZ_vs,
                                                   XYZ_t,
                                                   XYZ_r,
                                                   transform='CMCCAT2000')

        Lab_vs = XYZ_to_Lab(XYZ_vs, illuminant=xy_r)
        _L_vs, C_vs, _Hab = Lab_to_LCHab(Lab_vs)

        vs_data.append(VS_ColorimetryData(spd_vs.name, XYZ_vs, Lab_vs, C_vs))
    return vs_data
Пример #2
0
    def test_spectral_to_XYZ(self):
        """
        Tests :func:`colour.colorimetry.tristimulus.spectral_to_XYZ`
        definition.
        """

        cmfs = CMFS.get('CIE 1931 2 Degree Standard Observer')
        np.testing.assert_almost_equal(
            spectral_to_XYZ(
                RELATIVE_SPD_DATA.zeros(cmfs.shape), cmfs,
                ILLUMINANTS_RELATIVE_SPDS.get('A').clone().zeros(cmfs.shape)),
            np.array([14.46371626, 10.85832347, 2.04664796]),
            decimal=7)

        cmfs = CMFS.get('CIE 1964 10 Degree Standard Observer')
        np.testing.assert_almost_equal(
            spectral_to_XYZ(
                RELATIVE_SPD_DATA.zeros(cmfs.shape), cmfs,
                ILLUMINANTS_RELATIVE_SPDS.get('C').clone().zeros(cmfs.shape)),
            np.array([10.7704252, 9.44870313, 6.62742289]),
            decimal=7)

        np.testing.assert_almost_equal(
            spectral_to_XYZ(
                RELATIVE_SPD_DATA.zeros(cmfs.shape), cmfs,
                ILLUMINANTS_RELATIVE_SPDS.get('F2').clone().zeros(cmfs.shape)),
            np.array([11.57830745, 9.98744967, 3.95396539]),
            decimal=7)
Пример #3
0
    def test_spectral_to_XYZ(self):
        """
        Tests :func:`colour.colorimetry.tristimulus.spectral_to_XYZ`
        definition.
        """

        cmfs = CMFS.get('CIE 1931 2 Degree Standard Observer')
        np.testing.assert_almost_equal(
            spectral_to_XYZ(
                RELATIVE_SPD_DATA.zeros(cmfs.shape),
                cmfs,
                ILLUMINANTS_RELATIVE_SPDS.get('A').clone().zeros(cmfs.shape)),
            np.array([14.46371626, 10.85832347, 2.04664796]),
            decimal=7)

        cmfs = CMFS.get('CIE 1964 10 Degree Standard Observer')
        np.testing.assert_almost_equal(
            spectral_to_XYZ(
                RELATIVE_SPD_DATA.zeros(cmfs.shape),
                cmfs,
                ILLUMINANTS_RELATIVE_SPDS.get('C').clone().zeros(cmfs.shape)),
            np.array([10.7704252, 9.44870313, 6.62742289]),
            decimal=7)

        np.testing.assert_almost_equal(
            spectral_to_XYZ(
                RELATIVE_SPD_DATA.zeros(cmfs.shape),
                cmfs,
                ILLUMINANTS_RELATIVE_SPDS.get('F2').clone().zeros(cmfs.shape)),
            np.array([11.57830745, 9.98744967, 3.95396539]),
            decimal=7)
Пример #4
0
def vs_colorimetry_data(spd_test,
                        spd_reference,
                        spds_vs,
                        cmfs,
                        chromatic_adaptation=False):
    """
    Returns the *VS test colour samples* colorimetry data.

    Parameters
    ----------
    spd_test : SpectralPowerDistribution
        Test spectral power distribution.
    spd_reference : SpectralPowerDistribution
        Reference spectral power distribution.
    spds_vs : dict
        *VS test colour samples* spectral power distributions.
    cmfs : XYZ_ColourMatchingFunctions
        Standard observer colour matching functions.
    chromatic_adaptation : bool, optional
        Perform chromatic adaptation.

    Returns
    -------
    list
        *VS test colour samples* colorimetry data.
    """

    XYZ_t = spectral_to_XYZ(spd_test, cmfs)
    XYZ_t /= XYZ_t[1]

    XYZ_r = spectral_to_XYZ(spd_reference, cmfs)
    XYZ_r /= XYZ_r[1]
    xy_r = XYZ_to_xy(XYZ_r)

    vs_data = []
    for _key, value in sorted(VS_INDEXES_TO_NAMES.items()):
        spd_vs = spds_vs.get(value)
        XYZ_vs = spectral_to_XYZ(spd_vs, cmfs, spd_test)
        XYZ_vs /= 100

        if chromatic_adaptation:
            XYZ_vs = chromatic_adaptation_VonKries(XYZ_vs,
                                                   XYZ_t,
                                                   XYZ_r,
                                                   transform='CMCCAT2000')

        Lab_vs = XYZ_to_Lab(XYZ_vs, illuminant=xy_r)
        _L_vs, C_vs, _Hab = Lab_to_LCHab(Lab_vs)

        vs_data.append(
            VS_ColorimetryData(spd_vs.name,
                               XYZ_vs,
                               Lab_vs,
                               C_vs))
    return vs_data
Пример #5
0
def vs_colorimetry_data(spd_test,
                        spd_reference,
                        spds_vs,
                        cmfs,
                        chromatic_adaptation=False):
    """
    Returns the *VS test colour samples* colorimetry data.

    Parameters
    ----------
    spd_test : SpectralPowerDistribution
        Test spectral power distribution.
    spd_reference : SpectralPowerDistribution
        Reference spectral power distribution.
    spds_vs : dict
        *VS test colour samples* spectral power distributions.
    cmfs : XYZ_ColourMatchingFunctions
        Standard observer colour matching functions.
    chromatic_adaptation : bool, optional
        Perform chromatic adaptation.

    Returns
    -------
    list
        *VS test colour samples* colorimetry data.
    """

    XYZ_t = spectral_to_XYZ(spd_test, cmfs)
    XYZ_t /= XYZ_t[1]

    XYZ_r = spectral_to_XYZ(spd_reference, cmfs)
    XYZ_r /= XYZ_r[1]
    xy_r = XYZ_to_xy(XYZ_r)

    vs_data = []
    for _key, value in sorted(VS_INDEXES_TO_NAMES.items()):
        spd_vs = spds_vs[value]
        XYZ_vs = spectral_to_XYZ(spd_vs, cmfs, spd_test)
        XYZ_vs /= 100

        if chromatic_adaptation:
            XYZ_vs = chromatic_adaptation_VonKries(XYZ_vs,
                                                   XYZ_t,
                                                   XYZ_r,
                                                   transform='CMCCAT2000')

        Lab_vs = XYZ_to_Lab(XYZ_vs, illuminant=xy_r)
        _L_vs, C_vs, _Hab = Lab_to_LCHab(Lab_vs)

        vs_data.append(VS_ColorimetryData(spd_vs.name, XYZ_vs, Lab_vs, C_vs))
    return vs_data
Пример #6
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(uv, cmfs, 1000, 1010, 10))  # noqa  # doctest: +ELLIPSIS
    [PlanckianTable_Tuvdi(Ti=1000.0, ui=0.4480108..., vi=0.3546249..., di=0.2537821...),
     PlanckianTable_Tuvdi(Ti=1001.1111111..., ui=0.4477508..., vi=0.3546475..., di=0.2535294...),
     PlanckianTable_Tuvdi(Ti=1002.2222222..., ui=0.4474910..., vi=0.3546700..., di=0.2532771...),
     PlanckianTable_Tuvdi(Ti=1003.3333333..., ui=0.4472316..., vi=0.3546924..., di=0.2530251...),
     PlanckianTable_Tuvdi(Ti=1004.4444444..., ui=0.4469724..., vi=0.3547148..., di=0.2527734...),
     PlanckianTable_Tuvdi(Ti=1005.5555555..., ui=0.4467136..., vi=0.3547372..., di=0.2525220...),
     PlanckianTable_Tuvdi(Ti=1006.6666666..., ui=0.4464550..., vi=0.3547595..., di=0.2522710...),
     PlanckianTable_Tuvdi(Ti=1007.7777777..., ui=0.4461968..., vi=0.3547817..., di=0.2520202...),
     PlanckianTable_Tuvdi(Ti=1008.8888888..., ui=0.4459389..., vi=0.3548040..., di=0.2517697...),
     PlanckianTable_Tuvdi(Ti=1010.0, ui=0.4456812..., vi=0.3548261..., di=0.2515196...)]
    """

    ux, vx = uv

    shape = cmfs.shape

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

    return table
Пример #7
0
def planckian_table(uv, cmfs, start, end, count):
    """
    Returns a planckian table from given *CIE UCS* colourspace *uv*
    chromaticity coordinates, colour matching functions and temperature range
    using *Yoshi Ohno (2013)* method.

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

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

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

    ux, vx = uv

    shape = cmfs.shape

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

    return table
Пример #8
0
def spds_CIE_1976_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 1976 UCS Chromaticity Diagram*.

    Parameters
    ----------
    spds : list, 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 : \*\*
        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_1976_UCS_chromaticity_diagram_plot([A, D65])  # doctest: +SKIP
    True
    """

    CIE_1976_UCS_chromaticity_diagram_plot(standalone=False,
                                           **kwargs)

    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 = Luv_to_uv(XYZ_to_Luv(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'))

    display(standalone=True)
Пример #9
0
def colour_quality_scale(spd_test, T, additional_data=False):

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

    shape = cmfs.shape
    CCT, _D_uv = T

    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()
    ])
    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
Пример #10
0
def blackbody_colours_plot(shape=SpectralShape(150, 12500, 50),
                           cmfs='CIE 1931 2 Degree Standard Observer',
                           **kwargs):
    """
    Plots blackbody colours.

    Parameters
    ----------
    shape : SpectralShape, optional
        Spectral shape to use as plot boundaries.
    cmfs : unicode, optional
        Standard observer colour matching functions.
    \*\*kwargs : \*\*
        Keywords arguments.

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

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

    cmfs, name = get_cmfs(cmfs), cmfs

    colours = []
    temperatures = []

    for temperature in shape:
        spd = blackbody_spd(temperature, cmfs.shape)

        XYZ = spectral_to_XYZ(spd, cmfs)
        RGB = normalise(XYZ_to_sRGB(XYZ / 100))

        colours.append(RGB)
        temperatures.append(temperature)

    settings = {
        'title': 'Blackbody Colours',
        'x_label': 'Temperature K',
        'y_label': '',
        'x_tighten': True,
        'x_ticker': True,
        'y_ticker': False
    }

    settings.update(kwargs)
    return colour_parameters_plot([
        colour_parameter(x=x[0], RGB=x[1])
        for x in tuple(zip(temperatures, colours))
    ], **settings)
Пример #11
0
def blackbody_colours_plot(shape=SpectralShape(150, 12500, 50),
                           cmfs='CIE 1931 2 Degree Standard Observer',
                           **kwargs):
    """
    Plots blackbody colours.

    Parameters
    ----------
    shape : SpectralShape, optional
        Spectral shape to use as plot boundaries.
    cmfs : unicode, optional
        Standard observer colour matching functions.
    \*\*kwargs : \*\*
        Keywords arguments.

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

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

    cmfs = get_cmfs(cmfs)

    colours = []
    temperatures = []

    for temperature in shape:
        spd = blackbody_spd(temperature, cmfs.shape)

        XYZ = spectral_to_XYZ(spd, cmfs)
        RGB = normalise(XYZ_to_sRGB(XYZ / 100))

        colours.append(RGB)
        temperatures.append(temperature)

    settings = {
        'title': 'Blackbody Colours',
        'x_label': 'Temperature K',
        'y_label': '',
        'x_tighten': True,
        'x_ticker': True,
        'y_ticker': False}
    settings.update(kwargs)

    return colour_parameters_plot([colour_parameter(x=x[0], RGB=x[1])
                                   for x in tuple(zip(temperatures, colours))],
                                  **settings)
Пример #12
0
def blackbody_spectral_radiance_plot(
        temperature=3500,
        cmfs='CIE 1931 2 Degree Standard Observer',
        blackbody='VY Canis Major',
        **kwargs):
    """
    Plots given blackbody spectral radiance.

    Parameters
    ----------
    temperature : numeric, optional
        Blackbody temperature.
    cmfs : unicode, optional
        Standard observer colour matching functions.
    blackbody : unicode, optional
        Blackbody name.
    \**kwargs : dict, optional
        Keywords arguments.

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

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

    canvas(**kwargs)

    cmfs = get_cmfs(cmfs)

    matplotlib.pyplot.subplots_adjust(hspace=0.4)

    spd = blackbody_spd(temperature, cmfs.shape)

    matplotlib.pyplot.figure(1)
    matplotlib.pyplot.subplot(211)

    settings = {
        'title': '{0} - Spectral Radiance'.format(blackbody),
        'y_label': 'W / (sr m$^2$) / m',
        'standalone': False}
    settings.update(kwargs)

    single_spd_plot(spd, cmfs.name, **settings)

    XYZ = spectral_to_XYZ(spd, cmfs)
    RGB = normalise(XYZ_to_sRGB(XYZ / 100))

    matplotlib.pyplot.subplot(212)

    settings = {'title': '{0} - Colour'.format(blackbody),
                'x_label': '{0}K'.format(temperature),
                'y_label': '',
                'aspect': None,
                'standalone': False}

    single_colour_plot(ColourParameter(name='', RGB=RGB), **settings)

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

    boundaries(**settings)
    decorate(**settings)
    return display(**settings)
Пример #13
0
def spds_CIE_1976_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 1976 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
    -------
    Figure
        Current figure or None.

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

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

    CIE_1976_UCS_chromaticity_diagram_plot(cmfs=cmfs, **settings)

    for spd in spds:
        XYZ = spectral_to_XYZ(spd) / 100
        uv = Luv_to_uv(XYZ_to_Luv(XYZ))

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

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

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

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

    return display(**settings)
Пример #14
0
def multi_spd_plot(spds,
                   cmfs='CIE 1931 2 Degree Standard Observer',
                   use_spds_colours=False,
                   normalise_spds_colours=False,
                   **kwargs):
    """
    Plots given spectral power distributions.

    Parameters
    ----------
    spds : list
        Spectral power distributions to plot.
    cmfs : unicode, optional
        Standard observer colour matching functions used for spectrum creation.
    use_spds_colours : bool, optional
        Use spectral power distributions colours.
    normalise_spds_colours : bool
        Should spectral power distributions colours normalised.
    \**kwargs : dict, optional
        Keywords arguments.

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

    Examples
    --------
    >>> from colour import SpectralPowerDistribution
    >>> data1 = {400: 0.0641, 420: 0.0645, 440: 0.0562}
    >>> data2 = {400: 0.134, 420: 0.789, 440: 1.289}
    >>> spd1 = SpectralPowerDistribution('Custom1', data1)
    >>> spd2 = SpectralPowerDistribution('Custom2', data2)
    >>> multi_spd_plot([spd1, spd2])  # doctest: +SKIP
    True
    """

    canvas(**kwargs)

    cmfs = get_cmfs(cmfs)

    if use_spds_colours:
        illuminant = ILLUMINANTS_RELATIVE_SPDS.get('D65')

    x_limit_min, x_limit_max, y_limit_min, y_limit_max = [], [], [], []
    for spd in spds:
        wavelengths, values = tuple(zip(*spd.items))

        shape = spd.shape
        x_limit_min.append(shape.start)
        x_limit_max.append(shape.end)
        y_limit_min.append(min(values))
        y_limit_max.append(max(values))

        if use_spds_colours:
            XYZ = spectral_to_XYZ(spd, cmfs, illuminant) / 100
            if normalise_spds_colours:
                XYZ = normalise(XYZ, clip=False)
            RGB = np.clip(XYZ_to_sRGB(XYZ), 0, 1)

            pylab.plot(wavelengths, values, color=RGB, label=spd.title,
                       linewidth=2)
        else:
            pylab.plot(wavelengths, values, label=spd.title, linewidth=2)

    settings = {
        'x_label': 'Wavelength $\\lambda$ (nm)',
        'y_label': 'Spectral Power Distribution',
        'x_tighten': True,
        'legend': True,
        'legend_location': 'upper left',
        'limits': (min(x_limit_min), max(x_limit_max),
                   min(y_limit_min), max(y_limit_max))}
    settings.update(kwargs)

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

    return display(**settings)
Пример #15
0
def the_blue_sky_plot(cmfs='CIE 1931 2 Degree Standard Observer', **kwargs):
    """
    Plots the blue sky.

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

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

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

    canvas(**kwargs)

    cmfs, name = get_cmfs(cmfs), cmfs

    ASTM_G_173_spd = ASTM_G_173_ETR.clone()
    rayleigh_spd = rayleigh_scattering_spd()
    ASTM_G_173_spd.align(rayleigh_spd.shape)

    spd = rayleigh_spd * ASTM_G_173_spd

    matplotlib.pyplot.subplots_adjust(hspace=0.4)

    matplotlib.pyplot.figure(1)
    matplotlib.pyplot.subplot(211)

    settings = {
        'title': 'The Blue Sky - Synthetic Spectral Power Distribution',
        'y_label': u'W / m-2 / nm-1',
        'standalone': False
    }
    settings.update(kwargs)

    single_spd_plot(spd, name, **settings)

    matplotlib.pyplot.subplot(212)

    settings = {
        'title':
        'The Blue Sky - Colour',
        'x_label': ('The sky is blue because molecules in the atmosphere '
                    'scatter shorter wavelengths more than longer ones.\n'
                    'The synthetic spectral power distribution is computed as '
                    'follows: '
                    '(ASTM G-173 ETR * Standard Air Rayleigh Scattering).'),
        'y_label':
        '',
        'aspect':
        None,
        'standalone':
        False
    }

    blue_sky_color = XYZ_to_sRGB(spectral_to_XYZ(spd))
    single_colour_plot(ColourParameter('', normalise(blue_sky_color)),
                       **settings)

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

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

    return display(**settings)
Пример #16
0
def multi_spd_plot(spds,
                   cmfs='CIE 1931 2 Degree Standard Observer',
                   use_spds_colours=False,
                   normalise_spds_colours=False,
                   **kwargs):
    """
    Plots given spectral power distributions.

    Parameters
    ----------
    spds : list
        Spectral power distributions to plot.
    cmfs : unicode, optional
        Standard observer colour matching functions used for spectrum creation.
    use_spds_colours : bool, optional
        Whether to use spectral power distributions colours.
    normalise_spds_colours : bool
        Whether to normalise spectral power distributions colours.

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

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

    Examples
    --------
    >>> from colour import SpectralPowerDistribution
    >>> data_1 = {
    ...     500: 0.004900,
    ...     510: 0.009300,
    ...     520: 0.063270,
    ...     530: 0.165500,
    ...     540: 0.290400,
    ...     550: 0.433450,
    ...     560: 0.594500
    ... }
    >>> data_2 = {
    ...     500: 0.323000,
    ...     510: 0.503000,
    ...     520: 0.710000,
    ...     530: 0.862000,
    ...     540: 0.954000,
    ...     550: 0.994950,
    ...     560: 0.995000
    ... }
    >>> spd1 = SpectralPowerDistribution(data_1, name='Custom 1')
    >>> spd2 = SpectralPowerDistribution(data_2, name='Custom 2')
    >>> multi_spd_plot([spd1, spd2])  # doctest: +SKIP
    """

    canvas(**kwargs)

    cmfs = get_cmfs(cmfs)

    if use_spds_colours:
        illuminant = ILLUMINANTS_RELATIVE_SPDS['D65']

    x_limit_min, x_limit_max, y_limit_min, y_limit_max = [], [], [], []
    for spd in spds:
        wavelengths, values = spd.wavelengths, spd.values

        shape = spd.shape
        x_limit_min.append(shape.start)
        x_limit_max.append(shape.end)
        y_limit_min.append(min(values))
        y_limit_max.append(max(values))

        if use_spds_colours:
            XYZ = spectral_to_XYZ(spd, cmfs, illuminant) / 100
            if normalise_spds_colours:
                XYZ = normalise_maximum(XYZ, clip=False)
            RGB = np.clip(XYZ_to_sRGB(XYZ), 0, 1)

            pylab.plot(wavelengths,
                       values,
                       color=RGB,
                       label=spd.strict_name,
                       linewidth=1)
        else:
            pylab.plot(wavelengths, values, label=spd.strict_name, linewidth=1)

    settings = {
        'x_label':
        'Wavelength $\\lambda$ (nm)',
        'y_label':
        'Spectral Power Distribution',
        'x_tighten':
        True,
        'y_tighten':
        True,
        'legend':
        True,
        'legend_location':
        'upper left',
        'limits': (min(x_limit_min), max(x_limit_max), min(y_limit_min),
                   max(y_limit_max))
    }
    settings.update(kwargs)

    return render(**settings)
Пример #17
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
Пример #18
0
def tcs_colorimetry_data(spd_t,
                         spd_r,
                         spds_tcs,
                         cmfs,
                         chromatic_adaptation=False):
    """
    Returns the *test colour samples* colorimetry data.

    Parameters
    ----------
    spd_t : SpectralPowerDistribution
        Test spectral power distribution.
    spd_r : SpectralPowerDistribution
        Reference spectral power distribution.
    spds_tcs : dict
        *Test colour samples* spectral power 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 = spectral_to_XYZ(spd_t, cmfs)
    uv_t = np.ravel(UCS_to_uv(XYZ_to_UCS(XYZ_t)))
    u_t, v_t = uv_t[0], uv_t[1]

    XYZ_r = spectral_to_XYZ(spd_r, cmfs)
    uv_r = np.ravel(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()):
        spd_tcs = spds_tcs.get(value)
        XYZ_tcs = spectral_to_XYZ(spd_tcs, cmfs, spd_t)
        xyY_tcs = np.ravel(XYZ_to_xyY(XYZ_tcs))
        uv_tcs = np.ravel(UCS_to_uv(XYZ_to_UCS(XYZ_tcs)))
        u_tcs, v_tcs = uv_tcs[0], uv_tcs[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

            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 * 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(spd_tcs.name,
                                XYZ_tcs,
                                uv_tcs,
                                np.array([U_tcs, V_tcs, W_tcs])))

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

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

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

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

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

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

    shape = cmfs.shape

    delta = 0.01

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

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

        du = u0 - u1
        dv = v0 - v1

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

        return np.array([u, v])
Пример #20
0
def blackbody_colours_plot(shape=SpectralShape(150, 12500, 50),
                           cmfs='CIE 1931 2 Degree Standard Observer',
                           **kwargs):
    """
    Plots blackbody colours.

    Parameters
    ----------
    shape : SpectralShape, optional
        Spectral shape to use as plot boundaries.
    cmfs : unicode, optional
        Standard observer colour matching functions.

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

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

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

    axes = canvas(**kwargs).gca()

    cmfs = get_cmfs(cmfs)

    colours = []
    temperatures = []

    with suppress_warnings():
        for temperature in shape:
            spd = blackbody_spd(temperature, cmfs.shape)

            XYZ = spectral_to_XYZ(spd, cmfs)
            RGB = normalise_maximum(XYZ_to_sRGB(XYZ / 100))

            colours.append(RGB)
            temperatures.append(temperature)

    x_min, x_max = min(temperatures), max(temperatures)
    y_min, y_max = 0, 1

    axes.bar(x=temperatures,
             height=1,
             width=shape.interval,
             color=colours,
             align='edge')

    settings = {
        'title': 'Blackbody Colours',
        'x_label': 'Temperature K',
        'y_label': None,
        'limits': (x_min, x_max, y_min, y_max),
        'x_tighten': True,
        'y_tighten': True,
        'y_ticker': False
    }
    settings.update(kwargs)

    return render(**settings)
Пример #21
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])
Пример #22
0
def the_blue_sky_plot(
        cmfs='CIE 1931 2 Degree Standard Observer',
        **kwargs):
    """
    Plots the blue sky.

    Parameters
    ----------
    cmfs : unicode, optional
        Standard observer colour matching functions.
    \*\*kwargs : \*\*
        Keywords arguments.

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

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

    canvas(**kwargs)

    cmfs, name = get_cmfs(cmfs), cmfs

    ASTM_G_173_spd = ASTM_G_173_ETR.clone()
    rayleigh_spd = rayleigh_scattering_spd()
    ASTM_G_173_spd.align(rayleigh_spd.shape)

    spd = rayleigh_spd * ASTM_G_173_spd

    matplotlib.pyplot.subplots_adjust(hspace=0.4)

    matplotlib.pyplot.figure(1)
    matplotlib.pyplot.subplot(211)

    settings = {
        'title': 'The Blue Sky - Synthetic Spectral Power Distribution',
        'y_label': u'W / m-2 / nm-1',
        'standalone': False}
    settings.update(kwargs)

    single_spd_plot(spd, name, **settings)

    matplotlib.pyplot.subplot(212)

    settings = {
        'title': 'The Blue Sky - Colour',
        'x_label': ('The sky is blue because molecules in the atmosphere '
                    'scatter shorter wavelengths more than longer ones.\n'
                    'The synthetic spectral power distribution is computed as '
                    'follows: '
                    '(ASTM G-173 ETR * Standard Air Rayleigh Scattering).'),
        'y_label': '',
        'aspect': None,
        'standalone': False}

    blue_sky_color = XYZ_to_sRGB(spectral_to_XYZ(spd))
    single_colour_plot(colour_parameter('', normalise(blue_sky_color)),
                       **settings)

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

    boundaries(**settings)
    decorate(**settings)
    return display(**settings)
Пример #23
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.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

    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
Пример #24
0
def spds_CIE_1976_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 1976 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_1976_UCS_chromaticity_diagram_plot([A, D65])  # doctest: +SKIP
    True
    """

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

    CIE_1976_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 = Luv_to_uv(XYZ_to_Luv(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.1, 0.7),
        'standalone': True
    })
    settings.update(kwargs)

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

    return display(**settings)
Пример #25
0
def the_blue_sky_plot(cmfs='CIE 1931 2 Degree Standard Observer', **kwargs):
    """
    Plots the blue sky.

    Parameters
    ----------
    cmfs : unicode, optional
        Standard observer colour matching functions.

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

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

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

    canvas(**kwargs)

    cmfs, name = get_cmfs(cmfs), cmfs

    ASTM_G_173_spd = ASTM_G_173_ETR.copy()
    rayleigh_spd = rayleigh_scattering_spd()
    ASTM_G_173_spd.align(rayleigh_spd.shape)

    spd = rayleigh_spd * ASTM_G_173_spd

    matplotlib.pyplot.subplots_adjust(hspace=0.4)

    matplotlib.pyplot.figure(1)
    matplotlib.pyplot.subplot(211)

    settings = {
        'title': 'The Blue Sky - Synthetic Spectral Power Distribution',
        'y_label': u'W / m-2 / nm-1',
        'standalone': False
    }
    settings.update(kwargs)

    single_spd_plot(spd, name, **settings)

    matplotlib.pyplot.subplot(212)

    settings = {
        'title':
            'The Blue Sky - Colour',
        'x_label': ('The sky is blue because molecules in the atmosphere '
                    'scatter shorter wavelengths more than longer ones.\n'
                    'The synthetic spectral power distribution is computed as '
                    'follows: '
                    '(ASTM G-173 ETR * Standard Air Rayleigh Scattering).'),
        'y_label':
            '',
        'aspect':
            None,
        'standalone':
            False
    }

    blue_sky_color = XYZ_to_sRGB(spectral_to_XYZ(spd))
    single_colour_swatch_plot(
        ColourSwatch('', normalise_maximum(blue_sky_color)), **settings)

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

    return render(**settings)
Пример #26
0
def multi_spd_plot(spds,
                   cmfs='CIE 1931 2 Degree Standard Observer',
                   use_spds_colours=False,
                   normalise_spds_colours=False,
                   **kwargs):
    """
    Plots given spectral power distributions.

    Parameters
    ----------
    spds : list
        Spectral power distributions to plot.
    cmfs : unicode, optional
        Standard observer colour matching functions used for spectrum creation.
    use_spds_colours : bool, optional
        Use spectral power distributions colours.
    normalise_spds_colours : bool
        Should spectral power distributions colours normalised.
    \**kwargs : dict, optional
        Keywords arguments.

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

    Examples
    --------
    >>> from colour import SpectralPowerDistribution
    >>> data1 = {400: 0.0641, 420: 0.0645, 440: 0.0562}
    >>> data2 = {400: 0.134, 420: 0.789, 440: 1.289}
    >>> spd1 = SpectralPowerDistribution('Custom1', data1)
    >>> spd2 = SpectralPowerDistribution('Custom2', data2)
    >>> multi_spd_plot([spd1, spd2])  # doctest: +SKIP
    """

    canvas(**kwargs)

    cmfs = get_cmfs(cmfs)

    if use_spds_colours:
        illuminant = ILLUMINANTS_RELATIVE_SPDS.get('D65')

    x_limit_min, x_limit_max, y_limit_min, y_limit_max = [], [], [], []
    for spd in spds:
        wavelengths, values = tuple(zip(*spd.items))

        shape = spd.shape
        x_limit_min.append(shape.start)
        x_limit_max.append(shape.end)
        y_limit_min.append(min(values))
        y_limit_max.append(max(values))

        if use_spds_colours:
            XYZ = spectral_to_XYZ(spd, cmfs, illuminant) / 100
            if normalise_spds_colours:
                XYZ = normalise_maximum(XYZ, clip=False)
            RGB = np.clip(XYZ_to_sRGB(XYZ), 0, 1)

            pylab.plot(wavelengths,
                       values,
                       color=RGB,
                       label=spd.title,
                       linewidth=2)
        else:
            pylab.plot(wavelengths, values, label=spd.title, linewidth=2)

    settings = {
        'x_label':
        'Wavelength $\\lambda$ (nm)',
        'y_label':
        'Spectral Power Distribution',
        'x_tighten':
        True,
        'legend':
        True,
        'legend_location':
        'upper left',
        'limits': (min(x_limit_min), max(x_limit_max), min(y_limit_min),
                   max(y_limit_max))
    }
    settings.update(kwargs)

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

    return display(**settings)
Пример #27
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
Пример #28
0
def blackbody_spectral_radiance_plot(
        temperature=3500,
        cmfs='CIE 1931 2 Degree Standard Observer',
        blackbody='VY Canis Major',
        **kwargs):
    """
    Plots given blackbody spectral radiance.

    Parameters
    ----------
    temperature : numeric, optional
        Blackbody temperature.
    cmfs : unicode, optional
        Standard observer colour matching functions.
    blackbody : unicode, optional
        Blackbody name.
    \**kwargs : dict, optional
        Keywords arguments.

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

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

    canvas(**kwargs)

    cmfs = get_cmfs(cmfs)

    matplotlib.pyplot.subplots_adjust(hspace=0.4)

    spd = blackbody_spd(temperature, cmfs.shape)

    matplotlib.pyplot.figure(1)
    matplotlib.pyplot.subplot(211)

    settings = {
        'title': '{0} - Spectral Radiance'.format(blackbody),
        'y_label': 'W / (sr m$^2$) / m',
        'standalone': False
    }
    settings.update(kwargs)

    single_spd_plot(spd, cmfs.name, **settings)

    XYZ = spectral_to_XYZ(spd, cmfs)
    RGB = normalise_maximum(XYZ_to_sRGB(XYZ / 100))

    matplotlib.pyplot.subplot(212)

    settings = {
        'title': '{0} - Colour'.format(blackbody),
        'x_label': '{0}K'.format(temperature),
        'y_label': '',
        'aspect': None,
        'standalone': False
    }

    single_colour_plot(ColourParameter(name='', RGB=RGB), **settings)

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

    boundaries(**settings)
    decorate(**settings)
    return display(**settings)
Пример #29
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
Пример #30
0
def planckian_table(uv, cmfs, start, end, count):
    """
    Returns a planckian table from given *CIE UCS* colourspace *uv*
    chromaticity coordinates, colour matching functions and temperature range
    using *Ohno (2013)* method.

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

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

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

    ux, vx = uv

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

    shape = cmfs.shape

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

    return table
Пример #31
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
Пример #32
0
def colour_quality_scale(spd_test, additional_data=False):
    """
    Returns the *colour quality scale* 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.get('F2')
    >>> colour_quality_scale(spd)  # doctest: +ELLIPSIS
    64.6781117...
    """

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

    shape = cmfs.shape

    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()])
    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
Пример #33
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
Пример #34
0
def tcs_colorimetry_data(spd_t,
                         spd_r,
                         spds_tcs,
                         cmfs,
                         chromatic_adaptation=False):
    """
    Returns the *test colour samples* colorimetry data.

    Parameters
    ----------
    spd_t : SpectralPowerDistribution
        Test spectral power distribution.
    spd_r : SpectralPowerDistribution
        Reference spectral power distribution.
    spds_tcs : dict
        *Test colour samples* spectral power 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 = spectral_to_XYZ(spd_t, cmfs)
    uv_t = UCS_to_uv(XYZ_to_UCS(XYZ_t))
    u_t, v_t = uv_t[0], uv_t[1]

    XYZ_r = spectral_to_XYZ(spd_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()):
        spd_tcs = spds_tcs[value]
        XYZ_tcs = spectral_to_XYZ(spd_tcs, cmfs, spd_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 * 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(spd_tcs.name, XYZ_tcs, uv_tcs,
                                np.array([U_tcs, V_tcs, W_tcs])))

    return tcs_data
Пример #35
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])
Пример #36
0
def multi_spd_plot(spds,
                   cmfs='CIE 1931 2 Degree Standard Observer',
                   use_spds_colours=False,
                   normalise_spds_colours=False,
                   **kwargs):
    """
    Plots given spectral power distributions.

    Parameters
    ----------
    spds : list, optional
        Spectral power distributions to plot.
    cmfs : unicode, optional
        Standard observer colour matching functions used for spectrum creation.
    use_spds_colours : bool, optional
        Use spectral power distributions colours.
    normalise_spds_colours : bool
        Should spectral power distributions colours normalised.
    \*\*kwargs : \*\*
        Keywords arguments.

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

    Examples
    --------
    >>> from colour import SpectralPowerDistribution
    >>> data1 = {400: 0.0641, 420: 0.0645, 440: 0.0562}
    >>> data2 = {400: 0.134, 420: 0.789, 440: 1.289}
    >>> spd1 = SpectralPowerDistribution('Custom1', data1)
    >>> spd2 = SpectralPowerDistribution('Custom2', data2)
    >>> multi_spd_plot([spd1, spd2])  # doctest: +SKIP
    True
    """

    cmfs, name = get_cmfs(cmfs), cmfs

    if use_spds_colours:
        illuminant = ILLUMINANTS_RELATIVE_SPDS.get('D65')

    x_limit_min, x_limit_max, y_limit_min, y_limit_max = [], [], [], []
    for spd in spds:
        wavelengths, values = tuple(zip(*[(key, value) for key, value in spd]))

        shape = spd.shape
        x_limit_min.append(shape.start)
        x_limit_max.append(shape.end)
        y_limit_min.append(min(values))
        y_limit_max.append(max(values))

        matplotlib.pyplot.rc("axes", color_cycle=["r", "g", "b", "y"])

        if use_spds_colours:
            XYZ = spectral_to_XYZ(spd, cmfs, illuminant) / 100
            if normalise_spds_colours:
                XYZ = normalise(XYZ, clip=False)
            RGB = np.clip(XYZ_to_sRGB(XYZ), 0, 1)

            pylab.plot(wavelengths,
                       values,
                       color=RGB,
                       label=spd.name,
                       linewidth=2)
        else:
            pylab.plot(wavelengths, values, label=spd.name, linewidth=2)

    settings = {
        'x_label':
        u'Wavelength λ (nm)',
        'y_label':
        'Spectral Power Distribution',
        'x_tighten':
        True,
        'legend':
        True,
        'legend_location':
        'upper left',
        'x_ticker':
        True,
        'y_ticker':
        True,
        'limits': [
            min(x_limit_min),
            max(x_limit_max),
            min(y_limit_min),
            max(y_limit_max)
        ]
    }
    settings.update(kwargs)

    bounding_box(**settings)
    aspect(**settings)

    return display(**settings)
Пример #37
0
def spds_chromaticity_diagram_plot_CIE1931(
        spds,
        cmfs='CIE 1931 2 Degree Standard Observer',
        annotate=True,
        chromaticity_diagram_callable_CIE1931=(
            chromaticity_diagram_plot_CIE1931),
        **kwargs):
    """
    Plots given spectral power distribution chromaticity coordinates into the
    *CIE 1931 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.
    chromaticity_diagram_callable_CIE1931 : callable, optional
        Callable responsible for drawing the *CIE 1931 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_CIE1931`},
        Whether to display the chromaticity diagram background colours.
    use_cached_diagram_colours : bool, optional
        {:func:`colour.plotting.chromaticity_diagram_plot_CIE1931`},
        Whether to used the cached chromaticity diagram background colours
        image.

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

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

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

    chromaticity_diagram_callable_CIE1931(**settings)

    for spd in spds:
        XYZ = spectral_to_XYZ(spd) / 100
        xy = XYZ_to_xy(XYZ)

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

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

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

    return render(**settings)
Пример #38
0
def colour_rendering_index(spd_test, additional_data=False):
    """
    Returns the *colour rendering index* :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.

    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
    spd_test = spd_test.clone().align(shape)

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

    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
Пример #39
0
def spds_CIE_1931_chromaticity_diagram_plot(
        spds,
        cmfs='CIE 1931 2 Degree Standard Observer',
        annotate=True,
        **kwargs):
    """
    Plots given spectral power distribution chromaticity coordinates into the
    *CIE 1931 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
    -------
    Figure
        Current figure or None.

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

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

    CIE_1931_chromaticity_diagram_plot(cmfs=cmfs, **settings)

    for spd in spds:
        XYZ = spectral_to_XYZ(spd) / 100
        xy = XYZ_to_xy(XYZ)

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

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

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

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

    return display(**settings)
Пример #40
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
Пример #41
0
def spds_CIE_1931_chromaticity_diagram_plot(
        spds,
        cmfs='CIE 1931 2 Degree Standard Observer',
        annotate=True,
        **kwargs):
    """
    Plots given spectral power distribution chromaticity coordinates into the
    *CIE 1931 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_1931_chromaticity_diagram_plot([A, D65])  # doctest: +SKIP
    True
    """

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

    CIE_1931_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
        xy = XYZ_to_xy(XYZ)

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

        if spd.name is not None and annotate:
            pylab.annotate(spd.name,
                           xy=xy,
                           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.9, -0.1, 0.9),
        'standalone': True})
    settings.update(kwargs)

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

    return display(**settings)
Пример #42
0
def spds_CIE_1976_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 1976 UCS Chromaticity Diagram*.

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

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

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

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

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

    CIE_1976_UCS_chromaticity_diagram_plot(cmfs=cmfs, **settings)

    for spd in spds:
        XYZ = spectral_to_XYZ(spd) / 100
        uv = Luv_to_uv(XYZ_to_Luv(XYZ))

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

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

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

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

    return display(**settings)
Пример #43
0
def blackbody_colours_plot(shape=SpectralShape(150, 12500, 50),
                           cmfs='CIE 1931 2 Degree Standard Observer',
                           **kwargs):
    """
    Plots blackbody colours.

    Parameters
    ----------
    shape : SpectralShape, optional
        Spectral shape to use as plot boundaries.
    cmfs : unicode, optional
        Standard observer colour matching functions.

    Other Parameters
    ----------------
    \**kwargs : dict, optional
        {:func:`boundaries`, :func:`canvas`, :func:`decorate`,
        :func:`display`},
        Please refer to the documentation of the previously listed definitions.
    y0_plot : bool, optional
        {:func:`colour_parameters_plot`},
        Whether to plot *y0* line.
    y1_plot : bool, optional
        {:func:`colour_parameters_plot`},
        Whether to plot *y1* line.

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

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

    cmfs = get_cmfs(cmfs)

    colours = []
    temperatures = []

    for temperature in shape:
        spd = blackbody_spd(temperature, cmfs.shape)

        XYZ = spectral_to_XYZ(spd, cmfs)
        RGB = normalise_maximum(XYZ_to_sRGB(XYZ / 100))

        colours.append(RGB)
        temperatures.append(temperature)

    settings = {
        'title': 'Blackbody Colours',
        'x_label': 'Temperature K',
        'y_label': '',
        'x_tighten': True,
        'y_tighten': True,
        'y_ticker': False
    }
    settings.update(kwargs)

    return colour_parameters_plot([
        ColourParameter(x=x[0], RGB=x[1])
        for x in tuple(zip(temperatures, colours))
    ], **settings)