Source code for colour.quality.tm3018

# -*- coding: utf-8 -*-
"""
ANSI/IES TM-30-18 Colour Fidelity Index
=======================================

Defines the *ANSI/IES TM-30-18 Colour Fidelity Index* (CFI) computation
objects:

- :class:`colour.quality.ColourQuality_Specification_ANSIIESTM3018`
- :func:`colour.quality.colour_fidelity_index_ANSIIESTM3018`

References
----------
-   :cite:`ANSI2018` : ANSI, & IES Color Committee. (2018). ANSI/IES TM-30-18 -
    IES Method for Evaluating Light Source Color Rendition.
    ISBN:978-0-87995-379-9
"""

import numpy as np
from collections import namedtuple

from colour.quality import colour_fidelity_index_CIE2017
from colour.quality.cfi2017 import delta_E_to_R_f
from colour.utilities import as_float_array, as_int


[docs]class ColourQuality_Specification_ANSIIESTM3018( namedtuple( 'ColourQuality_Specification_ANSIIESTM3018', ('name', 'sd_test', 'sd_reference', 'R_f', 'R_s', 'CCT', 'D_uv', 'colorimetry_data', 'R_g', 'bins', 'averages_test', 'averages_reference', 'average_norms', 'R_fs', 'R_cs', 'R_hs'))): """ Defines the *ANSI/IES TM-30-18 Colour Fidelity Index* (CFI) colour quality specification. Parameters ---------- name : unicode Name of the test spectral distribution. sd_test : SpectralDistribution Spectral distribution of the tested illuminant. sd_reference : SpectralDistribution Spectral distribution of the reference illuminant. R_f : numeric *Colour Fidelity Index* (CFI) :math:`R_f`. R_s : list Individual *colour fidelity indexes* data for each sample. CCT : numeric Correlated colour temperature :math:`T_{cp}`. D_uv : numeric Distance from the Planckian locus :math:`\\Delta_{uv}`. colorimetry_data : tuple Colorimetry data for the test and reference computations. bins : list of list of int List of 16 lists, each containing the indexes of colour samples that lie in the respective hue bin. averages_test : ndarray, (16, 2) Averages of *CAM02-UCS* a', b' coordinates for each hue bin for test samples. averages_reference : ndarray, (16, 2) Averages for reference samples. average_norms : ndarray, (16,) Distance of averages for reference samples from the origin. R_fs : ndarray, (16,) Local colour fidelities for each hue bin. R_cs : ndarray, (16,) Local chromaticity shifts for each hue bin, in percents. R_hs : ndarray, (16,) Local hue shifts for each hue bin. """
[docs]def colour_fidelity_index_ANSIIESTM3018(sd_test, additional_data=False): """ Returns the *ANSI/IES TM-30-18 Colour Fidelity Index* (CFI) :math:`R_f` of given spectral distribution. Parameters ---------- sd_test : SpectralDistribution Test spectral distribution. additional_data : bool, optional Whether to output additional data. Returns ------- numeric or ColourQuality_Specification_ANSIIESTM3018 *ANSI/IES TM-30-18 Colour Fidelity Index* (CFI). References ---------- :cite:`ANSI2018` Examples -------- >>> from colour import SDS_ILLUMINANTS >>> sd = SDS_ILLUMINANTS['FL2'] >>> colour_fidelity_index_ANSIIESTM3018(sd) # doctest: +ELLIPSIS 70.1208254... """ if not additional_data: return colour_fidelity_index_CIE2017(sd_test, False) specification = colour_fidelity_index_CIE2017(sd_test, True) # Setup bins based on where the reference a'b' points are located. bins = [[] for _i in range(16)] for i, sample in enumerate(specification.colorimetry_data[1]): bin_index = as_int(np.floor(sample.CAM.h / 22.5)) bins[bin_index].append(i) # Per-bin a'b' averages. averages_test = np.empty([16, 2]) averages_reference = np.empty([16, 2]) for i in range(16): apbp_s = [ specification.colorimetry_data[0][j].Jpapbp[[1, 2]] for j in bins[i] ] averages_test[i, :] = np.mean(apbp_s, axis=0) apbp_s = [ specification.colorimetry_data[1][j].Jpapbp[[1, 2]] for j in bins[i] ] averages_reference[i, :] = np.mean(apbp_s, axis=0) # Gamut Index. R_g = 100 * ( averages_area(averages_test) / averages_area(averages_reference)) # Local colour fidelity indexes, i.e. 16 CFIs for each bin. bin_delta_E_s = [ np.mean([specification.delta_E_s[bins[i]]]) for i in range(16) ] R_fs = delta_E_to_R_f(as_float_array(bin_delta_E_s)) # Angles bisecting the hue bins. angles = (22.5 * np.arange(16) + 11.25) / 180 * np.pi cosines = np.cos(angles) sines = np.sin(angles) average_norms = np.linalg.norm(averages_reference, axis=1) a_deltas = averages_test[:, 0] - averages_reference[:, 0] b_deltas = averages_test[:, 1] - averages_reference[:, 1] # Local chromaticity shifts, multiplied by 100 to obtain percentages. R_cs = 100 * (a_deltas * cosines + b_deltas * sines) / average_norms # Local hue shifts. R_hs = (-a_deltas * sines + b_deltas * cosines) / average_norms return ColourQuality_Specification_ANSIIESTM3018( specification.name, sd_test, specification.sd_reference, specification.R_f, specification.R_s, specification.CCT, specification.D_uv, specification.colorimetry_data, R_g, bins, averages_test, averages_reference, average_norms, R_fs, R_cs, R_hs)
def averages_area(averages): """ Computes the area of the polygon formed by the hue bin averages. Parameters ---------- averages : array_like, (n, 2) Hue bin averages. Returns ------- float Area of the polygon. """ N = averages.shape[0] triangle_areas = np.empty(N) for i in range(N): u = averages[i, :] v = averages[(i + 1) % N, :] triangle_areas[i] = (u[0] * v[1] - u[1] * v[0]) / 2 return np.sum(triangle_areas)