# -*- coding: utf-8 -*-
"""
Colour Quality Plotting
=======================
Defines the colour quality plotting objects:
- :func:`colour.plotting.single_spd_colour_rendering_index_bars_plot`
- :func:`colour.plotting.multi_spd_colour_rendering_index_bars_plot`
- :func:`colour.plotting.single_spd_colour_quality_scale_bars_plot`
- :func:`colour.plotting.multi_spd_colour_quality_scale_bars_plot`
"""
from __future__ import division
import numpy as np
import pylab
from itertools import cycle
from colour.constants import DEFAULT_FLOAT_DTYPE
from colour.models import XYZ_to_sRGB
from colour.plotting import (DEFAULT_FIGURE_WIDTH, DEFAULT_HATCH_PATTERNS,
canvas, label_rectangles, render)
from colour.quality import (colour_quality_scale, colour_rendering_index)
from colour.quality.cri import TCS_ColorimetryData
from colour.utilities import warning
__author__ = 'Colour Developers'
__copyright__ = 'Copyright (C) 2013-2018 - Colour Developers'
__license__ = 'New BSD License - http://opensource.org/licenses/BSD-3-Clause'
__maintainer__ = 'Colour Developers'
__email__ = 'colour-science@googlegroups.com'
__status__ = 'Production'
__all__ = [
'colour_quality_bars_plot', 'single_spd_colour_rendering_index_bars_plot',
'multi_spd_colour_rendering_index_bars_plot',
'single_spd_colour_quality_scale_bars_plot',
'multi_spd_colour_quality_scale_bars_plot'
]
def colour_quality_bars_plot(specifications,
labels=True,
hatching=None,
hatching_repeat=1,
**kwargs):
"""
Plots the colour quality data of given illuminants or light sources colour
quality specifications.
Parameters
----------
specifications : array_like
Array of illuminants or light sources colour quality specifications.
labels : bool, optional
Add labels above bars.
hatching : bool or None, optional
Use hatching for the bars.
hatching_repeat : int, optional
Hatching pattern repeat.
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 (ILLUMINANTS_RELATIVE_SPDS,
... LIGHT_SOURCES_RELATIVE_SPDS, SpectralShape)
>>> illuminant = ILLUMINANTS_RELATIVE_SPDS['F2']
>>> light_source = LIGHT_SOURCES_RELATIVE_SPDS['Kinoton 75P']
>>> light_source = light_source.copy().align(SpectralShape(360, 830, 1))
>>> cqs_i = colour_quality_scale(illuminant, additional_data=True)
>>> cqs_l = colour_quality_scale(light_source, additional_data=True)
>>> colour_quality_bars_plot([cqs_i, cqs_l]) # doctest: +SKIP
"""
settings = {'figure_size': (DEFAULT_FIGURE_WIDTH, DEFAULT_FIGURE_WIDTH)}
settings.update(kwargs)
canvas(**settings)
bar_width = 0.5
y_ticks_interval = 10
count_s, count_Q_as = len(specifications), 0
patterns = cycle(DEFAULT_HATCH_PATTERNS)
if hatching is None:
hatching = False if count_s == 1 else True
for i, specification in enumerate(specifications):
Q_a, Q_as, colorimetry_data = (specification.Q_a, specification.Q_as,
specification.colorimetry_data)
count_Q_as = len(Q_as)
colours = (
[[1] * 3] +
[np.clip(XYZ_to_sRGB(x.XYZ), 0, 1) for x in colorimetry_data[0]])
x = (i + np.arange(
0, (count_Q_as + 1) * (count_s + 1), (count_s + 1),
dtype=DEFAULT_FLOAT_DTYPE)) * bar_width
y = [s[1].Q_a for s in sorted(Q_as.items(), key=lambda s: s[0])]
y = np.array([Q_a] + list(y))
if np.sign(np.min(y)) < 0:
warning(
('"{0}" spectral distribution has negative "Q_a" value(s), '
'using absolute value(s) '
'for plotting purpose!'.format(specification.name)))
y = np.abs(y)
bars = pylab.bar(
x,
y,
color=colours,
width=bar_width,
hatch=(next(patterns) * hatching_repeat if hatching else None),
label=specification.name)
if labels:
label_rectangles(
bars,
rotation='horizontal' if count_s == 1 else 'vertical',
offset=(0 if count_s == 1 else 3 / 100 * count_s + 65 / 1000,
0.025),
text_size=-5 / 7 * count_s + 12.5)
pylab.axhline(y=100, color='black', linestyle='--')
pylab.xticks(
(np.arange(
0, (count_Q_as + 1) * (count_s + 1), (count_s + 1),
dtype=DEFAULT_FLOAT_DTYPE) * bar_width +
(count_s * bar_width / 2)), ['Qa'] +
['Q{0}'.format(index + 1) for index in range(0, count_Q_as + 1, 1)])
pylab.yticks(range(0, 100 + y_ticks_interval, y_ticks_interval))
settings.update({
'title':
'Colour Quality',
'legend':
hatching,
'x_tighten':
True,
'y_tighten':
True,
'limits': (-bar_width, ((count_Q_as + 1) * (count_s + 1)) / 2, 0, 120),
'aspect':
1 / (120 / (bar_width + len(Q_as) + bar_width * 2))
})
settings.update(kwargs)
return render(**settings)
[docs]def single_spd_colour_rendering_index_bars_plot(spd, **kwargs):
"""
Plots the *Colour Rendering Index* (CRI) of given illuminant or light
source spectral power distribution.
Parameters
----------
spd : SpectralPowerDistribution
Illuminant or light source spectral power distribution to plot the
*Colour Rendering Index* (CRI).
Other Parameters
----------------
\**kwargs : dict, optional
{:func:`colour.plotting.render`},
Please refer to the documentation of the previously listed definition.
labels : bool, optional
{:func:`colour.plotting.quality.colour_quality_bars_plot`},
Add labels above bars.
hatching : bool or None, optional
{:func:`colour.plotting.quality.colour_quality_bars_plot`},
Use hatching for the bars.
hatching_repeat : int, optional
{:func:`colour.plotting.quality.colour_quality_bars_plot`},
Hatching pattern repeat.
Returns
-------
Figure
Current figure or None.
Examples
--------
>>> from colour import ILLUMINANTS_RELATIVE_SPDS
>>> illuminant = ILLUMINANTS_RELATIVE_SPDS['F2']
>>> single_spd_colour_rendering_index_bars_plot(illuminant)
... # doctest: +SKIP
"""
return multi_spd_colour_rendering_index_bars_plot([spd], **kwargs)
[docs]def multi_spd_colour_rendering_index_bars_plot(spds, **kwargs):
"""
Plots the *Colour Rendering Index* (CRI) of given illuminants or light
sources spectral power distributions.
Parameters
----------
spds : array_like
Array of illuminants or light sources spectral power distributions to
plot the *Colour Rendering Index* (CRI).
Other Parameters
----------------
\**kwargs : dict, optional
{:func:`colour.plotting.render`},
Please refer to the documentation of the previously listed definition.
labels : bool, optional
{:func:`colour.plotting.quality.colour_quality_bars_plot`},
Add labels above bars.
hatching : bool or None, optional
{:func:`colour.plotting.quality.colour_quality_bars_plot`},
Use hatching for the bars.
hatching_repeat : int, optional
{:func:`colour.plotting.quality.colour_quality_bars_plot`},
Hatching pattern repeat.
Returns
-------
Figure
Current figure or None.
Examples
--------
>>> from colour import (ILLUMINANTS_RELATIVE_SPDS,
... LIGHT_SOURCES_RELATIVE_SPDS)
>>> illuminant = ILLUMINANTS_RELATIVE_SPDS['F2']
>>> light_source = LIGHT_SOURCES_RELATIVE_SPDS['Kinoton 75P']
>>> multi_spd_colour_rendering_index_bars_plot([illuminant, light_source])
... # doctest: +SKIP
"""
settings = {}
settings.update(kwargs)
settings.update({'standalone': False})
specifications = [
colour_rendering_index(spd, additional_data=True) for spd in spds
]
# *colour rendering index* colorimetry data tristimulus values are
# computed in [0, 100] domain however `colour_quality_bars_plot` expects
# [0, 1] domain. As we want to keep `colour_quality_bars_plot` definition
# agnostic from the colour quality data, we update the test spd
# colorimetry data tristimulus values domain.
for specification in specifications:
colorimetry_data = specification.colorimetry_data
for i, c_d in enumerate(colorimetry_data[0]):
colorimetry_data[0][i] = TCS_ColorimetryData(
c_d.name, c_d.XYZ / 100, c_d.uv, c_d.UVW)
colour_quality_bars_plot(specifications, **settings)
settings = {
'title':
'Colour Rendering Index - {0}'
.format(', '.join([spd.strict_name for spd in spds]))
}
settings.update(kwargs)
return render(with_boundaries=False, **settings)
[docs]def single_spd_colour_quality_scale_bars_plot(spd, **kwargs):
"""
Plots the *Colour Quality Scale* (CQS) of given illuminant or light source
spectral power distribution.
Parameters
----------
spd : SpectralPowerDistribution
Illuminant or light source spectral power distribution to plot the
*Colour Quality Scale* (CQS).
Other Parameters
----------------
\**kwargs : dict, optional
{:func:`colour.plotting.render`},
Please refer to the documentation of the previously listed definition.
labels : bool, optional
{:func:`colour.plotting.quality.colour_quality_bars_plot`},
Add labels above bars.
hatching : bool or None, optional
{:func:`colour.plotting.quality.colour_quality_bars_plot`},
Use hatching for the bars.
hatching_repeat : int, optional
{:func:`colour.plotting.quality.colour_quality_bars_plot`},
Hatching pattern repeat.
Returns
-------
Figure
Current figure or None.
Examples
--------
>>> from colour import ILLUMINANTS_RELATIVE_SPDS
>>> illuminant = ILLUMINANTS_RELATIVE_SPDS['F2']
>>> single_spd_colour_quality_scale_bars_plot(illuminant)
... # doctest: +SKIP
"""
return multi_spd_colour_quality_scale_bars_plot([spd], **kwargs)
[docs]def multi_spd_colour_quality_scale_bars_plot(spds, **kwargs):
"""
Plots the *Colour Quality Scale* (CQS) of given illuminants or light
sources spectral power distributions.
Parameters
----------
spds : array_like
Array of illuminants or light sources spectral power distributions to
plot the *Colour Quality Scale* (CQS).
Other Parameters
----------------
\**kwargs : dict, optional
{:func:`colour.plotting.render`},
Please refer to the documentation of the previously listed definition.
labels : bool, optional
{:func:`colour.plotting.quality.colour_quality_bars_plot`},
Add labels above bars.
hatching : bool or None, optional
{:func:`colour.plotting.quality.colour_quality_bars_plot`},
Use hatching for the bars.
hatching_repeat : int, optional
{:func:`colour.plotting.quality.colour_quality_bars_plot`},
Hatching pattern repeat.
Returns
-------
Figure
Current figure or None.
Examples
--------
>>> from colour import (ILLUMINANTS_RELATIVE_SPDS,
... LIGHT_SOURCES_RELATIVE_SPDS)
>>> illuminant = ILLUMINANTS_RELATIVE_SPDS['F2']
>>> light_source = LIGHT_SOURCES_RELATIVE_SPDS['Kinoton 75P']
>>> multi_spd_colour_quality_scale_bars_plot([illuminant, light_source])
... # doctest: +SKIP
"""
settings = {}
settings.update(kwargs)
settings.update({'standalone': False})
specifications = [
colour_quality_scale(spd, additional_data=True) for spd in spds
]
colour_quality_bars_plot(specifications, **settings)
settings = {
'title':
'Colour Quality Scale - {0}'
.format(', '.join([spd.strict_name for spd in spds]))
}
settings.update(kwargs)
return render(with_boundaries=False, **settings)