def RGB_to_rgb(RGB: ArrayLike) -> NDArray: """ Convert given *RGB* array to *Hunt-Pointer-Estevez* :math:`\\rho\\gamma\\beta` colourspace. Parameters ---------- RGB *RGB* array. Returns ------- :class:`numpy.ndarray` *Hunt-Pointer-Estevez* :math:`\\rho\\gamma\\beta` colourspace array. Examples -------- >>> RGB = np.array([19.99370783, 20.00393634, 20.01326387]) >>> RGB_to_rgb(RGB) # doctest: +ELLIPSIS array([ 19.9969397..., 20.0018612..., 20.0135053...]) """ rgb = vector_dot(matrix_dot(MATRIX_XYZ_TO_HPE, CAT_INVERSE_CAT02), RGB) return rgb
def rgb_to_RGB(rgb: ArrayLike) -> NDArray: """ Convert given *Hunt-Pointer-Estevez* :math:`\\rho\\gamma\\beta` colourspace array to *RGB* array. Parameters ---------- rgb *Hunt-Pointer-Estevez* :math:`\\rho\\gamma\\beta` colourspace array. Returns ------- :class:`numpy.ndarray` *RGB* array. Examples -------- >>> rgb = np.array([19.99693975, 20.00186123, 20.01350530]) >>> rgb_to_RGB(rgb) # doctest: +ELLIPSIS array([ 19.9937078..., 20.0039363..., 20.0132638...]) """ RGB = vector_dot(matrix_dot(CAT_CAT02, MATRIX_HPE_TO_XYZ), rgb) return RGB
def test_matrix_dot(self): """Test :func:`colour.utilities.array.matrix_dot` definition.""" a = np.array( [ [0.7328, 0.4296, -0.1624], [-0.7036, 1.6975, 0.0061], [0.0030, 0.0136, 0.9834], ] ) a = np.reshape(np.tile(a, (6, 1)), (6, 3, 3)) b = a np.testing.assert_almost_equal( matrix_dot(a, b), np.array( [ [ [0.23424208, 1.04184824, -0.27609032], [-1.70994078, 2.57932265, 0.13061813], [-0.00442036, 0.03774904, 0.96667132], ], [ [0.23424208, 1.04184824, -0.27609032], [-1.70994078, 2.57932265, 0.13061813], [-0.00442036, 0.03774904, 0.96667132], ], [ [0.23424208, 1.04184824, -0.27609032], [-1.70994078, 2.57932265, 0.13061813], [-0.00442036, 0.03774904, 0.96667132], ], [ [0.23424208, 1.04184824, -0.27609032], [-1.70994078, 2.57932265, 0.13061813], [-0.00442036, 0.03774904, 0.96667132], ], [ [0.23424208, 1.04184824, -0.27609032], [-1.70994078, 2.57932265, 0.13061813], [-0.00442036, 0.03774904, 0.96667132], ], [ [0.23424208, 1.04184824, -0.27609032], [-1.70994078, 2.57932265, 0.13061813], [-0.00442036, 0.03774904, 0.96667132], ], ] ), decimal=7, )
def XYZ_to_RLAB( XYZ: ArrayLike, XYZ_n: ArrayLike, Y_n: FloatingOrArrayLike, sigma: FloatingOrArrayLike = VIEWING_CONDITIONS_RLAB["Average"], D: FloatingOrArrayLike = D_FACTOR_RLAB["Hard Copy Images"], ) -> CAM_Specification_RLAB: """ Compute the *RLAB* model color appearance correlates. Parameters ---------- XYZ *CIE XYZ* tristimulus values of test sample / stimulus. XYZ_n *CIE XYZ* tristimulus values of reference white. Y_n Absolute adapting luminance in :math:`cd/m^2`. sigma Relative luminance of the surround, see :attr:`colour.VIEWING_CONDITIONS_RLAB` for reference. D *Discounting-the-Illuminant* factor normalised to domain [0, 1]. Returns ------- CAM_Specification_RLAB *RLAB* colour appearance model specification. Notes ----- +------------+-----------------------+---------------+ | **Domain** | **Scale - Reference** | **Scale - 1** | +============+=======================+===============+ | ``XYZ`` | [0, 100] | [0, 1] | +------------+-----------------------+---------------+ | ``XYZ_n`` | [0, 100] | [0, 1] | +------------+-----------------------+---------------+ +------------------------------+-----------------------\ +---------------+ | **Range** | **Scale - Reference** \ | **Scale - 1** | +==============================+=======================\ +===============+ | ``CAM_Specification_RLAB.h`` | [0, 360] \ | [0, 1] | +------------------------------+-----------------------\ +---------------+ References ---------- :cite:`Fairchild1996a`, :cite:`Fairchild2013w` Examples -------- >>> XYZ = np.array([19.01, 20.00, 21.78]) >>> XYZ_n = np.array([109.85, 100, 35.58]) >>> Y_n = 31.83 >>> sigma = VIEWING_CONDITIONS_RLAB['Average'] >>> D = D_FACTOR_RLAB['Hard Copy Images'] >>> XYZ_to_RLAB(XYZ, XYZ_n, Y_n, sigma, D) # doctest: +ELLIPSIS CAM_Specification_RLAB(J=49.8347069..., C=54.8700585..., \ h=286.4860208..., s=1.1010410..., HC=None, a=15.5711021..., \ b=-52.6142956...) """ XYZ = to_domain_100(XYZ) XYZ_n = to_domain_100(XYZ_n) Y_n = as_float_array(Y_n) D = as_float_array(D) sigma = as_float_array(sigma) # Converting to cone responses. LMS_n = XYZ_to_rgb(XYZ_n) # Computing the :math:`A` matrix. LMS_l_E = (3 * LMS_n) / np.sum(LMS_n, axis=-1)[..., np.newaxis] LMS_p_L = (1 + spow(Y_n[..., np.newaxis], 1 / 3) + LMS_l_E) / (1 + spow(Y_n[..., np.newaxis], 1 / 3) + (1 / LMS_l_E)) LMS_a_L = (LMS_p_L + D[..., np.newaxis] * (1 - LMS_p_L)) / LMS_n M = matrix_dot(matrix_dot(MATRIX_R, row_as_diagonal(LMS_a_L)), MATRIX_XYZ_TO_HPE) XYZ_ref = vector_dot(M, XYZ) X_ref, Y_ref, Z_ref = tsplit(XYZ_ref) # Computing the correlate of *Lightness* :math:`L^R`. LR = 100 * spow(Y_ref, sigma) # Computing opponent colour dimensions :math:`a^R` and :math:`b^R`. aR = as_float(430 * (spow(X_ref, sigma) - spow(Y_ref, sigma))) bR = as_float(170 * (spow(Y_ref, sigma) - spow(Z_ref, sigma))) # Computing the *hue* angle :math:`h^R`. hR = np.degrees(np.arctan2(bR, aR)) % 360 # TODO: Implement hue composition computation. # Computing the correlate of *chroma* :math:`C^R`. CR = np.hypot(aR, bR) # Computing the correlate of *saturation* :math:`s^R`. sR = CR / LR return CAM_Specification_RLAB( LR, CR, as_float(from_range_degrees(hR)), sR, None, aR, bR, )
def matrix_anomalous_trichromacy_Machado2009( cmfs: LMS_ConeFundamentals, primaries: RGB_DisplayPrimaries, d_LMS: ArrayLike, ) -> NDArray: """ Compute the *Machado et al. (2009)* *CVD* matrix for given *LMS* cone fundamentals colour matching functions and display primaries tri-spectral distributions with given :math:`\\Delta_{LMS}` shift amount in nanometers to simulate anomalous trichromacy. Parameters ---------- cmfs *LMS* cone fundamentals colour matching functions. primaries *RGB* display primaries tri-spectral distributions. d_LMS :math:`\\Delta_{LMS}` shift amount in nanometers. Notes ----- - Input *LMS* cone fundamentals colour matching functions interval is expected to be 1 nanometer, incompatible input will be interpolated at 1 nanometer interval. - Input :math:`\\Delta_{LMS}` shift amount is in domain [0, 20]. Returns ------- :class:`numpy.ndarray` Anomalous trichromacy matrix. References ---------- :cite:`Colblindorb`, :cite:`Colblindora`, :cite:`Colblindorc`, :cite:`Machado2009` Examples -------- >>> from colour.characterisation import MSDS_DISPLAY_PRIMARIES >>> from colour.colorimetry import MSDS_CMFS_LMS >>> cmfs = MSDS_CMFS_LMS['Stockman & Sharpe 2 Degree Cone Fundamentals'] >>> d_LMS = np.array([15, 0, 0]) >>> primaries = MSDS_DISPLAY_PRIMARIES['Apple Studio Display'] >>> matrix_anomalous_trichromacy_Machado2009(cmfs, primaries, d_LMS) ... # doctest: +ELLIPSIS array([[-0.2777465..., 2.6515008..., -1.3737543...], [ 0.2718936..., 0.2004786..., 0.5276276...], [ 0.0064404..., 0.2592157..., 0.7343437...]]) """ if cmfs.shape.interval != 1: # pylint: disable=E1102 cmfs = reshape_msds( cmfs, # type: ignore[assignment] SpectralShape(cmfs.shape.start, cmfs.shape.end, 1), "Interpolate", ) M_n = matrix_RGB_to_WSYBRG(cmfs, primaries) cmfs_a = msds_cmfs_anomalous_trichromacy_Machado2009(cmfs, d_LMS) M_a = matrix_RGB_to_WSYBRG(cmfs_a, primaries) return matrix_dot(np.linalg.inv(M_n), M_a)
def matrix_chromatic_adaptation_VonKries( XYZ_w: ArrayLike, XYZ_wr: ArrayLike, transform: Union[Literal["Bianco 2010", "Bianco PC 2010", "Bradford", "CAT02 Brill 2008", "CAT02", "CAT16", "CMCCAT2000", "CMCCAT97", "Fairchild", "Sharp", "Von Kries", "XYZ Scaling", ], str, ] = "CAT02", ) -> NDArray: """ Compute the *chromatic adaptation* matrix from test viewing conditions to reference viewing conditions. Parameters ---------- XYZ_w Test viewing conditions *CIE XYZ* tristimulus values of whitepoint. XYZ_wr Reference viewing conditions *CIE XYZ* tristimulus values of whitepoint. transform Chromatic adaptation transform. Returns ------- :class:`numpy.ndarray` Chromatic adaptation matrix :math:`M_{cat}`. Notes ----- +------------+-----------------------+---------------+ | **Domain** | **Scale - Reference** | **Scale - 1** | +============+=======================+===============+ | ``XYZ_w`` | [0, 1] | [0, 1] | +------------+-----------------------+---------------+ | ``XYZ_wr`` | [0, 1] | [0, 1] | +------------+-----------------------+---------------+ References ---------- :cite:`Fairchild2013t` Examples -------- >>> XYZ_w = np.array([0.95045593, 1.00000000, 1.08905775]) >>> XYZ_wr = np.array([0.96429568, 1.00000000, 0.82510460]) >>> matrix_chromatic_adaptation_VonKries(XYZ_w, XYZ_wr) ... # doctest: +ELLIPSIS array([[ 1.0425738..., 0.0308910..., -0.0528125...], [ 0.0221934..., 1.0018566..., -0.0210737...], [-0.0011648..., -0.0034205..., 0.7617890...]]) Using Bradford method: >>> XYZ_w = np.array([0.95045593, 1.00000000, 1.08905775]) >>> XYZ_wr = np.array([0.96429568, 1.00000000, 0.82510460]) >>> method = 'Bradford' >>> matrix_chromatic_adaptation_VonKries(XYZ_w, XYZ_wr, method) ... # doctest: +ELLIPSIS array([[ 1.0479297..., 0.0229468..., -0.0501922...], [ 0.0296278..., 0.9904344..., -0.0170738...], [-0.0092430..., 0.0150551..., 0.7518742...]]) """ XYZ_w = to_domain_1(XYZ_w) XYZ_wr = to_domain_1(XYZ_wr) transform = validate_method( transform, CHROMATIC_ADAPTATION_TRANSFORMS, '"{0}" chromatic adaptation transform is invalid, ' "it must be one of {1}!", ) M = CHROMATIC_ADAPTATION_TRANSFORMS[transform] RGB_w = np.einsum("...i,...ij->...j", XYZ_w, np.transpose(M)) RGB_wr = np.einsum("...i,...ij->...j", XYZ_wr, np.transpose(M)) D = RGB_wr / RGB_w D = row_as_diagonal(D) M_CAT = matrix_dot(np.linalg.inv(M), D) M_CAT = matrix_dot(M_CAT, M) return M_CAT
def matrix_RGB_to_RGB( input_colourspace: RGB_Colourspace, output_colourspace: RGB_Colourspace, chromatic_adaptation_transform: Union[ Literal[ "Bianco 2010", "Bianco PC 2010", "Bradford", "CAT02 Brill 2008", "CAT02", "CAT16", "CMCCAT2000", "CMCCAT97", "Fairchild", "Sharp", "Von Kries", "XYZ Scaling", ], str, ] = "CAT02", ) -> NDArray: """ Compute the matrix :math:`M` converting from given input *RGB* colourspace to output *RGB* colourspace using given *chromatic adaptation* method. Parameters ---------- input_colourspace *RGB* input colourspace. output_colourspace *RGB* output colourspace. chromatic_adaptation_transform *Chromatic adaptation* transform, if *None* no chromatic adaptation is performed. Returns ------- :class:`numpy.ndarray` Conversion matrix :math:`M`. Examples -------- >>> from colour.models import ( ... RGB_COLOURSPACE_sRGB, RGB_COLOURSPACE_PROPHOTO_RGB) >>> matrix_RGB_to_RGB(RGB_COLOURSPACE_sRGB, RGB_COLOURSPACE_PROPHOTO_RGB) ... # doctest: +ELLIPSIS array([[ 0.5288241..., 0.3340609..., 0.1373616...], [ 0.0975294..., 0.8790074..., 0.0233981...], [ 0.0163599..., 0.1066124..., 0.8772485...]]) """ M = input_colourspace.matrix_RGB_to_XYZ if chromatic_adaptation_transform is not None: M_CAT = matrix_chromatic_adaptation_VonKries( xy_to_XYZ(input_colourspace.whitepoint), xy_to_XYZ(output_colourspace.whitepoint), chromatic_adaptation_transform, ) M = matrix_dot(M_CAT, input_colourspace.matrix_RGB_to_XYZ) M = matrix_dot(output_colourspace.matrix_XYZ_to_RGB, M) return M