Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions colour/colorimetry/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@
SD_TO_XYZ_METHODS,
SPECTRAL_SHAPE_ASTME308,
adjust_tristimulus_weighting_factors_ASTME308,
get_tristimulus_weighting_factors_integration,
handle_spectral_arguments,
lagrange_coefficients_ASTME2022,
msds_to_XYZ,
Expand Down Expand Up @@ -223,6 +224,7 @@
"sd_to_XYZ_ASTME308",
"sd_to_XYZ_integration",
"sd_to_XYZ_tristimulus_weighting_factors_ASTME308",
"get_tristimulus_weighting_factors_integration",
"tristimulus_weighting_factors_ASTME2022",
"wavelength_to_XYZ",
]
Expand Down
107 changes: 100 additions & 7 deletions colour/colorimetry/tristimulus_values.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,10 @@
- :cite:`ASTMInternational2015b` : ASTM International. (2015). ASTM E308-15 -
Standard Practice for Computing the Colors of Objects by Using the CIE
System (pp. 1-47). doi:10.1520/E0308-15
- :cite:`InternationalOrganizationforStandardization2024` : International
Organization for Standardization. (2024). INTERNATIONAL STANDARD ISO
18314-4 - Analytical colorimetry Part 4: Metamerism index for pairs of
samples for change of illuminant. https://www.iso.org/standard/85116.html
- :cite:`Wyszecki2000bf` : Wyszecki, Günther, & Stiles, W. S. (2000).
Integration Replaced by Summation. In Color Science: Concepts and Methods,
Quantitative Data and Formulae (pp. 158-163). Wiley. ISBN:978-0-471-39918-6
Expand Down Expand Up @@ -94,6 +98,7 @@
"lagrange_coefficients_ASTME2022",
"tristimulus_weighting_factors_ASTME2022",
"adjust_tristimulus_weighting_factors_ASTME308",
"get_tristimulus_weighting_factors_integration",
"sd_to_XYZ_integration",
"sd_to_XYZ_tristimulus_weighting_factors_ASTME308",
"sd_to_XYZ_ASTME308",
Expand Down Expand Up @@ -544,6 +549,99 @@ def adjust_tristimulus_weighting_factors_ASTME308(
return W[start_index : -end_index or None, ...]


def get_tristimulus_weighting_factors_integration(
cmfs: MultiSpectralDistributions,
illuminant: SpectralDistribution,
shape: SpectralShape,
k: Real | None = None,
) -> NDArrayFloat:
"""
Return the tristimulus weighting factors computation method for the
integration method.

Parameters
----------
cmfs
Standard observer colour matching functions.
illuminant
Illuminant spectral distribution.
shape
Shape used to build the table, only the interval is needed.
k
Normalisation constant :math:`k`. For reflecting or transmitting
object colours, :math:`k` is chosen so that :math:`Y = 100` for
objects for which the spectral reflectance factor
:math:`R(\\lambda)` of the object colour or the spectral
transmittance factor :math:`\\tau(\\lambda)` of the object is equal
to unity for all wavelengths. For self-luminous objects and
illuminants, the constants :math:`k` is usually chosen on the
grounds of convenience. If, however, in the CIE 1931 standard
colorimetric system, the :math:`Y` value is required to be
numerically equal to the absolute value of a photometric quantity,
the constant, :math:`k`, must be put equal to the numerical value
of :math:`K_m`, the maximum spectral luminous efficacy (which is
equal to 683 :math:`lm\\cdot W^{-1}`) and
:math:`\\Phi_\\lambda(\\lambda)` must be the spectral concentration
of the radiometric quantity corresponding to the photometric
quantity required.

Returns
-------
:class:`numpy.ndarray`
Tristimulus weighting factors table.

References
----------
:cite:`InternationalOrganizationforStandardization2024`

Examples
--------
>>> from colour import MSDS_CMFS, SDS_ILLUMINANTS
>>> cmfs = MSDS_CMFS["CIE 1931 2 Degree Standard Observer"]
>>> illuminant = SDS_ILLUMINANTS["D65"]
>>> shape = SpectralShape(400, 700, 20)
>>> get_tristimulus_weighting_factors_integration(cmfs, illuminant, shape)
... # doctest: +ELLIPSIS
array([[ 2.23634421e-01, 6.18862548e-03, 1.06034924e+00],
[ 2.37101690e+00, 7.05764816e-02, 1.13910441e+01],
[ 6.89706619e+00, 4.55474108e-01, 3.45974172e+01],
[ 6.46977575e+00, 1.33489183e+00, 3.71366908e+01],
[ 2.09370011e+00, 3.04335204e+00, 1.77966720e+01],
[ 1.01189640e-01, 6.67025585e+00, 5.61705756e+00],
[ 1.25205375e+00, 1.40502317e+01, 1.54849365e+00],
[ 5.72562903e+00, 1.88094012e+01, 4.00241975e-01],
[ 1.12268302e+01, 1.87900691e+01, 7.36495171e-02],
[ 1.65750211e+01, 1.57374968e+01, 2.98469948e-02],
[ 1.80544399e+01, 1.07252416e+01, 1.35977706e-02],
[ 1.41509324e+01, 6.30991383e+00, 3.14667619e-03],
[ 7.07958281e+00, 2.76607946e+00, 3.16123367e-04],
[ 2.49792489e+00, 9.24035282e-01, 0.00000000e+00],
[ 6.91427716e-01, 2.51320744e-01, 0.00000000e+00],
[ 1.53610085e-01, 5.54714053e-02, 0.00000000e+00]])
"""

if cmfs.shape != shape:
runtime_warning(f'Aligning "{cmfs.name}" cmfs shape to "{shape}".')
cmfs = reshape_msds(cmfs, shape, copy=False)

if illuminant.shape != shape:
runtime_warning(f'Aligning "{illuminant.name}" illuminant shape to "{shape}".')
illuminant = reshape_sd(illuminant, shape, copy=False)

XYZ_b = cmfs.values
S = illuminant.values

d_w = cmfs.shape.interval

# normalisation constant k from Y = 100 of perfect diffuser
with sdiv_mode():
k = cast("Real", optional(k, sdiv(100, (np.sum(XYZ_b[..., 1] * S) * d_w))))

# --- weights matrix A (DIN EN ISO 18314-4, eq. 7-8) ---
# A[i, :] = k * S(λ_i) * [x̄(λ_i), ȳ(λ_i), z̄(λ_i)] * Δλ
return k * S[..., np.newaxis] * XYZ_b * d_w # shape: (n, 3)


def sd_to_XYZ_integration(
sd: ArrayLike | SpectralDistribution | MultiSpectralDistributions,
cmfs: MultiSpectralDistributions | None = None,
Expand Down Expand Up @@ -724,16 +822,11 @@ def sd_to_XYZ_integration(
runtime_warning(f'Aligning "{illuminant.name}" illuminant shape to "{shape}".')
illuminant = reshape_sd(illuminant, shape, copy=False)

XYZ_b = cmfs.values
S = illuminant.values
R = np.reshape(R, (-1, wl_c_r))

d_w = cmfs.shape.interval

with sdiv_mode():
k = cast("Real", optional(k, sdiv(100, (np.sum(XYZ_b[..., 1] * S) * d_w))))
A = get_tristimulus_weighting_factors_integration(cmfs, illuminant, shape, k)

XYZ = k * np.dot(R * S, XYZ_b) * d_w
XYZ = np.dot(R, A)

XYZ = from_range_100(np.reshape(XYZ, [*list(shape_R[:-1]), 3]))

Expand Down
7 changes: 6 additions & 1 deletion colour/difference/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,11 @@
)
from .din99 import delta_E_DIN99
from .huang2015 import power_function_Huang2015
from .metamerism_index import Lab_to_metamerism_index, XYZ_to_metamerism_index
from .metamerism_index import (
Lab_to_metamerism_index,
XYZ_to_metamerism_index,
sd_to_metamerism_index,
)
from .stress import INDEX_STRESS_METHODS, index_stress, index_stress_Garcia2007

__all__ = [
Expand Down Expand Up @@ -103,6 +107,7 @@
__all__ += [
"Lab_to_metamerism_index",
"XYZ_to_metamerism_index",
"sd_to_metamerism_index",
]

DELTA_E_METHODS: CanonicalMapping = CanonicalMapping(
Expand Down
Loading
Loading