Source code for colour.plotting.diagrams

#!/usr/bin/env python
# -*- coding: utf-8 -*-

"""
CIE Chromaticity Diagrams Plotting
==================================

Defines the *CIE* chromaticity diagrams plotting objects:

-   :func:`CIE_1931_chromaticity_diagram_plot`
-   :func:`CIE_1960_UCS_chromaticity_diagram_plot`
-   :func:`CIE_1976_UCS_chromaticity_diagram_plot`
"""

from __future__ import division

import bisect
import os

import matplotlib
import matplotlib.image
import matplotlib.path
import numpy as np
import pylab

from colour.algebra import normalise
from colour.colorimetry import ILLUMINANTS
from colour.models import (
    UCS_uv_to_xy,
    XYZ_to_UCS,
    XYZ_to_xy,
    UCS_to_uv,
    xy_to_XYZ,
    XYZ_to_Luv,
    Luv_to_uv,
    Luv_uv_to_xy,
    XYZ_to_sRGB)
from colour.plotting import (
    PLOTTING_RESOURCES_DIRECTORY,
    aspect,
    bounding_box,
    display,
    figure_size,
    get_cmfs)

__author__ = 'Colour Developers'
__copyright__ = 'Copyright (C) 2013 - 2014 - Colour Developers'
__license__ = 'New BSD License - http://opensource.org/licenses/BSD-3-Clause'
__maintainer__ = 'Colour Developers'
__email__ = 'colour-science@googlegroups.com'
__status__ = 'Production'

__all__ = ['CIE_1931_chromaticity_diagram_colours_plot',
           'CIE_1931_chromaticity_diagram_plot',
           'CIE_1960_UCS_chromaticity_diagram_colours_plot',
           'CIE_1960_UCS_chromaticity_diagram_plot',
           'CIE_1976_UCS_chromaticity_diagram_colours_plot',
           'CIE_1976_UCS_chromaticity_diagram_plot']


