From 0b9738200eaed86463e9fb3839af1e95ec0ef8be Mon Sep 17 00:00:00 2001 From: Tucker Downs Date: Sun, 29 Oct 2023 00:34:40 -0400 Subject: [PATCH 1/4] Add is_within_visible_xy implementation --- colour/volume/spectrum.py | 99 ++++++++++++++++++++++++++-- colour/volume/tests/test_spectrum.py | 11 +++- 2 files changed, 104 insertions(+), 6 deletions(-) diff --git a/colour/volume/spectrum.py b/colour/volume/spectrum.py index 2e4ac4cd0..d7067a325 100644 --- a/colour/volume/spectrum.py +++ b/colour/volume/spectrum.py @@ -23,7 +23,6 @@ from __future__ import annotations import numpy as np - from colour.colorimetry import ( MultiSpectralDistributions, SpectralDistribution, @@ -44,6 +43,7 @@ validate_method, zeros, ) +from colour.models.cie_xyy import XYZ_to_xy from colour.volume import is_within_mesh_volume __author__ = "Colour Developers" @@ -393,14 +393,20 @@ def is_within_visible_spectrum( **kwargs: Any, ) -> NDArrayFloat: """ - Return whether given *CIE XYZ* tristimulus values are within the visible - spectrum volume, i.e. *Rösch-MacAdam* colour solid, for given colour - matching functions and illuminant. + Return whether given *CIE XYZ* tristimulus values or *CIE xy* chromaticity + values are within the visible spectrum volume, i.e. *Rösch-MacAdam* colour + solid, for given colour matching functions and illuminant. + + For the brightness invariant *CIE xy* check, the limit is determined by the + spectral locus and "line of purples" depending on the precision of the + spectral arguments. Parameters ---------- XYZ - *CIE XYZ* tristimulus values. + *CIE XYZ* or xy tristimulus values. xy chromaticity mode will be + selected if the size of the last dimension is 2. i.e. if XYZ.shape[-1] + == 2 cmfs Standard observer colour matching functions, default to the *CIE 1931 2 Degree Standard Observer*. @@ -439,6 +445,16 @@ def is_within_visible_spectrum( array([ True, False], dtype=bool) """ + XYZ = np.asarray(XYZ) + if XYZ.shape[-1] == 2: + return _is_within_visible_xy( + XYZ, + cmfs=cmfs, + illuminant=illuminant, + tolerance=tolerance, + **kwargs, + ) + cmfs, illuminant = handle_spectral_arguments( cmfs, illuminant, @@ -456,3 +472,76 @@ def is_within_visible_spectrum( ) return is_within_mesh_volume(XYZ, vertices, tolerance) + + +def _is_within_visible_xy( + xy: ArrayLike, + cmfs: MultiSpectralDistributions | None = None, + illuminant: SpectralDistribution | None = None, + tolerance: float = 100 * EPSILON, + **kwargs: Any, +) -> NDArrayFloat: + """ + Return whether given *CIE XYZ* tristimulus values are within the visible + spectrum volume, i.e. *Rösch-MacAdam* colour solid, for given colour + matching functions and illuminant. + + Parameters + ---------- + XYZ + *CIE XYZ* tristimulus values. + cmfs + Standard observer colour matching functions, default to the + *CIE 1931 2 Degree Standard Observer*. + illuminant + Illuminant spectral distribution, default to *CIE Illuminant E*. + tolerance + Tolerance allowed in the inside-triangle check. + + Other Parameters + ---------------- + kwargs + {:func:`colour.msds_to_XYZ`}, + See the documentation of the previously listed definition. + + Returns + ------- + :class:`numpy.ndarray` + Are *CIE XYZ* tristimulus values within the visible spectrum volume, + i.e. *Rösch-MacAdam* colour solid. + + Notes + ----- + +------------+-----------------------+---------------+ + | **Domain** | **Scale - Reference** | **Scale - 1** | + +============+=======================+===============+ + | ``XYZ`` | [0, 1] | [0, 1] | + +------------+-----------------------+---------------+ + + Examples + -------- + >>> import numpy as np + >>> is_within_visible_spectrum(np.array([0.33,0.33])) + array(True, dtype=bool) + >>> a = np.array([[.33,.33], [.1,.1]]) + >>> is_within_visible_spectrum(a) + array([ True, False], dtype=bool) + """ + + cmfs, illuminant = handle_spectral_arguments( + cmfs, + illuminant, + "CIE 1931 2 Degree Standard Observer", + "E", + SPECTRAL_SHAPE_OUTER_SURFACE_XYZ, + ) + + key = (hash(cmfs), hash(illuminant), str(kwargs)) + vertices = _CACHE_OUTER_SURFACE_XYZ_POINTS.get(key) + + if vertices is None: + _CACHE_OUTER_SURFACE_XYZ_POINTS[key] = vertices = XYZ_to_xy( + cmfs.values + ) + + return is_within_mesh_volume(xy, vertices, tolerance) diff --git a/colour/volume/tests/test_spectrum.py b/colour/volume/tests/test_spectrum.py index fa6db6529..95d4681d6 100644 --- a/colour/volume/tests/test_spectrum.py +++ b/colour/volume/tests/test_spectrum.py @@ -5,7 +5,6 @@ from itertools import product import numpy as np - from colour.colorimetry import ( MSDS_CMFS, SPECTRAL_SHAPE_DEFAULT, @@ -197,6 +196,16 @@ class TestIsWithinVisibleSpectrum(unittest.TestCase): definition unit tests methods. """ + def test_is_within_visible_spectrum_xy(self): + """Test :func:`colour.volume.spectrum.is_within_visible_spectrum` + detection and use of xy chromaticity. + """ + + samples = [[0.1, 0.1], [0.3, 0.3]] + results = is_within_visible_spectrum(samples) + + np.testing.assert_array_equal(results, (False, True)) + def test_is_within_visible_spectrum(self): """ Test :func:`colour.volume.spectrum.is_within_visible_spectrum` From d82c02f59794b9e18bbf3fd297fff0067c89df83 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Sun, 29 Oct 2023 04:36:24 +0000 Subject: [PATCH 2/4] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- colour/volume/spectrum.py | 5 +++-- colour/volume/tests/test_spectrum.py | 1 + 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/colour/volume/spectrum.py b/colour/volume/spectrum.py index d7067a325..ee2e89291 100644 --- a/colour/volume/spectrum.py +++ b/colour/volume/spectrum.py @@ -23,6 +23,7 @@ from __future__ import annotations import numpy as np + from colour.colorimetry import ( MultiSpectralDistributions, SpectralDistribution, @@ -521,9 +522,9 @@ def _is_within_visible_xy( Examples -------- >>> import numpy as np - >>> is_within_visible_spectrum(np.array([0.33,0.33])) + >>> is_within_visible_spectrum(np.array([0.33, 0.33])) array(True, dtype=bool) - >>> a = np.array([[.33,.33], [.1,.1]]) + >>> a = np.array([[0.33, 0.33], [0.1, 0.1]]) >>> is_within_visible_spectrum(a) array([ True, False], dtype=bool) """ diff --git a/colour/volume/tests/test_spectrum.py b/colour/volume/tests/test_spectrum.py index 95d4681d6..4bc388672 100644 --- a/colour/volume/tests/test_spectrum.py +++ b/colour/volume/tests/test_spectrum.py @@ -5,6 +5,7 @@ from itertools import product import numpy as np + from colour.colorimetry import ( MSDS_CMFS, SPECTRAL_SHAPE_DEFAULT, From ceba79e4526af168a684282ae8381804c3cf05a2 Mon Sep 17 00:00:00 2001 From: Tucker Downs Date: Thu, 4 Jan 2024 19:03:43 -0700 Subject: [PATCH 3/4] isort --- colour/volume/spectrum.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/colour/volume/spectrum.py b/colour/volume/spectrum.py index ee2e89291..c0aa77f6a 100644 --- a/colour/volume/spectrum.py +++ b/colour/volume/spectrum.py @@ -23,7 +23,6 @@ from __future__ import annotations import numpy as np - from colour.colorimetry import ( MultiSpectralDistributions, SpectralDistribution, @@ -38,13 +37,13 @@ Literal, NDArrayFloat, ) +from colour.models.cie_xyy import XYZ_to_xy from colour.utilities import ( CACHE_REGISTRY, is_caching_enabled, validate_method, zeros, ) -from colour.models.cie_xyy import XYZ_to_xy from colour.volume import is_within_mesh_volume __author__ = "Colour Developers" From b7fb49cb02ab3d113a576e3107c557bf1760e6da Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Fri, 5 Jan 2024 02:04:41 +0000 Subject: [PATCH 4/4] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- colour/volume/spectrum.py | 1 + 1 file changed, 1 insertion(+) diff --git a/colour/volume/spectrum.py b/colour/volume/spectrum.py index c0aa77f6a..a96906757 100644 --- a/colour/volume/spectrum.py +++ b/colour/volume/spectrum.py @@ -23,6 +23,7 @@ from __future__ import annotations import numpy as np + from colour.colorimetry import ( MultiSpectralDistributions, SpectralDistribution,