diff --git a/colour/characterisation/aces_it.py b/colour/characterisation/aces_it.py index 3eead7d299..4139ff166b 100644 --- a/colour/characterisation/aces_it.py +++ b/colour/characterisation/aces_it.py @@ -83,10 +83,10 @@ from colour.io import read_sds_from_csv_file from colour.models import XYZ_to_Jzazbz, XYZ_to_Lab, XYZ_to_xy, xy_to_XYZ from colour.models.rgb import ( + RGB_Colourspace, RGB_COLOURSPACE_ACES2065_1, RGB_to_XYZ, XYZ_to_RGB, - normalised_primary_matrix, ) from colour.temperature import CCT_to_xy_CIE_D from colour.utilities import ( @@ -259,23 +259,18 @@ def k(x: NDArrayFloat, y: NDArrayFloat) -> DTypeFloat: E_rgb *= S_FLARE_FACTOR if chromatic_adaptation_transform is not None: - xy = XYZ_to_xy(sd_to_XYZ(illuminant) / 100) - NPM = normalised_primary_matrix( - RGB_COLOURSPACE_ACES2065_1.primaries, xy - ) XYZ = RGB_to_XYZ( E_rgb, - xy, + RGB_Colourspace( + "~ACES2065-1", + RGB_COLOURSPACE_ACES2065_1.primaries, + XYZ_to_xy(sd_to_XYZ(illuminant) / 100), + illuminant.name, + ), RGB_COLOURSPACE_ACES2065_1.whitepoint, - NPM, chromatic_adaptation_transform, ) - E_rgb = XYZ_to_RGB( - XYZ, - RGB_COLOURSPACE_ACES2065_1.whitepoint, - RGB_COLOURSPACE_ACES2065_1.whitepoint, - RGB_COLOURSPACE_ACES2065_1.matrix_XYZ_to_RGB, - ) + E_rgb = XYZ_to_RGB(XYZ, RGB_COLOURSPACE_ACES2065_1) return from_range_1(E_rgb) @@ -864,9 +859,10 @@ def matrix_idt( | str | None = "CAT02", additional_data: bool = False, -) -> Tuple[NDArrayFloat, NDArrayFloat, NDArrayFloat, NDArrayFloat] | Tuple[ - NDArrayFloat, NDArrayFloat -]: +) -> ( + Tuple[NDArrayFloat, NDArrayFloat, NDArrayFloat, NDArrayFloat] + | Tuple[NDArrayFloat, NDArrayFloat] +): """ Compute an *Input Device Transform* (IDT) matrix for given camera *RGB* spectral sensitivities, illuminant, training data, standard observer colour diff --git a/colour/examples/characterisation/examples_colour_checkers.py b/colour/examples/characterisation/examples_colour_checkers.py index fc4f154a09..6fe927bf6b 100644 --- a/colour/examples/characterisation/examples_colour_checkers.py +++ b/colour/examples/characterisation/examples_colour_checkers.py @@ -36,11 +36,10 @@ for name, xyY in data.items(): RGB = colour.XYZ_to_RGB( colour.xyY_to_XYZ(xyY), + colour.RGB_COLOURSPACES["sRGB"], illuminant, - colour.CCS_ILLUMINANTS["CIE 1931 2 Degree Standard Observer"]["D65"], - colour.RGB_COLOURSPACES["sRGB"].matrix_XYZ_to_RGB, "Bradford", - colour.RGB_COLOURSPACES["sRGB"].cctf_encoding, + apply_cctf_encoding=True, ) RGB_i = [int(round(x * 255)) if x >= 0 else 0 for x in np.ravel(RGB)] diff --git a/colour/examples/models/examples_models.py b/colour/examples/models/examples_models.py index d0a95e8667..031e618b88 100644 --- a/colour/examples/models/examples_models.py +++ b/colour/examples/models/examples_models.py @@ -67,12 +67,7 @@ D65 = colour.CCS_ILLUMINANTS["CIE 1931 2 Degree Standard Observer"]["D65"] print( colour.XYZ_to_RGB( - XYZ, - D65, - colour.RGB_COLOURSPACES["sRGB"].whitepoint, - colour.RGB_COLOURSPACES["sRGB"].matrix_XYZ_to_RGB, - "Bradford", - colour.RGB_COLOURSPACES["sRGB"].cctf_encoding, + XYZ, colour.RGB_COLOURSPACES["sRGB"], D65, "Bradford", True ) ) @@ -85,12 +80,7 @@ ) print( colour.RGB_to_XYZ( - RGB, - colour.RGB_COLOURSPACES["sRGB"].whitepoint, - D65, - colour.RGB_COLOURSPACES["sRGB"].matrix_RGB_to_XYZ, - "Bradford", - colour.RGB_COLOURSPACES["sRGB"].cctf_decoding, + RGB, colour.RGB_COLOURSPACES["sRGB"], D65, "Bradford", True ) ) diff --git a/colour/graph/conversion.py b/colour/graph/conversion.py index 3d785442e3..d5c08d5207 100644 --- a/colour/graph/conversion.py +++ b/colour/graph/conversion.py @@ -756,22 +756,12 @@ def mired_to_CCT_D_uv(mired: ArrayLike) -> NDArrayFloat: ( "CIE XYZ", "RGB", - partial( - XYZ_to_RGB, - illuminant_XYZ=_RGB_COLOURSPACE_DEFAULT.whitepoint, - illuminant_RGB=_RGB_COLOURSPACE_DEFAULT.whitepoint, - matrix_XYZ_to_RGB=_RGB_COLOURSPACE_DEFAULT.matrix_XYZ_to_RGB, - ), + partial(XYZ_to_RGB, colourspace=_RGB_COLOURSPACE_DEFAULT), ), ( "RGB", "CIE XYZ", - partial( - RGB_to_XYZ, - illuminant_RGB=_RGB_COLOURSPACE_DEFAULT.whitepoint, - illuminant_XYZ=_RGB_COLOURSPACE_DEFAULT.whitepoint, - matrix_RGB_to_XYZ=_RGB_COLOURSPACE_DEFAULT.matrix_RGB_to_XYZ, - ), + partial(RGB_to_XYZ, colourspace=_RGB_COLOURSPACE_DEFAULT), ), ( "RGB", diff --git a/colour/models/rgb/common.py b/colour/models/rgb/common.py index de4a10e454..9c18bea83c 100644 --- a/colour/models/rgb/common.py +++ b/colour/models/rgb/common.py @@ -89,15 +89,12 @@ def XYZ_to_sRGB( array([ 0.7057393..., 0.1924826..., 0.2235416...]) """ - sRGB = RGB_COLOURSPACES["sRGB"] - return XYZ_to_RGB( XYZ, + RGB_COLOURSPACES["sRGB"], illuminant, - sRGB.whitepoint, - sRGB.matrix_XYZ_to_RGB, chromatic_adaptation_transform, - sRGB.cctf_encoding if apply_cctf_encoding else None, + apply_cctf_encoding, ) @@ -166,13 +163,10 @@ def sRGB_to_XYZ( array([ 0.2065429..., 0.1219794..., 0.0513714...]) """ - sRGB = RGB_COLOURSPACES["sRGB"] - return RGB_to_XYZ( RGB, - sRGB.whitepoint, + RGB_COLOURSPACES["sRGB"], illuminant, - sRGB.matrix_RGB_to_XYZ, chromatic_adaptation_transform, - sRGB.cctf_decoding if apply_cctf_decoding else None, + apply_cctf_decoding, ) diff --git a/colour/models/rgb/ictcp.py b/colour/models/rgb/ictcp.py index 4e86ec486a..146399e888 100644 --- a/colour/models/rgb/ictcp.py +++ b/colour/models/rgb/ictcp.py @@ -519,13 +519,10 @@ def XYZ_to_ICtCp( array([ 0.5924279..., -0.0374073..., 0.2512267...]) """ - BT2020 = RGB_COLOURSPACES["ITU-R BT.2020"] - RGB = XYZ_to_RGB( XYZ, + RGB_COLOURSPACES["ITU-R BT.2020"], illuminant, - BT2020.whitepoint, - BT2020.matrix_XYZ_to_RGB, chromatic_adaptation_transform, ) @@ -655,13 +652,10 @@ def ICtCp_to_XYZ( RGB = ICtCp_to_RGB(ICtCp, method, L_p) - BT2020 = RGB_COLOURSPACES["ITU-R BT.2020"] - XYZ = RGB_to_XYZ( RGB, - BT2020.whitepoint, + RGB_COLOURSPACES["ITU-R BT.2020"], illuminant, - BT2020.matrix_RGB_to_XYZ, chromatic_adaptation_transform, ) diff --git a/colour/models/rgb/rgb_colourspace.py b/colour/models/rgb/rgb_colourspace.py index f122adaa11..7a0d8f1c74 100644 --- a/colour/models/rgb/rgb_colourspace.py +++ b/colour/models/rgb/rgb_colourspace.py @@ -54,6 +54,8 @@ optional, to_domain_1, is_string, + usage_warning, + validate_method, ) __author__ = "Colour Developers" @@ -941,9 +943,8 @@ def copy(self) -> RGB_Colourspace: def XYZ_to_RGB( XYZ: ArrayLike, - illuminant_XYZ: ArrayLike, - illuminant_RGB: ArrayLike, - matrix_XYZ_to_RGB: ArrayLike, + colourspace: RGB_Colourspace | str, + illuminant: ArrayLike | None = None, chromatic_adaptation_transform: Literal[ "Bianco 2010", "Bianco PC 2010", @@ -960,7 +961,9 @@ def XYZ_to_RGB( ] | str | None = "CAT02", - cctf_encoding: Callable | None = None, + apply_cctf_encoding: bool = False, + *args: Any, + **kwargs: Any, ) -> NDArrayFloat: """ Convert from *CIE XYZ* tristimulus values to *RGB* colourspace array. @@ -969,21 +972,24 @@ def XYZ_to_RGB( ---------- XYZ *CIE XYZ* tristimulus values. - illuminant_XYZ + colourspace + Output *RGB* colourspace. + illuminant *CIE xy* chromaticity coordinates or *CIE xyY* colourspace array of the *illuminant* for the input *CIE XYZ* tristimulus values. - illuminant_RGB - *CIE xy* chromaticity coordinates or *CIE xyY* colourspace array of the - *illuminant* for the output *RGB* colourspace array. - matrix_XYZ_to_RGB - Matrix converting the *CIE XYZ* tristimulus values to *RGB* colourspace - array, i.e. the inverse *Normalised Primary Matrix* (NPM). chromatic_adaptation_transform *Chromatic adaptation* transform, if *None* no chromatic adaptation is performed. - cctf_encoding - Encoding colour component transfer function (Encoding CCTF) or - opto-electronic transfer function (OETF). + apply_cctf_encoding + Apply the *RGB* colourspace encoding colour component transfer + function / opto-electronic transfer function. + + Other Parameters + ---------------- + args + Arguments for deprecation management. + kwargs + Keywords arguments for deprecation management. Returns ------- @@ -1010,41 +1016,73 @@ def XYZ_to_RGB( Examples -------- + >>> from colour.models import RGB_COLOURSPACE_sRGB >>> XYZ = np.array([0.21638819, 0.12570000, 0.03847493]) - >>> illuminant_XYZ = np.array([0.34570, 0.35850]) - >>> illuminant_RGB = np.array([0.31270, 0.32900]) - >>> chromatic_adaptation_transform = "Bradford" - >>> matrix_XYZ_to_RGB = np.array( - ... [ - ... [3.24062548, -1.53720797, -0.49862860], - ... [-0.96893071, 1.87575606, 0.04151752], - ... [0.05571012, -0.20402105, 1.05699594], - ... ] - ... ) - >>> XYZ_to_RGB( - ... XYZ, - ... illuminant_XYZ, - ... illuminant_RGB, - ... matrix_XYZ_to_RGB, - ... chromatic_adaptation_transform, - ... ) # doctest: +ELLIPSIS - array([ 0.4559557..., 0.0303970..., 0.0408724...]) + >>> illuminant = np.array([0.34570, 0.35850]) + >>> XYZ_to_RGB(XYZ, RGB_COLOURSPACE_sRGB, illuminant, "Bradford") + ... # doctest: +ELLIPSIS + array([ 0.4559528..., 0.0304078..., 0.0408731...]) + >>> XYZ_to_RGB(XYZ, "sRGB", illuminant, "Bradford") + ... # doctest: +ELLIPSIS + array([ 0.4559528..., 0.0304078..., 0.0408731...]) """ + from colour.models import RGB_COLOURSPACES + XYZ = to_domain_1(XYZ) + if not isinstance(colourspace, (RGB_Colourspace, str)): + usage_warning( + 'The "colour.XYZ_to_RGB" definition signature has changed with ' + '"Colour 0.4.3". The used call arguments are deprecated, ' + "please refer to the documentation for more information about the " + "new signature." + ) + illuminant_XYZ = kwargs.pop("illuminant_XYZ", colourspace) + illuminant_RGB = kwargs.pop("illuminant_RGB", illuminant) + matrix_XYZ_to_RGB = kwargs.pop( + "matrix_XYZ_to_RGB", chromatic_adaptation_transform + ) + chromatic_adaptation_transform = kwargs.pop( + "chromatic_adaptation_transform", + ( + apply_cctf_encoding + if not isinstance(apply_cctf_encoding, bool) + else "CAT02" + ), + ) + cctf_encoding = kwargs.pop( + "cctf_encoding", args[0] if len(args) == 1 else None + ) + apply_cctf_encoding = True + else: + if isinstance(colourspace, str): + colourspace = validate_method( + colourspace, + RGB_COLOURSPACES, + '"{0}" "RGB" colourspace is invalid, it must be one of {1}!', + ) + colourspace = RGB_COLOURSPACES[colourspace] + + illuminant_XYZ = optional( + illuminant, colourspace.whitepoint # pyright: ignore + ) + illuminant_RGB = colourspace.whitepoint # pyright: ignore + matrix_XYZ_to_RGB = colourspace.matrix_XYZ_to_RGB # pyright: ignore + cctf_encoding = colourspace.cctf_encoding # pyright: ignore + if chromatic_adaptation_transform is not None: M_CAT = matrix_chromatic_adaptation_VonKries( xyY_to_XYZ(xy_to_xyY(illuminant_XYZ)), - xyY_to_XYZ(xy_to_xyY(illuminant_RGB)), + xyY_to_XYZ(xy_to_xyY(illuminant_RGB)), # pyright: ignore transform=chromatic_adaptation_transform, ) XYZ = vector_dot(M_CAT, XYZ) - RGB = vector_dot(matrix_XYZ_to_RGB, XYZ) + RGB = vector_dot(matrix_XYZ_to_RGB, XYZ) # pyright: ignore - if cctf_encoding is not None: + if apply_cctf_encoding and cctf_encoding is not None: with domain_range_scale("ignore"): RGB = cctf_encoding(RGB) @@ -1053,9 +1091,8 @@ def XYZ_to_RGB( def RGB_to_XYZ( RGB: ArrayLike, - illuminant_RGB: ArrayLike, - illuminant_XYZ: ArrayLike, - matrix_RGB_to_XYZ: ArrayLike, + colourspace: RGB_Colourspace | str, + illuminant: ArrayLike | None = None, chromatic_adaptation_transform: Literal[ "Bianco 2010", "Bianco PC 2010", @@ -1072,7 +1109,9 @@ def RGB_to_XYZ( ] | str | None = "CAT02", - cctf_decoding: Callable | None = None, + apply_cctf_decoding: bool = False, + *args, + **kwargs, ) -> NDArrayFloat: """ Convert given *RGB* colourspace array to *CIE XYZ* tristimulus values. @@ -1081,21 +1120,24 @@ def RGB_to_XYZ( ---------- RGB *RGB* colourspace array. - illuminant_RGB - *CIE xy* chromaticity coordinates or *CIE xyY* colourspace array of the - *illuminant* for the input *RGB* colourspace array. - illuminant_XYZ + colourspace + Input *RGB* colourspace. + illuminant *CIE xy* chromaticity coordinates or *CIE xyY* colourspace array of the *illuminant* for the output *CIE XYZ* tristimulus values. - matrix_RGB_to_XYZ - Matrix converting the *RGB* colourspace array to *CIE XYZ* tristimulus - values, i.e. the *Normalised Primary Matrix* (NPM). chromatic_adaptation_transform *Chromatic adaptation* transform, if *None* no chromatic adaptation is performed. - cctf_decoding - Decoding colour component transfer function (Decoding CCTF) or - electro-optical transfer function (EOTF). + apply_cctf_decoding + Apply the *RGB* colourspace decoding colour component transfer + function / opto-electronic transfer function. + + Other Parameters + ---------------- + args + Arguments for deprecation management. + kwargs + Keywords arguments for deprecation management. Returns ------- @@ -1122,39 +1164,70 @@ def RGB_to_XYZ( Examples -------- + >>> from colour.models import RGB_COLOURSPACE_sRGB >>> RGB = np.array([0.45595571, 0.03039702, 0.04087245]) - >>> illuminant_RGB = np.array([0.31270, 0.32900]) - >>> illuminant_XYZ = np.array([0.34570, 0.35850]) - >>> chromatic_adaptation_transform = "Bradford" - >>> matrix_RGB_to_XYZ = np.array( - ... [ - ... [0.41240000, 0.35760000, 0.18050000], - ... [0.21260000, 0.71520000, 0.07220000], - ... [0.01930000, 0.11920000, 0.95050000], - ... ] - ... ) - >>> RGB_to_XYZ( - ... RGB, - ... illuminant_RGB, - ... illuminant_XYZ, - ... matrix_RGB_to_XYZ, - ... chromatic_adaptation_transform, - ... ) # doctest: +ELLIPSIS + >>> illuminant = np.array([0.34570, 0.35850]) + >>> RGB_to_XYZ(RGB, RGB_COLOURSPACE_sRGB, illuminant, "Bradford") + ... # doctest: +ELLIPSIS array([ 0.2163881..., 0.1257 , 0.0384749...]) + >>> RGB_to_XYZ(RGB, "sRGB", illuminant, "Bradford") + ... # doctest: +ELLIPSIS """ + from colour.models import RGB_COLOURSPACES + RGB = to_domain_1(RGB) - if cctf_decoding is not None: + if not isinstance(colourspace, (RGB_Colourspace, str)): + usage_warning( + 'The "colour.RGB_to_XYZ" definition signature has changed with ' + '"Colour 0.4.3". The used call arguments are deprecated, ' + "please refer to the documentation for more information about the " + "new signature." + ) + illuminant_RGB = kwargs.pop("illuminant_RGB", colourspace) + illuminant_XYZ = kwargs.pop("illuminant_XYZ", illuminant) + matrix_RGB_to_XYZ = kwargs.pop( + "matrix_RGB_to_XYZ", chromatic_adaptation_transform + ) + chromatic_adaptation_transform = kwargs.pop( + "chromatic_adaptation_transform", + ( + apply_cctf_decoding + if not isinstance(apply_cctf_decoding, bool) + else "CAT02" + ), + ) + cctf_decoding = kwargs.pop( + "cctf_decoding", args[0] if len(args) == 1 else None + ) + apply_cctf_decoding = True + else: + if isinstance(colourspace, str): + colourspace = validate_method( + colourspace, + RGB_COLOURSPACES, + '"{0}" "RGB" colourspace is invalid, it must be one of {1}!', + ) + colourspace = RGB_COLOURSPACES[colourspace] + + illuminant_XYZ = optional( + illuminant, colourspace.whitepoint # pyright: ignore + ) + illuminant_RGB = colourspace.whitepoint # pyright: ignore + matrix_RGB_to_XYZ = colourspace.matrix_RGB_to_XYZ # pyright: ignore + cctf_decoding = colourspace.cctf_decoding # pyright: ignore + + if apply_cctf_decoding and cctf_decoding is not None: with domain_range_scale("ignore"): RGB = cctf_decoding(RGB) - XYZ = vector_dot(matrix_RGB_to_XYZ, RGB) + XYZ = vector_dot(matrix_RGB_to_XYZ, RGB) # pyright: ignore if chromatic_adaptation_transform is not None: M_CAT = matrix_chromatic_adaptation_VonKries( xyY_to_XYZ(xy_to_xyY(illuminant_RGB)), - xyY_to_XYZ(xy_to_xyY(illuminant_XYZ)), + xyY_to_XYZ(xy_to_xyY(illuminant_XYZ)), # pyright: ignore transform=chromatic_adaptation_transform, ) @@ -1272,11 +1345,11 @@ def RGB_to_RGB( *Chromatic adaptation* transform, if *None* no chromatic adaptation is performed. apply_cctf_decoding - Apply input colourspace decoding colour component transfer function / - electro-optical transfer function. + Apply the input colourspace decoding colour component transfer function + / electro-optical transfer function. apply_cctf_encoding - Apply output colourspace encoding colour component transfer function / - opto-electronic transfer function. + Apply the output colourspace encoding colour component transfer + function / opto-electronic transfer function. Other Parameters ---------------- diff --git a/colour/models/rgb/tests/test_rgb_colourspace.py b/colour/models/rgb/tests/test_rgb_colourspace.py index a6d0cd4795..904261eb0f 100644 --- a/colour/models/rgb/tests/test_rgb_colourspace.py +++ b/colour/models/rgb/tests/test_rgb_colourspace.py @@ -1,5 +1,7 @@ # !/usr/bin/env python -"""Define the unit tests for the :mod:`colour.models.rgb.rgb_colourspace` module.""" +""" +Define the unit tests for the :mod:`colour.models.rgb.rgb_colourspace` module. +""" import numpy as np import re @@ -8,6 +10,8 @@ from itertools import product from colour.models import ( + RGB_COLOURSPACE_ACES2065_1, + RGB_COLOURSPACE_sRGB, RGB_COLOURSPACES, RGB_Colourspace, XYZ_to_RGB, @@ -284,8 +288,62 @@ class TestXYZ_to_RGB(unittest.TestCase): """ def test_XYZ_to_RGB(self): - """Test :func:`colour.models.rgb.rgb_colourspace.XYZ_to_RGB` definition.""" + """ + Test :func:`colour.models.rgb.rgb_colourspace.XYZ_to_RGB` + definition. + """ + + np.testing.assert_array_almost_equal( + XYZ_to_RGB( + np.array([0.21638819, 0.12570000, 0.03847493]), + RGB_COLOURSPACE_sRGB, + np.array([0.34570, 0.35850]), + "Bradford", + True, + ), + np.array([0.70556403, 0.19112904, 0.22341005]), + decimal=7, + ) + + np.testing.assert_array_almost_equal( + XYZ_to_RGB( + np.array([0.21638819, 0.12570000, 0.03847493]), + RGB_COLOURSPACE_sRGB, + apply_cctf_encoding=True, + ), + np.array([0.72794351, 0.18184112, 0.17951801]), + decimal=7, + ) + + np.testing.assert_array_almost_equal( + XYZ_to_RGB( + np.array([0.21638819, 0.12570000, 0.03847493]), + RGB_COLOURSPACE_ACES2065_1, + np.array([0.34570, 0.35850]), + ), + np.array([0.21959099, 0.06985815, 0.04703704]), + decimal=7, + ) + + np.testing.assert_array_almost_equal( + XYZ_to_RGB( + np.array([0.21638819, 0.12570000, 0.03847493]), + "sRGB", + np.array([0.34570, 0.35850]), + "Bradford", + True, + ), + XYZ_to_RGB( + np.array([0.21638819, 0.12570000, 0.03847493]), + RGB_COLOURSPACE_sRGB, + np.array([0.34570, 0.35850]), + "Bradford", + True, + ), + decimal=7, + ) + # TODO: Remove tests when dropping deprecated signature support. np.testing.assert_array_almost_equal( XYZ_to_RGB( np.array([0.21638819, 0.12570000, 0.03847493]), @@ -366,38 +424,28 @@ def test_n_dimensional_XYZ_to_RGB(self): XYZ = np.array([0.21638819, 0.12570000, 0.03847493]) W_R = np.array([0.34570, 0.35850]) - W_T = np.array([0.31270, 0.32900]) - M = np.array( - [ - [3.24062548, -1.53720797, -0.49862860], - [-0.96893071, 1.87575606, 0.04151752], - [0.05571012, -0.20402105, 1.05699594], - ] - ) - RGB = XYZ_to_RGB(XYZ, W_R, W_T, M, "Bradford", eotf_inverse_sRGB) + RGB = XYZ_to_RGB(XYZ, "sRGB", W_R, "Bradford", True) XYZ = np.tile(XYZ, (6, 1)) RGB = np.tile(RGB, (6, 1)) np.testing.assert_array_almost_equal( - XYZ_to_RGB(XYZ, W_R, W_T, M, "Bradford", eotf_inverse_sRGB), + XYZ_to_RGB(XYZ, "sRGB", W_R, "Bradford", True), RGB, decimal=7, ) W_R = np.tile(W_R, (6, 1)) - W_T = np.tile(W_T, (6, 1)) np.testing.assert_array_almost_equal( - XYZ_to_RGB(XYZ, W_R, W_T, M, "Bradford", eotf_inverse_sRGB), + XYZ_to_RGB(XYZ, "sRGB", W_R, "Bradford", True), RGB, decimal=7, ) XYZ = np.reshape(XYZ, (2, 3, 3)) W_R = np.reshape(W_R, (2, 3, 2)) - W_T = np.reshape(W_T, (2, 3, 2)) RGB = np.reshape(RGB, (2, 3, 3)) np.testing.assert_array_almost_equal( - XYZ_to_RGB(XYZ, W_R, W_T, M, "Bradford", eotf_inverse_sRGB), + XYZ_to_RGB(XYZ, "sRGB", W_R, "Bradford", True), RGB, decimal=7, ) @@ -410,21 +458,13 @@ def test_domain_range_scale_XYZ_to_RGB(self): XYZ = np.array([0.21638819, 0.12570000, 0.03847493]) W_R = np.array([0.34570, 0.35850]) - W_T = np.array([0.31270, 0.32900]) - M = np.array( - [ - [3.24062548, -1.53720797, -0.49862860], - [-0.96893071, 1.87575606, 0.04151752], - [0.05571012, -0.20402105, 1.05699594], - ] - ) - RGB = XYZ_to_RGB(XYZ, W_R, W_T, M) + RGB = XYZ_to_RGB(XYZ, "sRGB", W_R) d_r = (("reference", 1), ("1", 1), ("100", 100)) for scale, factor in d_r: with domain_range_scale(scale): np.testing.assert_array_almost_equal( - XYZ_to_RGB(XYZ * factor, W_R, W_T, M), + XYZ_to_RGB(XYZ * factor, "sRGB", W_R), RGB * factor, decimal=7, ) @@ -451,8 +491,62 @@ class TestRGB_to_XYZ(unittest.TestCase): """ def test_RGB_to_XYZ(self): - """Test :func:`colour.models.rgb.rgb_colourspace.RGB_to_XYZ` definition.""" + """ + Test :func:`colour.models.rgb.rgb_colourspace.RGB_to_XYZ` + definition. + """ + + np.testing.assert_array_almost_equal( + RGB_to_XYZ( + np.array([0.70556403, 0.19112904, 0.22341005]), + RGB_COLOURSPACE_sRGB, + np.array([0.34570, 0.35850]), + "Bradford", + True, + ), + np.array([0.21639121, 0.12570714, 0.03847642]), + decimal=7, + ) + + np.testing.assert_array_almost_equal( + RGB_to_XYZ( + np.array([0.72794351, 0.18184112, 0.17951801]), + RGB_COLOURSPACE_sRGB, + apply_cctf_decoding=True, + ), + np.array([0.21639100, 0.12570754, 0.03847682]), + decimal=7, + ) + + np.testing.assert_array_almost_equal( + RGB_to_XYZ( + np.array([0.21959099, 0.06985815, 0.04703704]), + RGB_COLOURSPACE_ACES2065_1, + np.array([0.34570, 0.35850]), + ), + np.array([0.21638819, 0.12570000, 0.03847493]), + decimal=7, + ) + + np.testing.assert_array_almost_equal( + RGB_to_XYZ( + np.array([0.21638819, 0.12570000, 0.03847493]), + "sRGB", + np.array([0.34570, 0.35850]), + "Bradford", + True, + ), + RGB_to_XYZ( + np.array([0.21638819, 0.12570000, 0.03847493]), + RGB_COLOURSPACE_sRGB, + np.array([0.34570, 0.35850]), + "Bradford", + True, + ), + decimal=7, + ) + # TODO: Remove tests when dropping deprecated signature support. np.testing.assert_array_almost_equal( RGB_to_XYZ( np.array([0.70556599, 0.19109268, 0.22340812]), @@ -533,34 +627,24 @@ def test_n_dimensional_RGB_to_XYZ(self): RGB = np.array([0.70556599, 0.19109268, 0.22340812]) W_R = np.array([0.31270, 0.32900]) - W_T = np.array([0.34570, 0.35850]) - M = np.array( - [ - [0.41240000, 0.35760000, 0.18050000], - [0.21260000, 0.71520000, 0.07220000], - [0.01930000, 0.11920000, 0.95050000], - ] - ) - XYZ = RGB_to_XYZ(RGB, W_R, W_T, M, "Bradford", eotf_sRGB) + XYZ = RGB_to_XYZ(RGB, "sRGB", W_R, "Bradford", True) RGB = np.tile(RGB, (6, 1)) XYZ = np.tile(XYZ, (6, 1)) np.testing.assert_array_almost_equal( - RGB_to_XYZ(RGB, W_R, W_T, M, "Bradford", eotf_sRGB), XYZ, decimal=7 + RGB_to_XYZ(RGB, "sRGB", W_R, "Bradford", True), XYZ, decimal=7 ) W_R = np.tile(W_R, (6, 1)) - W_T = np.tile(W_T, (6, 1)) np.testing.assert_array_almost_equal( - RGB_to_XYZ(RGB, W_R, W_T, M, "Bradford", eotf_sRGB), XYZ, decimal=7 + RGB_to_XYZ(RGB, "sRGB", W_R, "Bradford", True), XYZ, decimal=7 ) RGB = np.reshape(RGB, (2, 3, 3)) W_R = np.reshape(W_R, (2, 3, 2)) - W_T = np.reshape(W_T, (2, 3, 2)) XYZ = np.reshape(XYZ, (2, 3, 3)) np.testing.assert_array_almost_equal( - RGB_to_XYZ(RGB, W_R, W_T, M, "Bradford", eotf_sRGB), XYZ, decimal=7 + RGB_to_XYZ(RGB, "sRGB", W_R, "Bradford", True), XYZ, decimal=7 ) def test_domain_range_scale_XYZ_to_RGB(self): @@ -571,21 +655,13 @@ def test_domain_range_scale_XYZ_to_RGB(self): RGB = np.array([0.45620801, 0.03079991, 0.04091883]) W_R = np.array([0.31270, 0.32900]) - W_T = np.array([0.34570, 0.35850]) - M = np.array( - [ - [3.24062548, -1.53720797, -0.49862860], - [-0.96893071, 1.87575606, 0.04151752], - [0.05571012, -0.20402105, 1.05699594], - ] - ) - XYZ = RGB_to_XYZ(RGB, W_R, W_T, M) + XYZ = RGB_to_XYZ(RGB, "sRGB", W_R) d_r = (("reference", 1), ("1", 1), ("100", 100)) for scale, factor in d_r: with domain_range_scale(scale): np.testing.assert_array_almost_equal( - RGB_to_XYZ(RGB * factor, W_R, W_T, M), + RGB_to_XYZ(RGB * factor, "sRGB", W_R), XYZ * factor, decimal=7, ) diff --git a/colour/plotting/common.py b/colour/plotting/common.py index af26bd4d67..6364ab3f81 100644 --- a/colour/plotting/common.py +++ b/colour/plotting/common.py @@ -386,13 +386,10 @@ def XYZ_to_plotting_colourspace( return XYZ_to_RGB( XYZ, + CONSTANTS_COLOUR_STYLE.colour.colourspace, illuminant, - CONSTANTS_COLOUR_STYLE.colour.colourspace.whitepoint, - CONSTANTS_COLOUR_STYLE.colour.colourspace.matrix_XYZ_to_RGB, chromatic_adaptation_transform, - CONSTANTS_COLOUR_STYLE.colour.colourspace.cctf_encoding - if apply_cctf_encoding - else None, + apply_cctf_encoding, ) diff --git a/colour/plotting/models.py b/colour/plotting/models.py index 6d6d4445ac..2cd9b9a95b 100644 --- a/colour/plotting/models.py +++ b/colour/plotting/models.py @@ -1057,12 +1057,7 @@ def plot_RGB_chromaticities_in_chromaticity_diagram( 1, ) - XYZ = RGB_to_XYZ( - RGB, - colourspace.whitepoint, - colourspace.whitepoint, - colourspace.matrix_RGB_to_XYZ, - ) + XYZ = RGB_to_XYZ(RGB, colourspace) if method == "cie 1931": ij = XYZ_to_xy(XYZ, colourspace.whitepoint) @@ -2058,23 +2053,12 @@ def _linear_equation( ) if use_RGB_colours: - - def _XYZ_to_RGB(XYZ: NDArrayFloat) -> NDArrayFloat: - """ - Convert given *CIE XYZ* tristimulus values to - ``colour.plotting`` *RGB* colourspace. - """ - - return XYZ_to_RGB( - XYZ, - xy_r, # noqa: B023 - colourspace.whitepoint, - colourspace.matrix_XYZ_to_RGB, - cctf_encoding=colourspace.cctf_encoding, - ) - - RGB_ct = _XYZ_to_RGB(XYZ_ct) - RGB_cr = _XYZ_to_RGB(XYZ_cr) + RGB_ct = XYZ_to_RGB( + XYZ_ct, colourspace, xy_r, apply_cctf_encoding=True + ) + RGB_cr = XYZ_to_RGB( + XYZ_cr, colourspace, xy_r, apply_cctf_encoding=True + ) scatter_settings["c"] = np.clip(RGB_ct, 0, 1) diff --git a/colour/plotting/section.py b/colour/plotting/section.py index f4b826ecae..3631a91300 100644 --- a/colour/plotting/section.py +++ b/colour/plotting/section.py @@ -754,12 +754,7 @@ def plot_RGB_colourspace_section( ) vertices, faces, _outline = primitive_cube(1, 1, 1, 64, 64, 64) - XYZ_vertices = RGB_to_XYZ( - vertices["position"] + 0.5, - colourspace.whitepoint, - colourspace.whitepoint, - colourspace.matrix_RGB_to_XYZ, - ) + XYZ_vertices = RGB_to_XYZ(vertices["position"] + 0.5, colourspace) hull = trimesh.Trimesh(XYZ_vertices, faces, process=False) if show_section_colours: diff --git a/colour/plotting/tests/test_section.py b/colour/plotting/tests/test_section.py index 42d90f5b3f..304674d611 100644 --- a/colour/plotting/tests/test_section.py +++ b/colour/plotting/tests/test_section.py @@ -50,10 +50,7 @@ def test_plot_hull_section_colours(self): vertices, faces, _outline = primitive_cube(1, 1, 1, 64, 64, 64) XYZ_vertices = RGB_to_XYZ( - vertices["position"] + 0.5, - RGB_COLOURSPACE_sRGB.whitepoint, - RGB_COLOURSPACE_sRGB.whitepoint, - RGB_COLOURSPACE_sRGB.matrix_RGB_to_XYZ, + vertices["position"] + 0.5, RGB_COLOURSPACE_sRGB ) hull = trimesh.Trimesh(XYZ_vertices, faces, process=False) @@ -92,10 +89,7 @@ def test_plot_hull_section_contour(self): vertices, faces, _outline = primitive_cube(1, 1, 1, 64, 64, 64) XYZ_vertices = RGB_to_XYZ( - vertices["position"] + 0.5, - RGB_COLOURSPACE_sRGB.whitepoint, - RGB_COLOURSPACE_sRGB.whitepoint, - RGB_COLOURSPACE_sRGB.matrix_RGB_to_XYZ, + vertices["position"] + 0.5, RGB_COLOURSPACE_sRGB ) hull = trimesh.Trimesh(XYZ_vertices, faces, process=False) diff --git a/colour/plotting/volume.py b/colour/plotting/volume.py index 5dcc43d93c..1500bc0067 100644 --- a/colour/plotting/volume.py +++ b/colour/plotting/volume.py @@ -600,7 +600,6 @@ def plot_RGB_colourspaces_gamuts( RGB_cf: list = [] RGB_ce: list = [] for i, colourspace in enumerate(colourspaces): - if chromatically_adapt and not np.array_equal( colourspace.whitepoint, plotting_colourspace.whitepoint ): @@ -615,12 +614,7 @@ def plot_RGB_colourspaces_gamuts( depth_segments=segments, ) - XYZ = RGB_to_XYZ( - quads_cb, - colourspace.whitepoint, - colourspace.whitepoint, - colourspace.matrix_RGB_to_XYZ, - ) + XYZ = RGB_to_XYZ(quads_cb, colourspace) convert_settings = {"illuminant": colourspace.whitepoint} convert_settings.update(convert_kwargs) @@ -836,12 +830,7 @@ def plot_RGB_scatter( **settings, ) - XYZ = RGB_to_XYZ( - RGB, - colourspace.whitepoint, - colourspace.whitepoint, - colourspace.matrix_RGB_to_XYZ, - ) + XYZ = RGB_to_XYZ(RGB, colourspace) convert_settings = {"illuminant": colourspace.whitepoint} convert_settings.update(convert_kwargs) diff --git a/colour/recovery/jakob2019.py b/colour/recovery/jakob2019.py index fe5ff82c9b..c22d9adacc 100644 --- a/colour/recovery/jakob2019.py +++ b/colour/recovery/jakob2019.py @@ -201,9 +201,10 @@ def error_function( illuminant: SpectralDistribution, max_error: float | None = None, additional_data: bool = False, -) -> Tuple[float, NDArrayFloat] | Tuple[ - float, NDArrayFloat, NDArrayFloat, NDArrayFloat, NDArrayFloat -]: +) -> ( + Tuple[float, NDArrayFloat] + | Tuple[float, NDArrayFloat, NDArrayFloat, NDArrayFloat, NDArrayFloat] +): """ Compute :math:`\\Delta E_{76}` between the target colour and the colour defined by given spectral model, along with its gradient. @@ -922,12 +923,7 @@ def optimize( RGB = self._lightness_scale[L] * chroma - XYZ = RGB_to_XYZ( - RGB, - colourspace.whitepoint, - xy_n, - colourspace.matrix_RGB_to_XYZ, - ) + XYZ = RGB_to_XYZ(RGB, colourspace, xy_n) coefficients, _error = find_coefficients_Jakob2019( XYZ, cmfs, illuminant, coefficients_0, dimensionalise=False diff --git a/colour/recovery/smits1999.py b/colour/recovery/smits1999.py index 7e8d0da7d5..1504b9db57 100644 --- a/colour/recovery/smits1999.py +++ b/colour/recovery/smits1999.py @@ -13,13 +13,11 @@ from __future__ import annotations -import numpy as np - from colour.colorimetry import CCS_ILLUMINANTS, SpectralDistribution from colour.hints import ArrayLike, NDArrayFloat from colour.models import ( XYZ_to_RGB, - normalised_primary_matrix, + RGB_Colourspace, RGB_COLOURSPACE_sRGB, ) from colour.recovery import SDS_SMITS1999 @@ -34,26 +32,36 @@ __all__ = [ "PRIMARIES_SMITS1999", + "WHITEPOINT_NAME_SMITS1999", "CCS_WHITEPOINT_SMITS1999", - "MATRIX_XYZ_TO_RGB_SMITS1999", + "RGB_COLOURSPACE_SMITS1999", "XYZ_to_RGB_Smits1999", "RGB_to_sd_Smits1999", ] PRIMARIES_SMITS1999: NDArrayFloat = RGB_COLOURSPACE_sRGB.primaries -"""Current *Smits (1999)* method implementation colourspace primaries.""" +"""*Smits (1999)* method implementation colourspace primaries.""" + +WHITEPOINT_NAME_SMITS1999 = "E" +"""*Smits (1999)* method implementation colourspace whitepoint name.""" CCS_WHITEPOINT_SMITS1999: NDArrayFloat = CCS_ILLUMINANTS[ "CIE 1931 2 Degree Standard Observer" -]["E"] -"""Current *Smits (1999)* method implementation colourspace whitepoint.""" - -MATRIX_XYZ_TO_RGB_SMITS1999: NDArrayFloat = np.linalg.inv( - normalised_primary_matrix(PRIMARIES_SMITS1999, CCS_WHITEPOINT_SMITS1999) +][WHITEPOINT_NAME_SMITS1999] +"""*Smits (1999)* method implementation colourspace whitepoint.""" + +RGB_COLOURSPACE_SMITS1999 = RGB_Colourspace( + "Smits 1999", + PRIMARIES_SMITS1999, + CCS_WHITEPOINT_SMITS1999, + WHITEPOINT_NAME_SMITS1999, ) -""" -Current *Smits (1999)* method implementation *RGB* colourspace to -*CIE XYZ* tristimulus values matrix. +RGB_COLOURSPACE_sRGB.__doc__ = """ +*Smits (1999)* colourspace. + +References +---------- +:cite:`Smits1999a`, """ @@ -74,17 +82,13 @@ def XYZ_to_RGB_Smits1999(XYZ: ArrayLike) -> NDArrayFloat: Examples -------- + >>> import numpy as np >>> XYZ = np.array([0.21781186, 0.12541048, 0.04697113]) >>> XYZ_to_RGB_Smits1999(XYZ) # doctest: +ELLIPSIS array([ 0.4063959..., 0.0275289..., 0.0398219...]) """ - return XYZ_to_RGB( - XYZ, - CCS_WHITEPOINT_SMITS1999, - CCS_WHITEPOINT_SMITS1999, - MATRIX_XYZ_TO_RGB_SMITS1999, - ) + return XYZ_to_RGB(XYZ, RGB_COLOURSPACE_SMITS1999) def RGB_to_sd_Smits1999(RGB: ArrayLike) -> SpectralDistribution: @@ -116,6 +120,7 @@ def RGB_to_sd_Smits1999(RGB: ArrayLike) -> SpectralDistribution: Examples -------- + >>> import numpy as np >>> from colour import MSDS_CMFS, SDS_ILLUMINANTS, SpectralShape >>> from colour.colorimetry import sd_to_XYZ_integration >>> from colour.utilities import numpy_print_options diff --git a/colour/recovery/tests/test_jakob2019.py b/colour/recovery/tests/test_jakob2019.py index 60ae897d1d..d5ce3b4082 100644 --- a/colour/recovery/tests/test_jakob2019.py +++ b/colour/recovery/tests/test_jakob2019.py @@ -323,12 +323,7 @@ def test_LUT3D_Jakob2019(self): full(3, 0.5), ones(3), ]: - XYZ = RGB_to_XYZ( - RGB, - self._RGB_colourspace.whitepoint, - self._xy_D65, - self._RGB_colourspace.matrix_RGB_to_XYZ, - ) + XYZ = RGB_to_XYZ(RGB, self._RGB_colourspace, self._xy_D65) Lab = XYZ_to_Lab(XYZ, self._xy_D65) recovered_sd = LUT.RGB_to_sd(RGB) diff --git a/colour/recovery/tests/test_mallett2019.py b/colour/recovery/tests/test_mallett2019.py index 0a8ea6dc81..22774726d3 100644 --- a/colour/recovery/tests/test_mallett2019.py +++ b/colour/recovery/tests/test_mallett2019.py @@ -82,12 +82,7 @@ def check_basis_functions(self): for name, sd in SDS_COLOURCHECKERS["ColorChecker N Ohta"].items(): XYZ = sd_to_XYZ(sd, self._cmfs, self._sd_D65) / 100 Lab = XYZ_to_Lab(XYZ, self._xy_D65) - RGB = XYZ_to_RGB( - XYZ, - self._RGB_colourspace.whitepoint, - self._xy_D65, - self._RGB_colourspace.matrix_XYZ_to_RGB, - ) + RGB = XYZ_to_RGB(XYZ, self._RGB_colourspace, self._xy_D65) recovered_sd = RGB_to_sd_Mallett2019(RGB, self._basis) recovered_XYZ = ( diff --git a/colour/volume/rgb.py b/colour/volume/rgb.py index 82168cd81b..a0ed381d8b 100644 --- a/colour/volume/rgb.py +++ b/colour/volume/rgb.py @@ -152,10 +152,9 @@ def sample_RGB_colourspace_volume_MonteCarlo( Lab = random_generator(DEFAULT_INT_DTYPE(samples), limits, random_state) RGB = XYZ_to_RGB( Lab_to_XYZ(Lab, illuminant_Lab), + colourspace, illuminant_Lab, - colourspace.whitepoint, - colourspace.matrix_XYZ_to_RGB, - chromatic_adaptation_transform=chromatic_adaptation_transform, + chromatic_adaptation_transform, ) RGB_w = RGB[ np.logical_and(np.min(RGB, axis=-1) >= 0, np.max(RGB, axis=-1) <= 1) @@ -200,12 +199,7 @@ def RGB_colourspace_limits(colourspace: RGB_Colourspace) -> NDArrayFloat: for combination in list(itertools.product([0, 1], repeat=3)): Lab_c.append( XYZ_to_Lab( - RGB_to_XYZ( - combination, - colourspace.whitepoint, - colourspace.whitepoint, - colourspace.matrix_RGB_to_XYZ, - ), + RGB_to_XYZ(combination, colourspace), colourspace.whitepoint, ) ) @@ -372,12 +366,7 @@ def RGB_colourspace_volume_coverage_MonteCarlo( ) XYZ_vs = XYZ[coverage_sampler(XYZ)] - RGB = XYZ_to_RGB( - XYZ_vs, - colourspace.whitepoint, - colourspace.whitepoint, - colourspace.matrix_XYZ_to_RGB, - ) + RGB = XYZ_to_RGB(XYZ_vs, colourspace) RGB_c = RGB[ np.logical_and(np.min(RGB, axis=-1) >= 0, np.max(RGB, axis=-1) <= 1) diff --git a/utilities/generate_plots.py b/utilities/generate_plots.py index 2cd4eb36fb..0bba3af6e7 100755 --- a/utilities/generate_plots.py +++ b/utilities/generate_plots.py @@ -874,12 +874,7 @@ def generate_documentation_plots(output_directory: str): output_directory, "Plotting_Plot_Hull_Section_Colours.png" ) vertices, faces, _outline = primitive_cube(1, 1, 1, 64, 64, 64) - XYZ_vertices = RGB_to_XYZ( - vertices["position"] + 0.5, - RGB_COLOURSPACE_sRGB.whitepoint, - RGB_COLOURSPACE_sRGB.whitepoint, - RGB_COLOURSPACE_sRGB.matrix_RGB_to_XYZ, - ) + XYZ_vertices = RGB_to_XYZ(vertices["position"] + 0.5, RGB_COLOURSPACE_sRGB) hull = trimesh.Trimesh(XYZ_vertices, faces, process=False) plt.close( plot_hull_section_colours(hull, section_colours="RGB", **arguments)[0]