@figure_size((32, 32))
[docs]def CIE_1931_chromaticity_diagram_colours_plot( surface=1.25, spacing=0.00075, cmfs='CIE 1931 2 Degree Standard Observer', **kwargs): """ Plots the *CIE 1931 Chromaticity Diagram* colours. Parameters ---------- surface : numeric, optional Generated markers surface. spacing : numeric, optional Spacing between markers. cmfs : unicode, optional Standard observer colour matching functions used for diagram bounds. \*\*kwargs : \*\* Keywords arguments. Returns ------- bool Definition success. Examples -------- >>> CIE_1931_chromaticity_diagram_colours_plot() # doctest: +SKIP True """ cmfs, name = get_cmfs(cmfs), cmfs illuminant = ILLUMINANTS.get( 'CIE 1931 2 Degree Standard Observer').get('E') XYZs = [value for key, value in cmfs] x, y = tuple(zip(*([XYZ_to_xy(x) for x in XYZs]))) path = matplotlib.path.Path(tuple(zip(x, y))) x_dot, y_dot, colours = [], [], [] for i in np.arange(0, 1, spacing): for j in np.arange(0, 1, spacing): if path.contains_path(matplotlib.path.Path([[i, j], [i, j]])): x_dot.append(i) y_dot.append(j) XYZ = xy_to_XYZ((i, j)) RGB = normalise(XYZ_to_sRGB(XYZ, illuminant)) colours.append(RGB) pylab.scatter(x_dot, y_dot, color=colours, s=surface) settings = {'no_ticks': True, 'bounding_box': [0, 1, 0, 1], 'bbox_inches': 'tight', 'pad_inches': 0} settings.update(kwargs) bounding_box(**settings) aspect(**settings) return display(**settings)
@figure_size((8, 8))
[docs]def CIE_1931_chromaticity_diagram_plot( cmfs='CIE 1931 2 Degree Standard Observer', **kwargs): """ Plots the *CIE 1931 Chromaticity Diagram*. Parameters ---------- cmfs : unicode, optional Standard observer colour matching functions used for diagram bounds. \*\*kwargs : \*\* Keywords arguments. Returns ------- bool Definition success. Examples -------- >>> CIE_1931_chromaticity_diagram_plot() # doctest: +SKIP True """ cmfs, name = get_cmfs(cmfs), cmfs image = matplotlib.image.imread( os.path.join(PLOTTING_RESOURCES_DIRECTORY, 'CIE_1931_Chromaticity_Diagram_{0}_Small.png'.format( cmfs.name.replace(' ', '_')))) pylab.imshow(image, interpolation='nearest', extent=(0, 1, 0, 1)) labels = ( [390, 460, 470, 480, 490, 500, 510, 520, 540, 560, 580, 600, 620, 700]) wavelengths = cmfs.wavelengths equal_energy = np.array([1 / 3] * 2) XYZs = [value for key, value in cmfs] x, y = tuple(zip(*([XYZ_to_xy(x) for x in XYZs]))) wavelengths_chromaticity_coordinates = dict( tuple(zip(wavelengths, tuple(zip(x, y))))) pylab.plot(x, y, color='black', linewidth=2) pylab.plot((x[-1], x[0]), (y[-1], y[0]), color='black', linewidth=2) for label in labels: x, y = wavelengths_chromaticity_coordinates.get(label) pylab.plot(x, y, 'o', color='black', linewidth=2) index = bisect.bisect(wavelengths, label) left = wavelengths[index - 1] if index >= 0 else wavelengths[index] right = (wavelengths[index] if index < len(wavelengths) else wavelengths[-1]) dx = (wavelengths_chromaticity_coordinates.get(right)[0] - wavelengths_chromaticity_coordinates.get(left)[0]) dy = (wavelengths_chromaticity_coordinates.get(right)[1] - wavelengths_chromaticity_coordinates.get(left)[1]) norme = lambda x: x / np.linalg.norm(x) xy = np.array([x, y]) direction = np.array((-dy, dx)) normal = (np.array((-dy, dx)) if np.dot(norme(xy - equal_energy), norme(direction)) > 0 else np.array((dy, -dx))) normal = norme(normal) normal /= 25 pylab.plot([x, x + normal[0] * 0.75], [y, y + normal[1] * 0.75], color='black', linewidth=1.5) pylab.text(x + normal[0], y + normal[1], label, clip_on=True, ha='left' if normal[0] >= 0 else 'right', va='center', fontdict={'size': 'small'}) settings = { 'title': 'CIE 1931 Chromaticity Diagram - {0}'.format(name), 'x_label': 'CIE x', 'y_label': 'CIE y', 'x_ticker': True, 'y_ticker': True, 'grid': True, 'bounding_box': [-0.1, 0.9, -0.1, 0.9], 'bbox_inches': 'tight', 'pad_inches': 0} settings.update(kwargs) bounding_box(**settings) aspect(**settings) return display(**settings)
@figure_size((32, 32))
[docs]def CIE_1960_UCS_chromaticity_diagram_colours_plot( surface=1.25, spacing=0.00075, cmfs='CIE 1931 2 Degree Standard Observer', **kwargs): """ Plots the *CIE 1960 UCS Chromaticity Diagram* colours. Parameters ---------- surface : numeric, optional Generated markers surface. spacing : numeric, optional Spacing between markers. cmfs : unicode, optional Standard observer colour matching functions used for diagram bounds. \*\*kwargs : \*\* Keywords arguments. Returns ------- bool Definition success. Examples -------- >>> CIE_1960_UCS_chromaticity_diagram_colours_plot() # doctest: +SKIP True """ cmfs, name = get_cmfs(cmfs), cmfs illuminant = ILLUMINANTS.get( 'CIE 1931 2 Degree Standard Observer').get('E') UVWs = [XYZ_to_UCS(value) for key, value in cmfs] u, v = tuple(zip(*([UCS_to_uv(x) for x in UVWs]))) path = matplotlib.path.Path(tuple(zip(u, v))) x_dot, y_dot, colours = [], [], [] for i in np.arange(0, 1, spacing): for j in np.arange(0, 1, spacing): if path.contains_path(matplotlib.path.Path([[i, j], [i, j]])): x_dot.append(i) y_dot.append(j) XYZ = xy_to_XYZ(UCS_uv_to_xy((i, j))) RGB = normalise(XYZ_to_sRGB(XYZ, illuminant)) colours.append(RGB) pylab.scatter(x_dot, y_dot, color=colours, s=surface) settings = {'no_ticks': True, 'bounding_box': [0, 1, 0, 1], 'bbox_inches': 'tight', 'pad_inches': 0} settings.update(kwargs) bounding_box(**settings) aspect(**settings) return display(**settings)
@figure_size((8, 8))
[docs]def CIE_1960_UCS_chromaticity_diagram_plot( cmfs='CIE 1931 2 Degree Standard Observer', **kwargs): """ Plots the *CIE 1960 UCS Chromaticity Diagram*. Parameters ---------- cmfs : unicode, optional Standard observer colour matching functions used for diagram bounds. \*\*kwargs : \*\* Keywords arguments. Returns ------- bool Definition success. Examples -------- >>> CIE_1960_UCS_chromaticity_diagram_plot() # doctest: +SKIP True """ cmfs, name = get_cmfs(cmfs), cmfs image = matplotlib.image.imread( os.path.join(PLOTTING_RESOURCES_DIRECTORY, 'CIE_1960_UCS_Chromaticity_Diagram_{0}_Small.png'.format( cmfs.name.replace(' ', '_')))) pylab.imshow(image, interpolation='nearest', extent=(0, 1, 0, 1)) labels = [420, 430, 440, 450, 460, 470, 480, 490, 500, 510, 520, 530, 540, 550, 560, 570, 580, 590, 600, 610, 620, 630, 640, 680] wavelengths = cmfs.wavelengths equal_energy = np.array([1 / 3] * 2) UVWs = [XYZ_to_UCS(value) for key, value in cmfs] u, v = tuple(zip(*([UCS_to_uv(x) for x in UVWs]))) wavelengths_chromaticity_coordinates = dict( tuple(zip(wavelengths, tuple(zip(u, v))))) pylab.plot(u, v, color='black', linewidth=2) pylab.plot((u[-1], u[0]), (v[-1], v[0]), color='black', linewidth=2) for label in labels: u, v = wavelengths_chromaticity_coordinates.get(label) pylab.plot(u, v, 'o', color='black', linewidth=2) index = bisect.bisect(wavelengths, label) left = wavelengths[index - 1] if index >= 0 else wavelengths[index] right = (wavelengths[index] if index < len(wavelengths) else wavelengths[-1]) dx = (wavelengths_chromaticity_coordinates.get(right)[0] - wavelengths_chromaticity_coordinates.get(left)[0]) dy = (wavelengths_chromaticity_coordinates.get(right)[1] - wavelengths_chromaticity_coordinates.get(left)[1]) norme = lambda x: x / np.linalg.norm(x) uv = np.array([u, v]) direction = np.array((-dy, dx)) normal = (np.array((-dy, dx)) if np.dot(norme(uv - equal_energy), norme(direction)) > 0 else np.array((dy, -dx))) normal = norme(normal) normal /= 25 pylab.plot([u, u + normal[0] * 0.75], [v, v + normal[1] * 0.75], color='black', linewidth=1.5) pylab.text(u + normal[0], v + normal[1], label, clip_on=True, ha='left' if normal[0] >= 0 else 'right', va='center', fontdict={'size': 'small'}) settings = { 'title': 'CIE 1960 UCS Chromaticity Diagram - {0}'.format(name), 'x_label': 'CIE u', 'y_label': 'CIE v', 'x_ticker': True, 'y_ticker': True, 'grid': True, 'bounding_box': [-0.075, 0.675, -0.15, 0.6], 'bbox_inches': 'tight', 'pad_inches': 0} settings.update(kwargs) bounding_box(**settings) aspect(**settings) return display(**settings)
@figure_size((32, 32))
[docs]def CIE_1976_UCS_chromaticity_diagram_colours_plot( surface=1.25, spacing=0.00075, cmfs='CIE 1931 2 Degree Standard Observer', **kwargs): """ Plots the *CIE 1976 UCS Chromaticity Diagram* colours. Parameters ---------- surface : numeric, optional Generated markers surface. spacing : numeric, optional Spacing between markers. cmfs : unicode, optional Standard observer colour matching functions used for diagram bounds. \*\*kwargs : \*\* Keywords arguments. Returns ------- bool Definition success. Examples -------- >>> CIE_1976_UCS_chromaticity_diagram_colours_plot() # doctest: +SKIP True """ cmfs, name = get_cmfs(cmfs), cmfs illuminant = ILLUMINANTS.get( 'CIE 1931 2 Degree Standard Observer').get('D50') Luvs = [XYZ_to_Luv(value, illuminant) for key, value in cmfs] u, v = tuple(zip(*([Luv_to_uv(x) for x in Luvs]))) path = matplotlib.path.Path(tuple(zip(u, v))) x_dot, y_dot, colours = [], [], [] for i in np.arange(0, 1, spacing): for j in np.arange(0, 1, spacing): if path.contains_path(matplotlib.path.Path([[i, j], [i, j]])): x_dot.append(i) y_dot.append(j) XYZ = xy_to_XYZ(Luv_uv_to_xy((i, j))) RGB = normalise(XYZ_to_sRGB(XYZ, illuminant)) colours.append(RGB) pylab.scatter(x_dot, y_dot, color=colours, s=surface) settings = {'no_ticks': True, 'bounding_box': [0, 1, 0, 1], 'bbox_inches': 'tight', 'pad_inches': 0} settings.update(kwargs) bounding_box(**settings) aspect(**settings) return display(**settings)
@figure_size((8, 8))
[docs]def CIE_1976_UCS_chromaticity_diagram_plot( cmfs='CIE 1931 2 Degree Standard Observer', **kwargs): """ Plots the *CIE 1976 UCS Chromaticity Diagram*. Parameters ---------- cmfs : unicode, optional Standard observer colour matching functions used for diagram bounds. \*\*kwargs : \*\* Keywords arguments. Returns ------- bool Definition success. Examples -------- >>> CIE_1976_UCS_chromaticity_diagram_plot() # doctest: +SKIP True """ cmfs, name = get_cmfs(cmfs), cmfs image = matplotlib.image.imread( os.path.join(PLOTTING_RESOURCES_DIRECTORY, 'CIE_1976_UCS_Chromaticity_Diagram_{0}_Small.png'.format( cmfs.name.replace(' ', '_')))) pylab.imshow(image, interpolation='nearest', extent=(0, 1, 0, 1)) labels = [420, 430, 440, 450, 460, 470, 480, 490, 500, 510, 520, 530, 540, 550, 560, 570, 580, 590, 600, 610, 620, 630, 640, 680] wavelengths = cmfs.wavelengths equal_energy = np.array([1 / 3] * 2) illuminant = ILLUMINANTS.get( 'CIE 1931 2 Degree Standard Observer').get('D50') Luvs = [XYZ_to_Luv(value, illuminant) for key, value in cmfs] u, v = tuple(zip(*([Luv_to_uv(x) for x in Luvs]))) wavelengths_chromaticity_coordinates = dict(zip(wavelengths, tuple(zip(u, v)))) pylab.plot(u, v, color='black', linewidth=2) pylab.plot((u[-1], u[0]), (v[-1], v[0]), color='black', linewidth=2) for label in labels: u, v = wavelengths_chromaticity_coordinates.get(label) pylab.plot(u, v, 'o', color='black', linewidth=2) index = bisect.bisect(wavelengths, label) left = wavelengths[index - 1] if index >= 0 else wavelengths[index] right = (wavelengths[index] if index < len(wavelengths) else wavelengths[-1]) dx = (wavelengths_chromaticity_coordinates.get(right)[0] - wavelengths_chromaticity_coordinates.get(left)[0]) dy = (wavelengths_chromaticity_coordinates.get(right)[1] - wavelengths_chromaticity_coordinates.get(left)[1]) norme = lambda x: x / np.linalg.norm(x) uv = np.array([u, v]) direction = np.array((-dy, dx)) normal = (np.array((-dy, dx)) if np.dot(norme(uv - equal_energy), norme(direction)) > 0 else np.array((dy, -dx))) normal = norme(normal) normal /= 25 pylab.plot([u, u + normal[0] * 0.75], [v, v + normal[1] * 0.75], color='black', linewidth=1.5) pylab.text(u + normal[0], v + normal[1], label, clip_on=True, ha='left' if normal[0] >= 0 else 'right', va='center', fontdict={'size': 'small'}) settings = { 'title': 'CIE 1976 UCS Chromaticity Diagram - {0}'.format(name), 'x_label': 'CIE u"', 'y_label': 'CIE v"', 'x_ticker': True, 'y_ticker': True, 'grid': True, 'bounding_box': [-0.1, .7, -.1, .7], 'bbox_inches': 'tight', 'pad_inches': 0} settings.update(kwargs) bounding_box(**settings) aspect(**settings) return display(**settings)