Source code for colour.quality.cri

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

"""
Colour Rendering Index
======================

Defines *colour rendering index* computation objects:

-   :func:`colour_rendering_index`

See Also
--------
`Colour Rendering Index IPython Notebook
<http://nbviewer.ipython.org/github/colour-science/colour-ipython/blob/master/notebooks/quality/cri.ipynb>`_  # noqa

References
----------
.. [1]  http://cie2.nist.gov/TC1-69/NIST%20CQS%20simulation%207.4.xls
        (Last accessed 10 June 2014)
"""

from __future__ import division, unicode_literals

import numpy as np
from collections import namedtuple

from colour.colorimetry import STANDARD_OBSERVERS_CMFS
from colour.colorimetry import (
    D_illuminant_relative_spd,
    blackbody_spd,
    spectral_to_XYZ)
from colour.quality.dataset.tcs import TCS_SPDS, TCS_INDEXES_TO_NAMES
from colour.models import UCS_to_uv, XYZ_to_UCS, XYZ_to_xyY
from colour.temperature import CCT_to_xy_illuminant_D, uv_to_CCT_robertson1968

__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__ = ['TSC_COLORIMETRY_DATA_NXYZUVUVW',
           'colour_rendering_index']

TSC_COLORIMETRY_DATA_NXYZUVUVW = namedtuple('TscColorimetryData_nXYZuvUVW',
                                            ('name', 'XYZ', 'uv', 'UVW'))


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


def _colour_rendering_indexes(test_data, reference_data):
    """
    Returns the *test colour samples* rendering indexes.

    Parameters
    ----------
    test_data : list
        Test data.
    reference_data : list
        Reference data.

    Returns
    -------
    dict
        *Test colour samples* colour rendering indexes.
    """

    colour_rendering_indexes = {}
    for i in range(len(test_data)):
        colour_rendering_indexes[i + 1] = 100 - 4.6 * np.linalg.norm(
            reference_data[i].UVW - test_data[i].UVW)
    return colour_rendering_indexes


[docs]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