def output_specification_from_data(self, data): """ Returns the CIECAM02 colour appearance model output specification from given data. Parameters ---------- data : list Fixture data. Returns ------- CIECAM02_Specification CIECAM02 colour appearance model specification. """ XYZ = tstack((data['X'], data['Y'], data['Z'])) XYZ_w = tstack((data['X_w'], data['Y_w'], data['Z_w'])) specification = XYZ_to_CIECAM02(XYZ, XYZ_w, data['L_A'], data['Y_b'], CIECAM02_InductionFactors( data['F'], data['c'], data['N_c'])) return specification
def output_specification_from_data(self, data): """ Returns the ATD (1995) colour vision model output specification from given data. Parameters ---------- data : list Fixture data. Returns ------- ATD95_Specification ATD (1995) colour vision model specification. """ XYZ = tstack((data['X'], data['Y'], data['Z'])) XYZ_0 = tstack((data['X_0'], data['Y_0'], data['Z_0'])) specification = XYZ_to_ATD95(XYZ, XYZ_0, data['Y_02'], data['K_1'], data['K_2'], data['sigma']) return specification
def cartesian_to_spherical(a): """ Transforms given Cartesian coordinates array to Spherical coordinates. Parameters ---------- a : array_like Cartesian coordinates array (x, y, z) to transform. Returns ------- ndarray Spherical coordinates array (r, theta, phi). See Also -------- spherical_to_cartesian, cartesian_to_cylindrical, cylindrical_to_cartesian Examples -------- >>> a = np.array([3, 1, 6]) >>> cartesian_to_spherical(a) # doctest: +ELLIPSIS array([ 6.7823299..., 1.0857465..., 0.3217505...]) """ x, y, z = tsplit(a) r = np.linalg.norm(a, axis=-1) theta = np.arctan2(z, np.linalg.norm(tstack((x, y)), axis=-1)) phi = np.arctan2(y, x) rtp = tstack((r, theta, phi)) return rtp
def cartesian_to_cylindrical(a): """ Transforms given Cartesian coordinates array to Cylindrical coordinates. Parameters ---------- a : array_like Cartesian coordinates array (x, y, z) to transform. Returns ------- ndarray Cylindrical coordinates array (z, theta, rho). See Also -------- cartesian_to_spherical, spherical_to_cartesian, cylindrical_to_cartesian Examples -------- >>> a = np.array([3, 1, 6]) >>> cartesian_to_cylindrical(a) # doctest: +ELLIPSIS array([ 6. , 0.3217505..., 3.1622776...]) """ x, y, z = tsplit(a) theta = np.arctan2(y, x) rho = np.linalg.norm(tstack((x, y)), axis=-1) return tstack((z, theta, rho))
def output_specification_from_data(self, data): """ Returns the Nayatani (1995) colour appearance model output specification from given data. Parameters ---------- data : list Fixture data. Returns ------- Nayatani95_Specification Nayatani (1995) colour appearance model specification. """ XYZ = tstack((data['X'], data['Y'], data['Z'])) XYZ_n = tstack((data['X_n'], data['Y_n'], data['Z_n'])) specification = XYZ_to_Nayatani95(XYZ, XYZ_n, data['Y_o'], data['E_o'], data['E_or']) return specification
def output_specification_from_data(self, data): """ Returns the RLAB colour appearance model output specification from given data. Parameters ---------- data : list Fixture data. Returns ------- RLAB_Specification RLAB colour appearance model specification. """ XYZ = tstack((data['X'], data['Y'], data['Z'])) XYZ_n = tstack((data['X_n'], data['Y_n'], data['Z_n'])) specification = XYZ_to_RLAB(XYZ, XYZ_n, data['Y_n2'], data['sigma'], data['D']) return specification
def output_specification_from_data(self, data): """ Returns the Hunt colour appearance model output specification from given data. Parameters ---------- data : list Fixture data. Returns ------- Hunt_Specification Hunt colour appearance model specification. """ XYZ = tstack((data["X"], data["Y"], data["Z"])) XYZ_w = tstack((data["X_w"], data["Y_w"], data["Z_w"])) XYZ_b = tstack((data["X_w"], 0.2 * data["Y_w"], data["Z_w"])) specification = XYZ_to_Hunt( XYZ, XYZ_w, XYZ_b, data["L_A"], Hunt_InductionFactors(data["N_c"], data["N_b"]), CCT_w=data["T"] ) return specification
def test_extrapolate(self): """ Tests :func:`colour.colorimetry.spectrum.\ MultiSpectralDistribution.extrapolate` method. """ data = dict(zip(range(25, 35), tstack([[0] * 5 + [1] * 5] * 3))) multi_sd = MultiSpectralDistribution(data) multi_sd.extrapolate(SpectralShape(10, 50)) np.testing.assert_almost_equal( multi_sd[10], np.array([0.0, 0.0, 0.0]), decimal=7) np.testing.assert_almost_equal( multi_sd[50], np.array([1.0, 1.0, 1.0]), decimal=7) multi_sd = MultiSpectralDistribution( tstack([np.linspace(0, 1, 10)] * 3), np.linspace(25, 35, 10)) multi_sd.extrapolate( SpectralShape(10, 50), extrapolator_args={ 'method': 'Linear', 'left': None, 'right': None }) np.testing.assert_almost_equal( multi_sd[10], np.array([-1.5, -1.5, -1.5]), decimal=7) np.testing.assert_almost_equal( multi_sd[50], np.array([2.5, 2.5, 2.5]), decimal=7)
def setUp(self): """ Initialises common tests attributes. """ self._labels = ('x_bar', 'y_bar', 'z_bar') self._multi_sd = MultiSpectralDistribution( CIE_1931_2_DEGREE_STANDARD_OBSERVER, name='Observer', labels=self._labels) sd = SpectralDistribution(SAMPLE_SD_DATA) domain = sd.domain range_ = tstack([sd.values, sd.values, sd.values]) self._sample_multi_sd = MultiSpectralDistribution( range_, domain, name='Sample Observer', labels=self._labels, ) sd = SpectralDistribution(NON_UNIFORM_SAMPLE_SD_DATA) domain = sd.domain range_ = tstack([sd.values, sd.values, sd.values]) self._non_uniform_sample_multi_sd = MultiSpectralDistribution( range_, domain, name='Non Uniform Sample Observer', strict_name='Strict Non Uniform Sample Observer', labels=self._labels, strict_labels=('Strict x_bar', 'Strict y_bar', 'Strict z_bar')) self._phi = (1 + np.sqrt(5)) / 2
def output_specification_from_data(self, data): """ Returns the Hunt colour appearance model output specification from given data. Parameters ---------- data : list Fixture data. Returns ------- Hunt_Specification Hunt colour appearance model specification. """ XYZ = tstack((data['X'], data['Y'], data['Z'])) XYZ_w = tstack((data['X_w'], data['Y_w'], data['Z_w'])) XYZ_b = tstack((data['X_w'], 0.2 * data['Y_w'], data['Z_w'])) specification = XYZ_to_Hunt(XYZ, XYZ_w, XYZ_b, data['L_A'], Hunt_InductionFactors( data['N_c'], data['N_b']), CCT_w=data['T']) return specification
def LCHab_to_Lab(LCHab): """ Converts from *CIE L\\*C\\*Hab* colourspace to *CIE L\\*a\\*b\\** colourspace. Parameters ---------- LCHab : array_like *CIE L\\*C\\*Hab* colourspace array. Returns ------- ndarray *CIE L\\*a\\*b\\** colourspace array. Notes ----- +-------------+-----------------------+-----------------+ | **Domain** | **Scale - Reference** | **Scale - 1** | +=============+=======================+=================+ | ``LCHab`` | ``L`` : [0, 100] | ``L`` : [0, 1] | | | | | | | ``C`` : [0, 100] | ``C`` : [0, 1] | | | | | | | ``ab`` : [0, 360] | ``ab`` : [0, 1] | +-------------+-----------------------+-----------------+ +-------------+-----------------------+-----------------+ | **Range** | **Scale - Reference** | **Scale - 1** | +=============+=======================+=================+ | ``Lab`` | ``L`` : [0, 100] | ``L`` : [0, 1] | | | | | | | ``a`` : [-100, 100] | ``a`` : [-1, 1] | | | | | | | ``b`` : [-100, 100] | ``b`` : [-1, 1] | +-------------+-----------------------+-----------------+ References ---------- :cite:`CIETC1-482004m` Examples -------- >>> LCHab = np.array([41.52787529, 59.12425901, 27.08848784]) >>> LCHab_to_Lab(LCHab) # doctest: +ELLIPSIS array([ 41.5278752..., 52.6385830..., 26.9231792...]) """ L, C, H = tsplit(LCHab) a, b = tsplit( polar_to_cartesian(tstack([C, np.radians(to_domain_degrees(H))]))) Lab = tstack([L, a, b]) return Lab
def LCHuv_to_Luv(LCHuv): """ Converts from *CIE L\\*C\\*Huv* colourspace to *CIE L\\*u\\*v\\** colourspace. Parameters ---------- LCHuv : array_like *CIE L\\*C\\*Huv* colourspace array. Returns ------- ndarray *CIE L\\*u\\*v\\** colourspace array. Notes ----- +------------+-----------------------+-----------------+ | **Domain** | **Scale - Reference** | **Scale - 1** | +============+=======================+=================+ | ``LCHuv`` | ``L`` : [0, 100] | ``L`` : [0, 1] | | | | | | | ``C`` : [0, 100] | ``C`` : [0, 1] | | | | | | | ``uv`` : [0, 360] | ``uv`` : [0, 1] | +------------+-----------------------+-----------------+ +------------+-----------------------+-----------------+ | **Range** | **Scale - Reference** | **Scale - 1** | +============+=======================+=================+ | ``Luv`` | ``L`` : [0, 100] | ``L`` : [0, 1] | | | | | | | ``u`` : [-100, 100] | ``u`` : [-1, 1] | | | | | | | ``v`` : [-100, 100] | ``v`` : [-1, 1] | +------------+-----------------------+-----------------+ References ---------- :cite:`CIETC1-482004m` Examples -------- >>> LCHuv = np.array([41.52787529, 98.44997950, 10.38816348]) >>> LCHuv_to_Luv(LCHuv) # doctest: +ELLIPSIS array([ 41.5278752..., 96.8362605..., 17.7521014...]) """ L, C, H = tsplit(LCHuv) u, v = tsplit( polar_to_cartesian(tstack([C, np.radians(to_domain_degrees(H))]))) Luv = tstack([L, u, v]) return Luv
def chromatic_adaptation(RGB, RGB_0, RGB_0r, Y, D=1): """ Applies chromatic adaptation to given *RGB* normalised cone responses array. Parameters ---------- RGB : array_like *RGB* normalised cone responses array of test sample / stimulus. RGB_0 : array_like *RGB* normalised cone responses array of reference white. RGB_0r : array_like *RGB* normalised cone responses array of reference illuminant *CIE Standard Illuminant D Series* *D65*. Y : numeric or array_like Tristimulus values :math:`Y` of the stimulus. D : numeric or array_like, optional *Discounting-the-Illuminant* factor in domain [0, 1]. Returns ------- ndarray Adapted *CIE XYZ* tristimulus values. Examples -------- >>> RGB = np.array([0.94142795, 1.04040120, 1.08970885]) >>> RGB_0 = np.array([0.94146023, 1.04039386, 1.08950293]) >>> RGB_0r = np.array([0.94146023, 1.04039386, 1.08950293]) >>> Y = 20.0 >>> chromatic_adaptation(RGB, RGB_0, RGB_0r, Y) # doctest: +ELLIPSIS array([ 19.01, 20. , 21.78]) """ R, G, B = tsplit(RGB) R_0, G_0, B_0 = tsplit(RGB_0) R_0r, G_0r, B_0r = tsplit(RGB_0r) Y = np.asarray(Y) beta = (B_0 / B_0r) ** 0.0834 R_r = (D * (R_0r / R_0) + 1 - D) * R G_r = (D * (G_0r / G_0) + 1 - D) * G B_r = (D * (B_0r / (B_0 ** beta)) + 1 - D) * (abs(B) ** beta) RGB_r = tstack((R_r, G_r, B_r)) Y = tstack((Y, Y, Y)) XYZ_r = dot_vector(LLAB_RGB_TO_XYZ_MATRIX, RGB_r * Y) return XYZ_r
def xyY_to_XYZ(xyY): """ Converts from *CIE xyY* colourspace to *CIE XYZ* tristimulus values. Parameters ---------- xyY : array_like *CIE xyY* colourspace array. Returns ------- ndarray *CIE XYZ* tristimulus values. Notes ----- +------------+-----------------------+---------------+ | **Domain** | **Scale - Reference** | **Scale - 1** | +============+=======================+===============+ | ``xyY`` | [0, 1] | [0, 1] | +------------+-----------------------+---------------+ +------------+-----------------------+---------------+ | **Range** | **Scale - Reference** | **Scale - 1** | +============+=======================+===============+ | ``XYZ`` | [0, 1] | [0, 1] | +------------+-----------------------+---------------+ References ---------- :cite:`Lindbloom2009d`, :cite:`Wikipedia2005` Examples -------- >>> xyY = np.array([0.54369557, 0.32107944, 0.12197225]) >>> xyY_to_XYZ(xyY) # doctest: +ELLIPSIS array([ 0.2065400..., 0.1219722..., 0.0513695...]) """ x, y, Y = tsplit(xyY) Y = to_domain_1(Y) XYZ = np.where( (y == 0)[..., np.newaxis], tstack([y, y, y]), tstack([x * Y / y, Y, (1 - x - y) * Y / y]), ) return from_range_1(XYZ)
def xy_to_UCS_uv(xy): """ Returns the *CIE 1960 UCS* colourspace *uv* chromaticity coordinates from given *xy* chromaticity coordinates. Parameters ---------- xy : array_like *xy* chromaticity coordinates. Returns ------- ndarray *CIE UCS uv* chromaticity coordinates. References ---------- :cite:`Wikipedia2008c` Examples -------- >>> xy = np.array([0.54369555, 0.32107941]) >>> xy_to_UCS_uv(xy) # doctest: +ELLIPSIS array([ 0.3772021..., 0.3341350...]) """ x, y = tsplit(xy) d = 12 * y - 2 * x + 3 uv = tstack([4 * x / d, 6 * y / d]) return uv
def UCS_to_XYZ(UVW): """ Converts from *CIE UCS* colourspace to *CIE XYZ* tristimulus values. Parameters ---------- UVW : array_like *CIE UCS* colourspace array. Returns ------- ndarray *CIE XYZ* tristimulus values. Notes ----- - Input *CIE UCS* colourspace array is in domain [0, 1]. - Output *CIE XYZ* tristimulus values are in domain [0, 1]. Examples -------- >>> UVW = np.array([0.04699689, 0.10080000, 0.16374390]) >>> UCS_to_XYZ(UVW) # doctest: +ELLIPSIS array([ 0.0704953..., 0.1008 , 0.0955831...]) """ U, V, W = tsplit(UVW) XYZ = tstack((3 / 2 * U, V, 3 / 2 * U - (3 * V) + (2 * W))) return XYZ
def viewing_condition_dependent_parameters(Y_b, Y_w, L_A): """ Returns the viewing condition dependent parameters. Parameters ---------- Y_b : numeric or array_like Adapting field *Y* tristimulus value :math:`Y_b`. Y_w : numeric or array_like Whitepoint *Y* tristimulus value :math:`Y_w`. L_A : numeric or array_like Adapting field *luminance* :math:`L_A` in :math:`cd/m^2`. Returns ------- ndarray Viewing condition dependent parameters. Examples -------- >>> viewing_condition_dependent_parameters( # doctest: +ELLIPSIS ... 20.0, 100.0, 318.31) array([ 0.2..., 1.1675444..., 1.000304..., 1.000304..., 1.9272136...]) """ Y_b = np.asarray(Y_b) Y_w = np.asarray(Y_w) n = Y_b / Y_w F_L = luminance_level_adaptation_factor(L_A) N_bb, N_cb = tsplit(chromatic_induction_factors(n)) z = base_exponential_non_linearity(n) return tstack((n, F_L, N_bb, N_cb, z))
def chromatic_induction_factors(n): """ Returns the chromatic induction factors :math:`N_{bb}` and :math:`N_{cb}`. Parameters ---------- n : numeric or array_like Function of the luminance factor of the background :math:`n`. Returns ------- ndarray Chromatic induction factors :math:`N_{bb}` and :math:`N_{cb}`. Examples -------- >>> chromatic_induction_factors(0.2) # doctest: +ELLIPSIS array([ 1.000304..., 1.000304...]) """ n = np.asarray(n) N_cbb = 0.725 * (1 / n) ** 0.2 N_cbb = tstack((N_cbb, N_cbb)) return N_cbb
def UCS_uv_to_xy(uv): """ Returns the *xy* chromaticity coordinates from given *CIE UCS* colourspace *uv* chromaticity coordinates. Parameters ---------- uv : array_like *CIE UCS uv* chromaticity coordinates. Returns ------- ndarray *xy* chromaticity coordinates. Notes ----- - Input *uv* chromaticity coordinates are in domain [0, 1]. - Output *xy* chromaticity coordinates are in domain [0, 1]. Examples -------- >>> uv = np.array([0.15085308732766581, 0.3235531372954405]) >>> UCS_uv_to_xy(uv) # doctest: +ELLIPSIS array([ 0.2641477..., 0.3777000...]) """ u, v = tsplit(uv) xy = tstack((3 * u / (2 * u - 8 * v + 4), 2 * v / (2 * u - 8 * v + 4))) return xy
def XYZ_to_UCS(XYZ): """ Converts from *CIE XYZ* tristimulus values to *CIE UCS* colourspace. Parameters ---------- XYZ : array_like *CIE XYZ* tristimulus values. Returns ------- ndarray *CIE UCS* colourspace array. Notes ----- - Input *CIE XYZ* tristimulus values are in domain [0, 1]. - Output *CIE UCS* colourspace array is in domain [0, 1]. Examples -------- >>> XYZ = np.array([0.07049534, 0.10080000, 0.09558313]) >>> XYZ_to_UCS(XYZ) # doctest: +ELLIPSIS array([ 0.0469968..., 0.1008 , 0.1637439...]) """ X, Y, Z = tsplit(XYZ) UVW = tstack((2 / 3 * X, Y, 1 / 2 * (-X + 3 * Y + Z))) return UVW
def opponent_colour_dimensions_forward(RGB): """ Returns opponent colour dimensions from given compressed CMCCAT2000 transform sharpened *RGB* array for forward CIECAM02 implementation Parameters ---------- RGB : array_like Compressed CMCCAT2000 transform sharpened *RGB* array. Returns ------- ndarray Opponent colour dimensions. Examples -------- >>> RGB = np.array([7.94632020, 7.94711528, 7.94899595]) >>> opponent_colour_dimensions_forward(RGB) # doctest: +ELLIPSIS array([-0.0006241..., -0.0005062...]) """ R, G, B = tsplit(RGB) a = R - 12 * G / 11 + B / 11 b = (R + G - 2 * B) / 9 ab = tstack((a, b)) return ab
def UCS_to_uv(UVW): """ Returns the *uv* chromaticity coordinates from given *CIE UCS* colourspace array. Parameters ---------- UVW : array_like *CIE UCS* colourspace array. Returns ------- ndarray *uv* chromaticity coordinates. Notes ----- - Input *CIE UCS* colourspace array is in domain [0, 1]. - Output *uv* chromaticity coordinates are in domain [0, 1]. Examples -------- >>> UCS = np.array([0.04699689, 0.10080000, 0.16374390]) >>> UCS_to_uv(UCS) # doctest: +ELLIPSIS array([ 0.1508530..., 0.3235531...]) """ U, V, W = tsplit(UVW) uv = tstack((U / (U + V + W), V / (U + V + W))) return uv
def XYZ_to_xyY( XYZ, illuminant=ILLUMINANTS['CIE 1931 2 Degree Standard Observer']['D65']): """ Converts from *CIE XYZ* tristimulus values to *CIE xyY* colourspace and reference *illuminant*. Parameters ---------- XYZ : array_like *CIE XYZ* tristimulus values. illuminant : array_like, optional Reference *illuminant* chromaticity coordinates. Returns ------- ndarray *CIE xyY* colourspace array. Notes ----- +------------+-----------------------+---------------+ | **Domain** | **Scale - Reference** | **Scale - 1** | +============+=======================+===============+ | ``XYZ`` | [0, 1] | [0, 1] | +------------+-----------------------+---------------+ +------------+-----------------------+---------------+ | **Range** | **Scale - Reference** | **Scale - 1** | +============+=======================+===============+ | ``xyY`` | [0, 1] | [0, 1] | +------------+-----------------------+---------------+ References ---------- :cite:`Lindbloom2003e`, :cite:`Wikipedia2005` Examples -------- >>> XYZ = np.array([0.20654008, 0.12197225, 0.05136952]) >>> XYZ_to_xyY(XYZ) # doctest: +ELLIPSIS array([ 0.5436955..., 0.3210794..., 0.1219722...]) """ XYZ = to_domain_1(XYZ) X, Y, Z = tsplit(XYZ) xy_w = as_float_array(illuminant) XYZ_n = np.zeros(XYZ.shape) XYZ_n[..., 0:2] = xy_w xyY = np.where( np.all(XYZ == 0, axis=-1)[..., np.newaxis], XYZ_n, tstack([X / (X + Y + Z), Y / (X + Y + Z), from_range_1(Y)]), ) return xyY
def colour_difference_signals(rgb): """ Returns the colour difference signals :math:`C_1`, :math:`C_2` and :math:`C_3` from given *Hunt-Pointer-Estevez* :math:`\\rho\gamma\\beta` colourspace array. Parameters ---------- rgb : array_like *Hunt-Pointer-Estevez* :math:`\\rho\gamma\\beta` colourspace array. Returns ------- ndarray Colour difference signals :math:`C_1`, :math:`C_2` and :math:`C_3`. Examples -------- >>> rgb = np.array([6.89594549, 6.89599915, 6.89657085]) >>> colour_difference_signals(rgb) # doctest: +ELLIPSIS array([ -5.3660000...e-05, -5.7170000...e-04, 6.2536000...e-04]) """ r, g, b = tsplit(rgb) C_1 = r - g C_2 = g - b C_3 = b - r C = tstack((C_1, C_2, C_3)) return C
def exponential_factors(RGB_o): """ Returns the chromatic adaptation exponential factors :math:`\\beta_1(R_o)`, :math:`\\beta_1(G_o)` and :math:`\\beta_2(B_o)` of given cone responses. Parameters ---------- RGB_o: array_like Cone responses. Returns ------- ndarray Chromatic adaptation exponential factors :math:`\\beta_1(R_o)`, :math:`\\beta_1(G_o)` and :math:`\\beta_2(B_o)`. Examples -------- >>> RGB_o = np.array([318.32331631, 318.30352317, 318.23283482]) >>> exponential_factors(RGB_o) # doctest: +ELLIPSIS array([ 4.6106222..., 4.6105892..., 4.6520698...]) """ R_o, G_o, B_o = tsplit(RGB_o) bR_o = beta_1(R_o) bG_o = beta_1(G_o) bB_o = beta_2(B_o) bRGB_o = tstack((bR_o, bG_o, bB_o)) return bRGB_o
def test_range(self): """ Tests :func:`colour.continuous.multi_signal.MultiSignal.range` property. """ multi_signal = self._multi_signal.copy() np.testing.assert_almost_equal( multi_signal[np.array([0, 1, 2])], np.array([[10.0, 20.0, 30.0], [20.0, 30.0, 40.0], [30.0, 40.0, 50.0]]), decimal=7) multi_signal.range = self._range_1 * 10 np.testing.assert_array_equal(multi_signal.range, tstack([self._range_1] * 3) * 10) np.testing.assert_almost_equal( multi_signal[np.array([0, 1, 2])], np.array([[10.0, 10.0, 10.0], [20.0, 20.0, 20.0], [30.0, 30.0, 30.0]]) * 10, decimal=7) multi_signal.range = self._range_2 * 10 np.testing.assert_array_equal(multi_signal.range, self._range_2 * 10) np.testing.assert_almost_equal( multi_signal[np.array([0, 1, 2])], np.array([[10.0, 20.0, 30.0], [20.0, 30.0, 40.0], [30.0, 40.0, 50.0]]) * 10, decimal=7)
def LCHuv_to_Luv(LCHuv): """ Converts from *CIE LCHuv* colourspace to *CIE Luv* colourspace. Parameters ---------- LCHuv : array_like *CIE LCHuv* colourspace array. Returns ------- ndarray *CIE Luv* colourspace array. Notes ----- - Input / output :math:`L^*` is in domain / range [0, 100]. References ---------- .. [7] Lindbloom, B. (2006). LCH(uv) to Luv. Retrieved February 24, 2014, from http://www.brucelindbloom.com/Eqn_LCH_to_Luv.html Examples -------- >>> LCHuv = np.array([37.98562910, 28.83419279, 182.69946404]) >>> LCHuv_to_Luv(LCHuv) # doctest: +ELLIPSIS array([ 37.9856291..., -28.8021959..., -1.3580070...]) """ L, C, H = tsplit(LCHuv) Luv = tstack((L, C * np.cos(np.radians(H)), C * np.sin(np.radians(H)))) return Luv
def spherical_to_cartesian(a): """ Transforms given Spherical coordinates array to Cartesian coordinates. Parameters ---------- a : array_like Spherical coordinates array (r, theta, phi) to transform. Returns ------- ndarray Cartesian coordinates array (x, y, z). See Also -------- cartesian_to_spherical, cartesian_to_cylindrical, cylindrical_to_cartesian Examples -------- >>> a = np.array([6.78232998, 1.08574654, 0.32175055]) >>> spherical_to_cartesian(a) # doctest: +ELLIPSIS array([ 3. , 0.9999999..., 6. ]) """ r, theta, phi = tsplit(a) x = r * np.cos(theta) * np.cos(phi) y = r * np.cos(theta) * np.sin(phi) z = r * np.sin(theta) xyz = tstack((x, y, z)) return xyz
def UCS_uv_to_xy(uv): """ Returns the *xy* chromaticity coordinates from given *CIE 1960 UCS* colourspace *uv* chromaticity coordinates. Parameters ---------- uv : array_like *CIE UCS uv* chromaticity coordinates. Returns ------- ndarray *xy* chromaticity coordinates. References ---------- :cite:`Wikipedia2008c` Examples -------- >>> uv = np.array([0.37720213, 0.33413508]) >>> UCS_uv_to_xy(uv) # doctest: +ELLIPSIS array([ 0.5436955..., 0.3210794...]) """ u, v = tsplit(uv) d = 2 * u - 8 * v + 4 xy = tstack([3 * u / d, 2 * v / d]) return xy
def intermediate_values(xy_o): """ Returns the intermediate values :math:`\\xi`, :math:`\eta`, :math:`\zeta`. Parameters ---------- xy_o : array_like Chromaticity coordinates :math:`x_o` and :math:`y_o` of whitepoint. Returns ------- ndarray Intermediate values :math:`\\xi`, :math:`\eta`, :math:`\zeta`. Examples -------- >>> xy_o = np.array([0.4476, 0.4074]) >>> intermediate_values(xy_o) # doctest: +ELLIPSIS array([ 1.1185719..., 0.9329553..., 0.3268087...]) """ x_o, y_o = tsplit(xy_o) # Computing :math:`\xi`, :math:`\eta`, :math:`\zeta` values. xi = (0.48105 * x_o + 0.78841 * y_o - 0.08081) / y_o eta = (-0.27200 * x_o + 1.11962 * y_o + 0.04570) / y_o zeta = (0.91822 * (1 - x_o - y_o)) / y_o xez = tstack((xi, eta, zeta)) return xez
def UVW_to_XYZ( UVW, illuminant=ILLUMINANTS['CIE 1931 2 Degree Standard Observer']['D65']): """ Converts *CIE 1964 U\\*V\\*W\\** colourspace to *CIE XYZ* tristimulus values. Parameters ---------- UVW : array_like *CIE 1964 U\\*V\\*W\\** colourspace array. illuminant : array_like, optional Reference *illuminant* *xy* chromaticity coordinates or *CIE xyY* colourspace array. Returns ------- ndarray *CIE XYZ* tristimulus values. Warning ------- The input domain and output range of that definition are non standard! Notes ----- +----------------+-----------------------+-----------------+ | **Domain** | **Scale - Reference** | **Scale - 1** | +================+=======================+=================+ | ``UVW`` | ``U`` : [-100, 100] | ``U`` : [-1, 1] | | | | | | | ``V`` : [-100, 100] | ``V`` : [-1, 1] | | | | | | | ``W`` : [0, 100] | ``W`` : [0, 1] | +----------------+-----------------------+-----------------+ | ``illuminant`` | [0, 1] | [0, 1] | +----------------+-----------------------+-----------------+ +----------------+-----------------------+-----------------+ | **Range** | **Scale - Reference** | **Scale - 1** | +================+=======================+=================+ | ``XYZ`` | [0, 1] | [0, 1] | +----------------+-----------------------+-----------------+ References ---------- :cite:`Wikipedia2008a` Examples -------- >>> import numpy as np >>> UVW = np.array([94.55035725, 11.55536523, 40.54757405]) >>> UVW_to_XYZ(UVW) array([ 20.654008, 12.197225, 5.136952]) """ U, V, W = tsplit(to_domain_100(UVW)) u_0, v_0 = tsplit(xy_to_UCS_uv(xyY_to_xy(illuminant))) Y = ((W + 17) / 25)**3 u = U / (13 * W) + u_0 v = V / (13 * W) + v_0 x, y = tsplit(UCS_uv_to_xy(tstack([u, v]))) XYZ = xyY_to_XYZ(tstack([x, y, Y])) return from_range_100(XYZ)
def XYZ_to_Hunter_Rdab( XYZ, XYZ_n=HUNTERLAB_ILLUMINANTS['CIE 1931 2 Degree Standard Observer'] ['D65'].XYZ_n, K_ab=HUNTERLAB_ILLUMINANTS['CIE 1931 2 Degree Standard Observer'] ['D65'].K_ab): """ Converts from *CIE XYZ* tristimulus values to *Hunter Rd,a,b* colour scale. Parameters ---------- XYZ : array_like *CIE XYZ* tristimulus values. XYZ_n : array_like, optional Reference *illuminant* tristimulus values. K_ab : array_like, optional Reference *illuminant* chromaticity coefficients, if ``K_ab`` is set to *None* it will be computed using :func:`colour.XYZ_to_K_ab_HunterLab1966`. Returns ------- ndarray *Hunter Rd,a,b* colour scale array. Notes ----- +------------+------------------------+--------------------+ | **Domain** | **Scale - Reference** | **Scale - 1** | +============+========================+====================+ | ``XYZ`` | [0, 100] | [0, 1] | +------------+------------------------+--------------------+ | ``XYZ_n`` | [0, 100] | [0, 1] | +------------+------------------------+--------------------+ +------------+------------------------+--------------------+ | **Range** | **Scale - Reference** | **Scale - 1** | +============+========================+====================+ | ``R_d_ab`` | ``R_d`` : [0, 100] | ``R_d`` : [0, 1] | | | | | | | ``a_Rd`` : [-100, 100] | ``a_Rd`` : [-1, 1] | | | | | | | ``b_Rd`` : [-100, 100] | ``b_Rd`` : [-1, 1] | +------------+------------------------+--------------------+ References ---------- :cite:`HunterLab2012a` Examples -------- >>> import numpy as np >>> XYZ = np.array([0.20654008, 0.12197225, 0.05136952]) * 100 >>> D65 = HUNTERLAB_ILLUMINANTS[ ... 'CIE 1931 2 Degree Standard Observer']['D65'] >>> XYZ_to_Hunter_Rdab(XYZ, D65.XYZ_n, D65.K_ab) ... # doctest: +ELLIPSIS array([ 12.197225 ..., 57.1253787..., 17.4624134...]) """ X, Y, Z = tsplit(to_domain_100(XYZ)) X_n, Y_n, Z_n = tsplit(to_domain_100(XYZ_n)) K_a, K_b = (tsplit(XYZ_to_K_ab_HunterLab1966(XYZ_n)) if K_ab is None else tsplit(K_ab)) f = 0.51 * ((21 + 0.2 * Y) / (1 + 0.2 * Y)) Y_Yn = Y / Y_n R_d = Y a_Rd = K_a * f * (X / X_n - Y_Yn) b_Rd = K_b * f * (Y_Yn - Z / Z_n) R_d_ab = tstack([R_d, a_Rd, b_Rd]) return from_range_100(R_d_ab)
def DIN99_to_Lab(Lab_99, k_E=1, k_CH=1): """ Converts from *DIN99* colourspace to *CIE L\\*a\\*b\\** colourspace. Parameters ---------- Lab_99 : array_like *DIN99* colourspace array. k_E : numeric, optional Parametric factor :math:`K_E` used to compensate for texture and other specimen presentation effects. k_CH : numeric, optional Parametric factor :math:`K_{CH}` used to compensate for texture and other specimen presentation effects. Returns ------- ndarray *CIE L\\*a\\*b\\** colourspace array. Notes ----- +------------+------------------------+--------------------+ | **Domain** | **Scale - Reference** | **Scale - 1** | +============+========================+====================+ | ``Lab_99`` | ``L_99`` : [0, 100] | ``L_99`` : [0, 1] | | | | | | | ``a_99`` : [-100, 100] | ``a_99`` : [-1, 1] | | | | | | | ``b_99`` : [-100, 100] | ``b_99`` : [-1, 1] | +------------+------------------------+--------------------+ +------------+------------------------+--------------------+ | **Range** | **Scale - Reference** | **Scale - 1** | +============+========================+====================+ | ``Lab`` | ``L`` : [0, 100] | ``L`` : [0, 1] | | | | | | | ``a`` : [-100, 100] | ``a`` : [-1, 1] | | | | | | | ``b`` : [-100, 100] | ``b`` : [-1, 1] | +------------+------------------------+--------------------+ References ---------- :cite:`ASTMInternational2007` Examples -------- >>> import numpy as np >>> Lab_99 = np.array([53.22821988, 28.41634656, 3.89839552]) >>> DIN99_to_Lab(Lab_99) # doctest: +ELLIPSIS array([ 41.5278752..., 52.6385830..., 26.9231792...]) """ L_99, a_99, b_99 = tsplit(to_domain_100(Lab_99)) cos_16 = np.cos(np.radians(16)) sin_16 = np.sin(np.radians(16)) h_99 = np.arctan2(b_99, a_99) C_99 = np.sqrt(a_99**2 + b_99**2) G = (np.exp(0.045 * C_99 * k_CH * k_E) - 1) / 0.045 e = G * np.cos(h_99) f = G * np.sin(h_99) a = e * cos_16 - (f / 0.7) * sin_16 b = e * sin_16 + (f / 0.7) * cos_16 L = (np.exp(L_99 * k_E / 105.509) - 1) / 0.0158 Lab = tstack([L, a, b]) return from_range_100(Lab)
def read_LUT_ResolveCube(path): """ Reads given *Resolve* *.cube* *LUT* file. Parameters ---------- path : unicode *LUT* path. Returns ------- LUT2D or LUT3D or LUTSequence :class:`LUT2D` or :class:`LUT3D` or :class:`LUTSequence` class instance. References ---------- :cite:`Chamberlain2015` Examples -------- Reading a 2D *Resolve* *.cube* *LUT*: >>> import os >>> path = os.path.join( ... os.path.dirname(__file__), 'tests', 'resources', 'resolve_cube', ... 'ACES_Proxy_10_to_ACES.cube') >>> print(read_LUT_ResolveCube(path)) LUT2D - ACES Proxy 10 to ACES ----------------------------- <BLANKLINE> Dimensions : 2 Domain : [[ 0. 0. 0.] [ 1. 1. 1.]] Size : (32, 3) Reading a 3D *Resolve* *.cube* *LUT*: >>> path = os.path.join( ... os.path.dirname(__file__), 'tests', 'resources', 'resolve_cube', ... 'ColourCorrect.cube') >>> print(read_LUT_ResolveCube(path)) LUT3D - Generated by Foundry::LUT --------------------------------- <BLANKLINE> Dimensions : 3 Domain : [[ 0. 0. 0.] [ 1. 1. 1.]] Size : (4, 4, 4, 3) Reading a 3D *Resolve* *.cube* *LUT* with comments: >>> path = os.path.join( ... os.path.dirname(__file__), 'tests', 'resources', 'resolve_cube', ... 'Demo.cube') >>> print(read_LUT_ResolveCube(path)) LUT2D - Demo ------------ <BLANKLINE> Dimensions : 2 Domain : [[ 0. 0. 0.] [ 3. 3. 3.]] Size : (3, 3) Comment 01 : Comments can't go anywhere """ title = path_to_title(path) size_2D = size_3D = 2 table = [] comments = [] has_2D, has_3D = False, False with open(path) as cube_file: lines = cube_file.readlines() LUT = LUTSequence(LUT2D(), LUT3D()) for line in lines: line = line.strip() if len(line) == 0: continue if line.startswith('#'): comments.append(line[1:].strip()) continue tokens = line.split() if tokens[0] == 'TITLE': title = ' '.join(tokens[1:])[1:-1] elif tokens[0] == 'LUT_1D_INPUT_RANGE': domain = parse_array(tokens[1:]) LUT[0].domain = tstack([domain, domain, domain]) elif tokens[0] == 'LUT_3D_INPUT_RANGE': domain = parse_array(tokens[1:]) LUT[1].domain = tstack([domain, domain, domain]) elif tokens[0] == 'LUT_1D_SIZE': has_2D = True size_2D = np.int_(tokens[1]) elif tokens[0] == 'LUT_3D_SIZE': has_3D = True size_3D = np.int_(tokens[1]) else: table.append(parse_array(tokens)) table = as_float_array(table) if has_2D and has_3D: LUT[0].name = '{0} - Shaper'.format(title) LUT[1].name = '{0} - Cube'.format(title) LUT[1].comments = comments LUT[0].table = table[:size_2D] # The lines of table data shall be in ascending index order, # with the first component index (Red) changing most rapidly, # and the last component index (Blue) changing least rapidly. LUT[1].table = table[size_2D:].reshape((size_3D, size_3D, size_3D, 3), order='F') return LUT elif has_2D: LUT[0].name = title LUT[0].comments = comments LUT[0].table = table return LUT[0] elif has_3D: LUT[1].name = title LUT[1].comments = comments # The lines of table data shall be in ascending index order, # with the first component index (Red) changing most rapidly, # and the last component index (Blue) changing least rapidly. table = table.reshape([size_3D, size_3D, size_3D, 3], order='F') LUT[1].table = table return LUT[1]
def XYZ_to_JzAzBz(XYZ_D65, constants=CONSTANTS_JZAZBZ): """ Converts from *CIE XYZ* tristimulus values to :math:`J_zA_zB_z` colourspace. Parameters ---------- XYZ_D65 : array_like *CIE XYZ* tristimulus values under *CIE Standard Illuminant D Series D65*. constants : Structure, optional :math:`J_zA_zB_z` colourspace constants. Returns ------- ndarray :math:`J_zA_zB_z` colourspace array where :math:`J_z` is Lightness, :math:`A_z` is redness-greenness and :math:`B_z` is yellowness-blueness. Warnings -------- The underlying *SMPTE ST 2084:2014* transfer function is an absolute transfer function. Notes ----- - The underlying *SMPTE ST 2084:2014* transfer function is an absolute transfer function, thus the domain and range values for the *Reference* and *1* scales are only indicative that the data is not affected by scale transformations. The effective domain of *SMPTE ST 2084:2014* inverse electro-optical transfer function (EOTF / EOCF) is [0.0001, 10000]. +------------+-----------------------+------------------+ | **Domain** | **Scale - Reference** | **Scale - 1** | +============+=======================+==================+ | ``XYZ`` | ``UN`` | ``UN`` | +------------+-----------------------+------------------+ +------------+-----------------------+------------------+ | **Range** | **Scale - Reference** | **Scale - 1** | +============+=======================+==================+ | ``JzAzBz`` | ``Jz`` : [0, 1] | ``Jz`` : [0, 1] | | | | | | | ``Az`` : [-1, 1] | ``Az`` : [-1, 1] | | | | | | | ``Bz`` : [-1, 1] | ``Bz`` : [-1, 1] | +------------+-----------------------+------------------+ References ---------- :cite:`Safdar2017` Examples -------- >>> XYZ = np.array([0.20654008, 0.12197225, 0.05136952]) >>> XYZ_to_JzAzBz(XYZ) # doctest: +ELLIPSIS array([ 0.0053504..., 0.0092430..., 0.0052600...]) """ X_D65, Y_D65, Z_D65 = tsplit(to_domain_1(XYZ_D65)) X_p_D65 = constants.b * X_D65 - (constants.b - 1) * Z_D65 Y_p_D65 = constants.g * Y_D65 - (constants.g - 1) * X_D65 XYZ_p_D65 = tstack([X_p_D65, Y_p_D65, Z_D65]) LMS = vector_dot(MATRIX_JZAZBZ_XYZ_TO_LMS, XYZ_p_D65) with domain_range_scale('ignore'): LMS_p = eotf_inverse_ST2084(LMS, 10000, constants) I_z, A_z, B_z = tsplit(vector_dot(MATRIX_JZAZBZ_LMS_P_TO_IZAZBZ, LMS_p)) J_z = ((1 + constants.d) * I_z) / (1 + constants.d * I_z) - constants.d_0 JzAzBz = tstack([J_z, A_z, B_z]) return from_range_1(JzAzBz)
def RGB_to_HSL(RGB): """ Converts from *RGB* colourspace to *HSL* colourspace. Parameters ---------- RGB : array_like *RGB* colourspace array. Returns ------- ndarray *HSL* array. Notes ----- +------------+-----------------------+---------------+ | **Domain** | **Scale - Reference** | **Scale - 1** | +============+=======================+===============+ | ``RGB`` | [0, 1] | [0, 1] | +------------+-----------------------+---------------+ +------------+-----------------------+---------------+ | **Range** | **Scale - Reference** | **Scale - 1** | +============+=======================+===============+ | ``HSL`` | [0, 1] | [0, 1] | +------------+-----------------------+---------------+ References ---------- :cite:`EasyRGBl`, :cite:`Smith1978b`, :cite:`Wikipedia2003` Examples -------- >>> RGB = np.array([0.45620519, 0.03081071, 0.04091952]) >>> RGB_to_HSL(RGB) # doctest: +ELLIPSIS array([ 0.9960394..., 0.8734714..., 0.2435079...]) """ RGB = to_domain_1(RGB) minimum = np.amin(RGB, -1) maximum = np.amax(RGB, -1) delta = np.ptp(RGB, -1) R, G, B = tsplit(RGB) L = (maximum + minimum) / 2 S = np.where( L < 0.5, delta / (maximum + minimum), delta / (2 - maximum - minimum), ) S[np.asarray(delta == 0)] = 0 delta_R = (((maximum - R) / 6) + (delta / 2)) / delta delta_G = (((maximum - G) / 6) + (delta / 2)) / delta delta_B = (((maximum - B) / 6) + (delta / 2)) / delta H = delta_B - delta_G H = np.where(G == maximum, (1 / 3) + delta_R - delta_B, H) H = np.where(B == maximum, (2 / 3) + delta_G - delta_R, H) H[np.asarray(H < 0)] += 1 H[np.asarray(H > 1)] -= 1 H[np.asarray(delta == 0)] = 0 HSL = tstack([H, S, L]) return from_range_1(HSL)
def Hunter_Rdab_to_XYZ( R_d_ab, XYZ_n=HUNTERLAB_ILLUMINANTS['CIE 1931 2 Degree Standard Observer'] ['D65'].XYZ_n, K_ab=HUNTERLAB_ILLUMINANTS['CIE 1931 2 Degree Standard Observer'] ['D65'].K_ab): """ Converts from *Hunter Rd,a,b* colour scale to *CIE XYZ* tristimulus values. Parameters ---------- R_d_ab : array_like *Hunter Rd,a,b* colour scale array. XYZ_n : array_like, optional Reference *illuminant* tristimulus values. K_ab : array_like, optional Reference *illuminant* chromaticity coefficients, if ``K_ab`` is set to *None* it will be computed using :func:`colour.XYZ_to_K_ab_HunterLab1966`. Returns ------- ndarray *CIE XYZ* tristimulus values. Notes ----- +------------+------------------------+--------------------+ | **Domain** | **Scale - Reference** | **Scale - 1** | +============+========================+====================+ | ``R_d_ab`` | ``R_d`` : [0, 100] | ``R_d`` : [0, 1] | | | | | | | ``a_Rd`` : [-100, 100] | ``a_Rd`` : [-1, 1] | | | | | | | ``b_Rd`` : [-100, 100] | ``b_Rd`` : [-1, 1] | +------------+------------------------+--------------------+ | ``XYZ_n`` | [0, 100] | [0, 1] | +------------+------------------------+--------------------+ +------------+------------------------+--------------------+ | **Range** | **Scale - Reference** | **Scale - 1** | +============+========================+====================+ | ``XYZ`` | [0, 100] | [0, 1] | +------------+------------------------+--------------------+ References ---------- :cite:`HunterLab2012a` Examples -------- >>> import numpy as np >>> R_d_ab = np.array([12.19722500, 57.12537874, 17.46241341]) >>> D65 = HUNTERLAB_ILLUMINANTS[ ... 'CIE 1931 2 Degree Standard Observer']['D65'] >>> Hunter_Rdab_to_XYZ(R_d_ab, D65.XYZ_n, D65.K_ab) array([ 20.654008, 12.197225, 5.136952]) """ R_d, a_Rd, b_Rd = tsplit(to_domain_100(R_d_ab)) X_n, Y_n, Z_n = tsplit(to_domain_100(XYZ_n)) K_a, K_b = (tsplit(XYZ_to_K_ab_HunterLab1966(XYZ_n)) if K_ab is None else tsplit(K_ab)) f = 0.51 * ((21 + 0.2 * R_d) / (1 + 0.2 * R_d)) Rd_Yn = R_d / Y_n X = (a_Rd / (K_a * f) + Rd_Yn) * X_n Z = -(b_Rd / (K_b * f) - Rd_Yn) * Z_n XYZ = tstack([X, R_d, Z]) return from_range_100(XYZ)
def HSV_to_RGB(HSV): """ Converts from *HSV* colourspace to *RGB* colourspace. Parameters ---------- HSV : array_like *HSV* colourspace array. Returns ------- ndarray *RGB* colourspace array. Notes ----- +------------+-----------------------+---------------+ | **Domain** | **Scale - Reference** | **Scale - 1** | +============+=======================+===============+ | ``HSV`` | [0, 1] | [0, 1] | +------------+-----------------------+---------------+ +------------+-----------------------+---------------+ | **Range** | **Scale - Reference** | **Scale - 1** | +============+=======================+===============+ | ``RGB`` | [0, 1] | [0, 1] | +------------+-----------------------+---------------+ References ---------- :cite:`EasyRGBn`, :cite:`Smith1978b`, :cite:`Wikipedia2003` Examples -------- >>> HSV = np.array([0.99603944, 0.93246304, 0.45620519]) >>> HSV_to_RGB(HSV) # doctest: +ELLIPSIS array([ 0.4562051..., 0.0308107..., 0.0409195...]) """ H, S, V = tsplit(to_domain_1(HSV)) h = as_float_array(H * 6) h[np.asarray(h == 6)] = 0 i = np.floor(h) j = V * (1 - S) k = V * (1 - S * (h - i)) l = V * (1 - S * (1 - (h - i))) # noqa i = tstack([i, i, i]).astype(np.uint8) RGB = np.choose(i, [ tstack([V, l, j]), tstack([k, V, j]), tstack([j, V, l]), tstack([j, k, V]), tstack([l, j, V]), tstack([V, j, k]), ], mode='clip') return from_range_1(RGB)
def XYZ_to_UVW( XYZ, illuminant=ILLUMINANTS['CIE 1931 2 Degree Standard Observer']['D65']): """ Converts from *CIE XYZ* tristimulus values to *CIE 1964 U\\*V\\*W\\** colourspace. Parameters ---------- XYZ : array_like *CIE XYZ* tristimulus values. illuminant : array_like, optional Reference *illuminant* *xy* chromaticity coordinates or *CIE xyY* colourspace array. Returns ------- ndarray *CIE 1964 U\\*V\\*W\\** colourspace array. Warning ------- The input domain and output range of that definition are non standard! Notes ----- +----------------+-----------------------+-----------------+ | **Domain** | **Scale - Reference** | **Scale - 1** | +================+=======================+=================+ | ``XYZ`` | [0, 1] | [0, 1] | +----------------+-----------------------+-----------------+ | ``illuminant`` | [0, 1] | [0, 1] | +----------------+-----------------------+-----------------+ +----------------+-----------------------+-----------------+ | **Range** | **Scale - Reference** | **Scale - 1** | +================+=======================+=================+ | ``UVW`` | ``U`` : [-100, 100] | ``U`` : [-1, 1] | | | | | | | ``V`` : [-100, 100] | ``V`` : [-1, 1] | | | | | | | ``W`` : [0, 100] | ``W`` : [0, 1] | +----------------+-----------------------+-----------------+ References ---------- :cite:`Wikipedia2008a` Examples -------- >>> import numpy as np >>> XYZ = np.array([0.20654008, 0.12197225, 0.05136952]) * 100 >>> XYZ_to_UVW(XYZ) # doctest: +ELLIPSIS array([ 94.5503572..., 11.5553652..., 40.5475740...]) """ XYZ = to_domain_100(XYZ) xy = xyY_to_xy(illuminant) xyY = XYZ_to_xyY(XYZ, xy) _x, _y, Y = tsplit(xyY) u, v = tsplit(UCS_to_uv(XYZ_to_UCS(XYZ))) u_0, v_0 = tsplit(xy_to_UCS_uv(xy)) W = 25 * spow(Y, 1 / 3) - 17 U = 13 * W * (u - u_0) V = 13 * W * (v - v_0) UVW = tstack([U, V, W]) return from_range_100(UVW)
def XYZ_to_Luv( XYZ, illuminant=ILLUMINANTS['CIE 1931 2 Degree Standard Observer']['D65']): """ Converts from *CIE XYZ* tristimulus values to *CIE L\\*u\\*v\\** colourspace. Parameters ---------- XYZ : array_like *CIE XYZ* tristimulus values. illuminant : array_like, optional Reference *illuminant* *CIE xy* chromaticity coordinates or *CIE xyY* colourspace array. Returns ------- ndarray *CIE L\\*u\\*v\\** colourspace array. Notes ----- +----------------+-----------------------+-----------------+ | **Domain** | **Scale - Reference** | **Scale - 1** | +================+=======================+=================+ | ``XYZ`` | [0, 1] | [0, 1] | +----------------+-----------------------+-----------------+ | ``illuminant`` | [0, 1] | [0, 1] | +----------------+-----------------------+-----------------+ +----------------+-----------------------+-----------------+ | **Range** | **Scale - Reference** | **Scale - 1** | +================+=======================+=================+ | ``Luv`` | ``L`` : [0, 100] | ``L`` : [0, 1] | | | | | | | ``u`` : [-100, 100] | ``u`` : [-1, 1] | | | | | | | ``v`` : [-100, 100] | ``v`` : [-1, 1] | +----------------+-----------------------+-----------------+ References ---------- :cite:`CIETC1-482004m`, :cite:`Wikipedia2007b` Examples -------- >>> XYZ = np.array([0.20654008, 0.12197225, 0.05136952]) >>> XYZ_to_Luv(XYZ) # doctest: +ELLIPSIS array([ 41.5278752..., 96.8362605..., 17.7521014...]) """ X, Y, Z = tsplit(to_domain_1(XYZ)) X_r, Y_r, Z_r = tsplit(xyY_to_XYZ(xy_to_xyY(illuminant))) with domain_range_scale('100'): L = lightness_CIE1976(Y, Y_r) u = (13 * L * ((4 * X / (X + 15 * Y + 3 * Z)) - (4 * X_r / (X_r + 15 * Y_r + 3 * Z_r)))) v = (13 * L * ((9 * Y / (X + 15 * Y + 3 * Z)) - (9 * Y_r / (X_r + 15 * Y_r + 3 * Z_r)))) Luv = tstack([L, u, v]) return from_range_100(Luv)
def Luv_to_XYZ( Luv, illuminant=ILLUMINANTS['CIE 1931 2 Degree Standard Observer']['D65']): """ Converts from *CIE L\\*u\\*v\\** colourspace to *CIE XYZ* tristimulus values. Parameters ---------- Luv : array_like *CIE L\\*u\\*v\\** colourspace array. illuminant : array_like, optional Reference *illuminant* *CIE xy* chromaticity coordinates or *CIE xyY* colourspace array. Returns ------- ndarray *CIE XYZ* tristimulus values. Notes ----- +----------------+-----------------------+-----------------+ | **Domain** | **Scale - Reference** | **Scale - 1** | +================+=======================+=================+ | ``Luv`` | ``L`` : [0, 100] | ``L`` : [0, 1] | | | | | | | ``u`` : [-100, 100] | ``u`` : [-1, 1] | | | | | | | ``v`` : [-100, 100] | ``v`` : [-1, 1] | +----------------+-----------------------+-----------------+ | ``illuminant`` | [0, 1] | [0, 1] | +----------------+-----------------------+-----------------+ +----------------+-----------------------+-----------------+ | **Range** | **Scale - Reference** | **Scale - 1** | +================+=======================+=================+ | ``XYZ`` | [0, 1] | [0, 1] | +----------------+-----------------------+-----------------+ References ---------- :cite:`CIETC1-482004m`, :cite:`Wikipedia2007b` Examples -------- >>> Luv = np.array([41.52787529, 96.83626054, 17.75210149]) >>> Luv_to_XYZ(Luv) # doctest: +ELLIPSIS array([ 0.2065400..., 0.1219722..., 0.0513695...]) """ L, u, v = tsplit(to_domain_100(Luv)) X_r, Y_r, Z_r = tsplit(xyY_to_XYZ(xy_to_xyY(illuminant))) with domain_range_scale('100'): Y = luminance_CIE1976(L, Y_r) a = 1 / 3 * ((52 * L / (u + 13 * L * (4 * X_r / (X_r + 15 * Y_r + 3 * Z_r)))) - 1) b = -5 * Y c = -1 / 3.0 d = Y * (39 * L / (v + 13 * L * (9 * Y_r / (X_r + 15 * Y_r + 3 * Z_r))) - 5) X = (d - b) / (a - c) Z = X * a + b XYZ = tstack([X, Y, Z]) return from_range_1(XYZ)
def JzAzBz_to_XYZ(JzAzBz, constants=CONSTANTS_JZAZBZ): """ Converts from :math:`J_zA_zB_z` colourspace to *CIE XYZ* tristimulus values. Parameters ---------- JzAzBz : array_like :math:`J_zA_zB_z` colourspace array where :math:`J_z` is Lightness, :math:`A_z` is redness-greenness and :math:`B_z` is yellowness-blueness. constants : Structure, optional :math:`J_zA_zB_z` colourspace constants. Returns ------- ndarray *CIE XYZ* tristimulus values under *CIE Standard Illuminant D Series D65*. Warnings -------- The underlying *SMPTE ST 2084:2014* transfer function is an absolute transfer function. Notes ----- - The underlying *SMPTE ST 2084:2014* transfer function is an absolute transfer function, thus the domain and range values for the *Reference* and *1* scales are only indicative that the data is not affected by scale transformations. +------------+-----------------------+------------------+ | **Domain** | **Scale - Reference** | **Scale - 1** | +============+=======================+==================+ | ``JzAzBz`` | ``Jz`` : [0, 1] | ``Jz`` : [0, 1] | | | | | | | ``Az`` : [-1, 1] | ``Az`` : [-1, 1] | | | | | | | ``Bz`` : [-1, 1] | ``Bz`` : [-1, 1] | +------------+-----------------------+------------------+ +------------+-----------------------+------------------+ | **Range** | **Scale - Reference** | **Scale - 1** | +============+=======================+==================+ | ``XYZ`` | ``UN`` | ``UN`` | +------------+-----------------------+------------------+ References ---------- :cite:`Safdar2017` Examples -------- >>> JzAzBz = np.array([0.00535048, 0.00924302, 0.00526007]) >>> JzAzBz_to_XYZ(JzAzBz) # doctest: +ELLIPSIS array([ 0.2065402..., 0.1219723..., 0.0513696...]) """ J_z, A_z, B_z = tsplit(to_domain_1(JzAzBz)) I_z = ((J_z + constants.d_0) / (1 + constants.d - constants.d * (J_z + constants.d_0))) LMS_p = vector_dot(MATRIX_JZAZBZ_IZAZBZ_TO_LMS_P, tstack([I_z, A_z, B_z])) with domain_range_scale('ignore'): LMS = eotf_ST2084(LMS_p, 10000, constants) X_p_D65, Y_p_D65, Z_p_D65 = tsplit( vector_dot(MATRIX_JZAZBZ_LMS_TO_XYZ, LMS)) X_D65 = (X_p_D65 + (constants.b - 1) * Z_p_D65) / constants.b Y_D65 = (Y_p_D65 + (constants.g - 1) * X_D65) / constants.g XYZ_D65 = tstack([X_D65, Y_D65, Z_p_D65]) return from_range_1(XYZ_D65)
def whiteness_CIE2004( xy: ArrayLike, Y: FloatingOrNDArray, xy_n: ArrayLike, observer: Literal["CIE 1931 2 Degree Standard Observer", "CIE 1964 10 Degree Standard Observer", ] = ( "CIE 1931 2 Degree Standard Observer"), ) -> NDArray: """ Return the *whiteness* :math:`W` or :math:`W_{10}` and *tint* :math:`T` or :math:`T_{10}` of given sample *CIE xy* chromaticity coordinates using *CIE 2004* method. Parameters ---------- xy Chromaticity coordinates *CIE xy* of the sample. Y Tristimulus :math:`Y` value of the sample. xy_n Chromaticity coordinates *xy_n* of a perfect diffuser. observer *CIE Standard Observer* used for computations, *tint* :math:`T` or :math:`T_{10}` value is dependent on viewing field angular subtense. Returns ------- :class:`numpy.ndarray` *Whiteness* :math:`W` or :math:`W_{10}` and *tint* :math:`T` or :math:`T_{10}` of given sample. Notes ----- +------------+-----------------------+---------------+ | **Domain** | **Scale - Reference** | **Scale - 1** | +============+=======================+===============+ | ``Y`` | [0, 100] | [0, 1] | +------------+-----------------------+---------------+ +------------+-----------------------+---------------+ | **Range** | **Scale - Reference** | **Scale - 1** | +============+=======================+===============+ | ``WT`` | [0, 100] | [0, 1] | +------------+-----------------------+---------------+ - This method may be used only for samples whose values of :math:`W` or :math:`W_{10}` lie within the following limits: greater than 40 and less than 5Y - 280, or 5Y10 - 280. - This method may be used only for samples whose values of :math:`T` or :math:`T_{10}` lie within the following limits: greater than -4 and less than +2. - Output *whiteness* :math:`W` or :math:`W_{10}` values larger than 100 indicate a bluish white while values smaller than 100 indicate a yellowish white. - Positive output *tint* :math:`T` or :math:`T_{10}` values indicate a greener tint while negative values indicate a redder tint. References ---------- :cite:`CIETC1-482004k` Examples -------- >>> import numpy as np >>> xy = np.array([0.3167, 0.3334]) >>> xy_n = np.array([0.3139, 0.3311]) >>> whiteness_CIE2004(xy, 100, xy_n) # doctest: +ELLIPSIS array([ 93.85..., -1.305...]) """ x, y = tsplit(xy) Y = to_domain_100(Y) x_n, y_n = tsplit(xy_n) W = Y + 800 * (x_n - x) + 1700 * (y_n - y) T = (1000 if "1931" in observer else 900) * (x_n - x) - 650 * (y_n - y) WT = tstack([W, T]) return from_range_100(WT)
def polynomial_expansion_Finlayson2015(RGB, degree=1, root_polynomial_expansion=True): """ Performs polynomial expansion of given *RGB* colourspace array using *Finlayson et al. (2015)* method. Parameters ---------- RGB : array_like *RGB* colourspace array to expand. degree : int, optional Expanded polynomial degree. root_polynomial_expansion : bool Whether to use the root-polynomials set for the expansion. Returns ------- ndarray Expanded *RGB* colourspace array. References ---------- :cite:`Finlayson2015` Examples -------- >>> RGB = np.array([0.17224810, 0.09170660, 0.06416938]) >>> polynomial_expansion_Finlayson2015(RGB, degree=2) # doctest: +ELLIPSIS array([ 0.1722481..., 0.0917066..., 0.06416938...\ , 0.0078981..., 0.0029423..., 0.0055265...]) """ R, G, B = tsplit(RGB) # TODO: Generalise polynomial expansion. existing_degrees = np.array([1, 2, 3, 4]) closest_degree = as_int(closest(existing_degrees, degree)) if closest_degree != degree: raise ValueError('"Finlayson et al. (2015)" method does not define ' 'a polynomial expansion for {0} degree, ' 'closest polynomial expansion is {1} degree!'.format( degree, closest_degree)) if degree == 1: return RGB elif degree == 2: if root_polynomial_expansion: return tstack([ R, G, B, (R * G) ** 1 / 2, (G * B) ** 1 / 2, (R * B) ** 1 / 2 ]) else: return tstack( [R, G, B, R ** 2, G ** 2, B ** 2, R * G, G * B, R * B]) elif degree == 3: if root_polynomial_expansion: return tstack([ R, G, B, (R * G) ** 1 / 2, (G * B) ** 1 / 2, (R * B) ** 1 / 2, (R * G ** 2) ** 1 / 3, (G * B ** 2) ** 1 / 3, (R * B ** 2) ** 1 / 3, (G * R ** 2) ** 1 / 3, (B * G ** 2) ** 1 / 3, (B * R ** 2) ** 1 / 3, (R * G * B) ** 1 / 3 ]) else: return tstack([ R, G, B, R ** 2, G ** 2, B ** 2, R * G, G * B, R * B, R ** 3, G ** 3, B ** 3, R * G ** 2, G * B ** 2, R * B ** 2, G * R ** 2, B * G ** 2, B * R ** 2, R * G * B ]) elif degree == 4: if root_polynomial_expansion: return tstack([ R, G, B, (R * G) ** 1 / 2, (G * B) ** 1 / 2, (R * B) ** 1 / 2, (R * G ** 2) ** 1 / 3, (G * B ** 2) ** 1 / 3, (R * B ** 2) ** 1 / 3, (G * R ** 2) ** 1 / 3, (B * G ** 2) ** 1 / 3, (B * R ** 2) ** 1 / 3, (R * G * B) ** 1 / 3, (R ** 3 * G) ** 1 / 4, (R ** 3 * B) ** 1 / 4, (G ** 3 * R) ** 1 / 4, (G ** 3 * B) ** 1 / 4, (B ** 3 * R) ** 1 / 4, (B ** 3 * G) ** 1 / 4, (R ** 2 * G * B) ** 1 / 4, (G ** 2 * R * B) ** 1 / 4, (B ** 2 * R * G) ** 1 / 4 ]) else: return tstack([ R, G, B, R ** 2, G ** 2, B ** 2, R * G, G * B, R * B, R ** 3, G ** 3, B ** 3, R * G ** 2, G * B ** 2, R * B ** 2, G * R ** 2, B * G ** 2, B * R ** 2, R * G * B, R ** 4, G ** 4, B ** 4, R ** 3 * G, R ** 3 * B, G ** 3 * R, G ** 3 * B, B ** 3 * R, B ** 3 * G, R ** 2 * G ** 2, G ** 2 * B ** 2, R ** 2 * B ** 2, R ** 2 * G * B, G ** 2 * R * B, B ** 2 * R * G ])
def HSL_to_RGB(HSL): """ Converts from *HSL* colourspace to *RGB* colourspace. Parameters ---------- HSL : array_like *HSL* colourspace array. Returns ------- ndarray *RGB* colourspace array. Notes ----- +------------+-----------------------+---------------+ | **Domain** | **Scale - Reference** | **Scale - 1** | +============+=======================+===============+ | ``HSL`` | [0, 1] | [0, 1] | +------------+-----------------------+---------------+ +------------+-----------------------+---------------+ | **Range** | **Scale - Reference** | **Scale - 1** | +============+=======================+===============+ | ``RGB`` | [0, 1] | [0, 1] | +------------+-----------------------+---------------+ References ---------- :cite:`EasyRGBk`, :cite:`Smith1978b`, :cite:`Wikipedia2003` Examples -------- >>> HSL = np.array([0.99603944, 0.87347144, 0.24350795]) >>> HSL_to_RGB(HSL) # doctest: +ELLIPSIS array([ 0.4562051..., 0.0308107..., 0.0409195...]) """ H, S, L = tsplit(to_domain_1(HSL)) def H_to_RGB(vi, vj, vH): """ Converts *hue* value to *RGB* colourspace. """ vH = as_float_array(vH) vH[np.asarray(vH < 0)] += 1 vH[np.asarray(vH > 1)] -= 1 v = np.full(vi.shape, np.nan) v = np.where( np.logical_and(6 * vH < 1, np.isnan(v)), vi + (vj - vi) * 6 * vH, v, ) v = np.where(np.logical_and(2 * vH < 1, np.isnan(v)), vj, v) v = np.where( np.logical_and(3 * vH < 2, np.isnan(v)), vi + (vj - vi) * ((2 / 3) - vH) * 6, v, ) v = np.where(np.isnan(v), vi, v) return v j = np.where(L < 0.5, L * (1 + S), (L + S) - (S * L)) i = 2 * L - j R = H_to_RGB(i, j, H + (1 / 3)) G = H_to_RGB(i, j, H) B = H_to_RGB(i, j, H - (1 / 3)) R = np.where(S == 1, L, R) G = np.where(S == 1, L, G) B = np.where(S == 1, L, B) RGB = tstack([R, G, B]) return from_range_1(RGB)
def augmented_matrix_Cheung2004(RGB, terms=3): """ Performs polynomial expansion of given *RGB* colourspace array using *Cheung et al. (2004)* method. Parameters ---------- RGB : array_like *RGB* colourspace array to expand. terms : int, optional Number of terms of the expanded polynomial, must be one of *[3, 5, 7, 8, 10, 11, 14, 16, 17, 19, 20, 22]*. Returns ------- ndarray Expanded *RGB* colourspace array. Notes ----- - This definition combines the augmented matrices given in :cite:`Cheung2004` and :cite:`Westland2004`. References ---------- :cite:`Cheung2004`, :cite:`Westland2004` Examples -------- >>> RGB = np.array([0.17224810, 0.09170660, 0.06416938]) >>> augmented_matrix_Cheung2004(RGB, terms=5) # doctest: +ELLIPSIS array([ 0.1722481..., 0.0917066..., 0.0641693..., 0.0010136..., 1...]) """ R, G, B = tsplit(RGB) ones = np.ones(R.shape) existing_terms = np.array([3, 5, 7, 8, 10, 11, 14, 16, 17, 19, 20, 22]) closest_terms = as_int(closest(existing_terms, terms)) if closest_terms != terms: raise ValueError('"Cheung et al. (2004)" method does not define ' 'an augmented matrix with {0} terms, ' 'closest augmented matrix has {1} terms!'.format( terms, closest_terms)) if terms == 3: return RGB elif terms == 5: return tstack([R, G, B, R * G * B, ones]) elif terms == 7: return tstack([R, G, B, R * G, R * B, G * B, ones]) elif terms == 8: return tstack([R, G, B, R * G, R * B, G * B, R * G * B, ones]) elif terms == 10: return tstack( [R, G, B, R * G, R * B, G * B, R ** 2, G ** 2, B ** 2, ones]) elif terms == 11: return tstack([ R, G, B, R * G, R * B, G * B, R ** 2, G ** 2, B ** 2, R * G * B, ones ]) elif terms == 14: return tstack([ R, G, B, R * G, R * B, G * B, R ** 2, G ** 2, B ** 2, R * G * B, R ** 3, G ** 3, B ** 3, ones ]) elif terms == 16: return tstack([ R, G, B, R * G, R * B, G * B, R ** 2, G ** 2, B ** 2, R * G * B, R ** 2 * G, G ** 2 * B, B ** 2 * R, R ** 3, G ** 3, B ** 3 ]) elif terms == 17: return tstack([ R, G, B, R * G, R * B, G * B, R ** 2, G ** 2, B ** 2, R * G * B, R ** 2 * G, G ** 2 * B, B ** 2 * R, R ** 3, G ** 3, B ** 3, ones ]) elif terms == 19: return tstack([ R, G, B, R * G, R * B, G * B, R ** 2, G ** 2, B ** 2, R * G * B, R ** 2 * G, G ** 2 * B, B ** 2 * R, R ** 2 * B, G ** 2 * R, B ** 2 * G, R ** 3, G ** 3, B ** 3 ]) elif terms == 20: return tstack([ R, G, B, R * G, R * B, G * B, R ** 2, G ** 2, B ** 2, R * G * B, R ** 2 * G, G ** 2 * B, B ** 2 * R, R ** 2 * B, G ** 2 * R, B ** 2 * G, R ** 3, G ** 3, B ** 3, ones ]) elif terms == 22: return tstack([ R, G, B, R * G, R * B, G * B, R ** 2, G ** 2, B ** 2, R * G * B, R ** 2 * G, G ** 2 * B, B ** 2 * R, R ** 2 * B, G ** 2 * R, B ** 2 * G, R ** 3, G ** 3, B ** 3, R ** 2 * G * B, R * G ** 2 * B, R * G * B ** 2 ])
def UCS_Luo2006_to_JMh_CIECAM02(Jpapbp, coefficients): """ Converts from one of the *Luo et al. (2006)* *CAM02-LCD*, *CAM02-SCD*, or *CAM02-UCS* colourspaces :math:`J'a'b'` array to *CIECAM02* :math:`JMh` correlates array. Parameters ---------- Jpapbp : array_like *Luo et al. (2006)* *CAM02-LCD*, *CAM02-SCD*, or *CAM02-UCS* colourspaces :math:`J'a'b'` array. coefficients : array_like Coefficients of one of the *Luo et al. (2006)* *CAM02-LCD*, *CAM02-SCD*, or *CAM02-UCS* colourspaces. Returns ------- ndarray *CIECAM02* correlates array :math:`JMh`. Notes ----- +------------+------------------------+--------------------+ | **Domain** | **Scale - Reference** | **Scale - 1** | +============+========================+====================+ | ``Jpapbp`` | ``Jp_1`` : [0, 100] | ``Jp_1`` : [0, 1] | | | | | | | ``ap_1`` : [-100, 100] | ``ap_1`` : [-1, 1] | | | | | | | ``bp_1`` : [-100, 100] | ``bp_1`` : [-1, 1] | +------------+------------------------+--------------------+ +------------+------------------------+--------------------+ | **Range** | **Scale - Reference** | **Scale - 1** | +============+========================+====================+ | ``JMh`` | ``J`` : [0, 100] | ``J`` : [0, 1] | | | | | | | ``M`` : [0, 100] | ``M`` : [0, 1] | | | | | | | ``h`` : [0, 360] | ``h`` : [0, 1] | +------------+------------------------+--------------------+ Examples -------- >>> Jpapbp = np.array([54.90433134, -0.08450395, -0.06854831]) >>> UCS_Luo2006_to_JMh_CIECAM02( ... Jpapbp, COEFFICIENTS_UCS_LUO2006['CAM02-LCD']) ... # doctest: +ELLIPSIS array([ 4.1731091...e+01, 1.0884217...e-01, 2.1904843...e+02]) """ J_p, a_p, b_p = tsplit(to_domain_100(Jpapbp)) _K_L, c_1, c_2 = tsplit(coefficients) J = -J_p / (c_1 * J_p - 1 - 100 * c_1) M_p, h = tsplit(cartesian_to_polar(tstack([a_p, b_p]))) M = (np.exp(M_p / (1 / c_2)) - 1) / c_2 JMh = tstack([ from_range_100(J), from_range_100(M), from_range_degrees(np.degrees(h) % 360) ]) return JMh
def corresponding_colour(RGB_1, xez_1, xez_2, bRGB_o1, bRGB_o2, Y_o, K, n=1): """ Computes the corresponding colour cone responses of given test sample cone responses :math:`RGB_1`. Parameters ---------- RGB_1: array_like Test sample cone responses :math:`RGB_1`. xez_1: array_like Intermediate values :math:`\\xi_1`, :math:`\eta_1`, :math:`\zeta_1` for the test illuminant and background. xez_2: array_like Intermediate values :math:`\\xi_2`, :math:`\eta_2`, :math:`\zeta_2` for the reference illuminant and background. bRGB_o1: array_like Chromatic adaptation exponential factors :math:`\\beta_1(R_{o1})`, :math:`\\beta_1(G_{o1})` and :math:`\\beta_2(B_{o1})` of test sample. bRGB_o2: array_like Chromatic adaptation exponential factors :math:`\\beta_1(R_{o2})`, :math:`\\beta_1(G_{o2})` and :math:`\\beta_2(B_{o2})` of reference sample. Y_o : numeric or array_like Luminance factor :math:`Y_o` of achromatic background as percentage in domain [18, 100]. K : numeric or array_like Coefficient :math:`K`. n : numeric or array_like, optional Noise component in fundamental primary system. Returns ------- ndarray Corresponding colour cone responses of given test sample cone responses. Examples -------- >>> RGB_1 = np.array([25.82442730, 18.67914220, 4.83901940]) >>> xez_1 = np.array([1.11857195, 0.93295530, 0.32680879]) >>> xez_2 = np.array([1.00000372, 1.00000176, 0.99999461]) >>> bRGB_o1 = np.array([3.74852518, 3.63920879, 2.78924811]) >>> bRGB_o2 = np.array([3.68102374, 3.68102256, 3.56557351]) >>> Y_o = 20 >>> K = 1.0 >>> corresponding_colour( # doctest: +ELLIPSIS ... RGB_1, xez_1, xez_2, bRGB_o1, bRGB_o2, Y_o, K) array([ 23.1636901..., 20.0211948..., 16.2001664...]) """ R_1, G_1, B_1 = tsplit(RGB_1) xi_1, eta_1, zeta_1 = tsplit(xez_1) xi_2, eta_2, zeta_2 = tsplit(xez_2) bR_o1, bG_o1, bB_o1 = tsplit(bRGB_o1) bR_o2, bG_o2, bB_o2 = tsplit(bRGB_o2) Y_o = np.asarray(Y_o) K = np.asarray(K) def RGB_c(x_1, x_2, y_1, y_2, z): """ Computes the corresponding colour cone responses component. """ return ((Y_o * x_2 + n) * K**(1 / y_2) * ((z + n) / (Y_o * x_1 + n))**(y_1 / y_2) - n) R_2 = RGB_c(xi_1, xi_2, bR_o1, bR_o2, R_1) G_2 = RGB_c(eta_1, eta_2, bG_o1, bG_o2, G_1) B_2 = RGB_c(zeta_1, zeta_2, bB_o1, bB_o2, B_1) RGB_2 = tstack((R_2, G_2, B_2)) return RGB_2
def whiteness_Ganz1979(xy: ArrayLike, Y: FloatingOrNDArray) -> NDArray: """ Return the *whiteness* index :math:`W` and *tint* :math:`T` of given sample *CIE xy* chromaticity coordinates using *Ganz and Griesser (1979)* method. Parameters ---------- xy Chromaticity coordinates *CIE xy* of the sample. Y Tristimulus :math:`Y` value of the sample. Returns ------- :class:`numpy.ndarray` *Whiteness* :math:`W` and *tint* :math:`T`. Notes ----- +------------+-----------------------+---------------+ | **Domain** | **Scale - Reference** | **Scale - 1** | +============+=======================+===============+ | ``Y`` | [0, 100] | [0, 1] | +------------+-----------------------+---------------+ +------------+-----------------------+---------------+ | **Range** | **Scale - Reference** | **Scale - 1** | +============+=======================+===============+ | ``WT`` | [0, 100] | [0, 1] | +------------+-----------------------+---------------+ - The formula coefficients are valid for *CIE Standard Illuminant D Series* *D65* and *CIE 1964 10 Degree Standard Observer*. - Positive output *tint* :math:`T` values indicate a greener tint while negative values indicate a redder tint. - Whiteness differences of less than 5 Ganz units appear to be indistinguishable to the human eye. - Tint differences of less than 0.5 Ganz units appear to be indistinguishable to the human eye. References ---------- :cite:`X-Rite2012a` Examples -------- >>> import numpy as np >>> xy = np.array([0.3167, 0.3334]) >>> whiteness_Ganz1979(xy, 100) # doctest: +ELLIPSIS array([ 85.6003766..., 0.6789003...]) """ x, y = tsplit(xy) Y = to_domain_100(Y) W = Y - 1868.322 * x - 3695.690 * y + 1809.441 T = -1001.223 * x + 748.366 * y + 68.261 WT = tstack([W, T]) return from_range_100(WT)
def JMh_CIECAM02_to_UCS_Luo2006(JMh, coefficients): """ Converts from *CIECAM02* :math:`JMh` correlates array to one of the *Luo et al. (2006)* *CAM02-LCD*, *CAM02-SCD*, or *CAM02-UCS* colourspaces :math:`J'a'b'` array. The :math:`JMh` correlates array is constructed using the CIECAM02 correlate of *Lightness* :math:`J`, the *CIECAM02* correlate of *colourfulness* :math:`M` and the *CIECAM02* *Hue* angle :math:`h` in degrees. Parameters ---------- JMh : array_like *CIECAM02* correlates array :math:`JMh`. coefficients : array_like Coefficients of one of the *Luo et al. (2006)* *CAM02-LCD*, *CAM02-SCD*, or *CAM02-UCS* colourspaces. Returns ------- ndarray *Luo et al. (2006)* *CAM02-LCD*, *CAM02-SCD*, or *CAM02-UCS* colourspaces :math:`J'a'b'` array. Notes ----- +------------+------------------------+--------------------+ | **Domain** | **Scale - Reference** | **Scale - 1** | +============+========================+====================+ | ``JMh`` | ``J`` : [0, 100] | ``J`` : [0, 1] | | | | | | | ``M`` : [0, 100] | ``M`` : [0, 1] | | | | | | | ``h`` : [0, 360] | ``h`` : [0, 1] | +------------+------------------------+--------------------+ +------------+------------------------+--------------------+ | **Range** | **Scale - Reference** | **Scale - 1** | +============+========================+====================+ | ``Jpapbp`` | ``Jp_1`` : [0, 100] | ``Jp_1`` : [0, 1] | | | | | | | ``ap_1`` : [-100, 100] | ``ap_1`` : [-1, 1] | | | | | | | ``bp_1`` : [-100, 100] | ``bp_1`` : [-1, 1] | +------------+------------------------+--------------------+ Examples -------- >>> from colour.appearance import ( ... CIECAM02_VIEWING_CONDITIONS, ... XYZ_to_CIECAM02) >>> XYZ = np.array([19.01, 20.00, 21.78]) >>> XYZ_w = np.array([95.05, 100.00, 108.88]) >>> L_A = 318.31 >>> Y_b = 20.0 >>> surround = CIECAM02_VIEWING_CONDITIONS['Average'] >>> specification = XYZ_to_CIECAM02( ... XYZ, XYZ_w, L_A, Y_b, surround) >>> JMh = (specification.J, specification.M, specification.h) >>> JMh_CIECAM02_to_UCS_Luo2006(JMh, COEFFICIENTS_UCS_LUO2006['CAM02-LCD']) ... # doctest: +ELLIPSIS array([ 54.9043313..., -0.0845039..., -0.0685483...]) """ J, M, h = tsplit(JMh) J = to_domain_100(J) M = to_domain_100(M) h = to_domain_degrees(h) _K_L, c_1, c_2 = tsplit(coefficients) J_p = ((1 + 100 * c_1) * J) / (1 + c_1 * J) M_p = (1 / c_2) * np.log(1 + c_2 * M) a_p, b_p = tsplit(polar_to_cartesian(tstack([M_p, np.radians(h)]))) Jpapbp = tstack([J_p, a_p, b_p]) return from_range_100(Jpapbp)
def msds_cmfs_anomalous_trichromacy_Machado2009( cmfs: LMS_ConeFundamentals, d_LMS: ArrayLike) -> LMS_ConeFundamentals: """ Shift given *LMS* cone fundamentals colour matching functions with given :math:`\\Delta_{LMS}` shift amount in nanometers to simulate anomalous trichromacy using *Machado et al. (2009)* method. Parameters ---------- cmfs *LMS* cone fundamentals colour matching functions. 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:`colour.LMS_ConeFundamentals` Anomalous trichromacy *LMS* cone fundamentals colour matching functions. Warnings -------- *Machado et al. (2009)* simulation of tritanomaly is based on the shift paradigm as an approximation to the actual phenomenon and restrain the model from trying to model tritanopia. The pre-generated matrices are using a shift value in domain [5, 59] contrary to the domain [0, 20] used for protanomaly and deuteranomaly simulation. References ---------- :cite:`Colblindorb`, :cite:`Colblindora`, :cite:`Colblindorc`, :cite:`Machado2009` Examples -------- >>> from colour.colorimetry import MSDS_CMFS_LMS >>> cmfs = MSDS_CMFS_LMS['Stockman & Sharpe 2 Degree Cone Fundamentals'] >>> cmfs[450] array([ 0.0498639, 0.0870524, 0.955393 ]) >>> msds_cmfs_anomalous_trichromacy_Machado2009( ... cmfs, np.array([15, 0, 0]))[450] # doctest: +ELLIPSIS array([ 0.0891288..., 0.0870524 , 0.955393 ]) """ cmfs = cast(LMS_ConeFundamentals, cmfs.copy()) if cmfs.shape.interval != 1: cmfs.interpolate(SpectralShape(cmfs.shape.start, cmfs.shape.end, 1)) cmfs.extrapolator_kwargs = {"method": "Constant", "left": 0, "right": 0} L, M, _S = tsplit(cmfs.values) d_L, d_M, d_S = tsplit(d_LMS) if d_S != 0: usage_warning( '"Machado et al. (2009)" simulation of tritanomaly is based on ' "the shift paradigm as an approximation to the actual phenomenon " "and restrain the model from trying to model tritanopia.\n" "The pre-generated matrices are using a shift value in domain " "[5, 59] contrary to the domain [0, 20] used for protanomaly and " "deuteranomaly simulation.") area_L = np.trapz(L, cmfs.wavelengths) area_M = np.trapz(M, cmfs.wavelengths) def alpha(x: NDArray) -> NDArray: """Compute :math:`alpha` factor.""" return (20 - x) / 20 # Corrected equations as per: # http://www.inf.ufrgs.br/~oliveira/pubs_files/ # CVD_Simulation/CVD_Simulation.html#Errata L_a = alpha(d_L) * L + 0.96 * area_L / area_M * (1 - alpha(d_L)) * M M_a = alpha(d_M) * M + 1 / 0.96 * area_M / area_L * (1 - alpha(d_M)) * L S_a = as_float_array(cmfs[cmfs.wavelengths - d_S])[:, 2] LMS_a = tstack([L_a, M_a, S_a]) cmfs[cmfs.wavelengths] = LMS_a severity = f"{d_L}, {d_M}, {d_S}" template = "{0} - Anomalous Trichromacy ({1})" cmfs.name = template.format(cmfs.name, severity) cmfs.strict_name = template.format(cmfs.strict_name, severity) return cmfs
def plot_single_sd( sd: SpectralDistribution, cmfs: Union[MultiSpectralDistributions, str, Sequence[Union[ MultiSpectralDistributions, str]], ] = "CIE 1931 2 Degree Standard Observer", out_of_gamut_clipping: Boolean = True, modulate_colours_with_sd_amplitude: Boolean = False, equalize_sd_amplitude: Boolean = False, **kwargs: Any, ) -> Tuple[plt.Figure, plt.Axes]: """ Plot given spectral distribution. Parameters ---------- sd Spectral distribution to plot. cmfs Standard observer colour matching functions used for computing the spectrum domain and colours. ``cmfs`` can be of any type or form supported by the :func:`colour.plotting.filter_cmfs` definition. out_of_gamut_clipping Whether to clip out of gamut colours otherwise, the colours will be offset by the absolute minimal colour leading to a rendering on gray background, less saturated and smoother. modulate_colours_with_sd_amplitude Whether to modulate the colours with the spectral distribution amplitude. equalize_sd_amplitude Whether to equalize the spectral distribution amplitude. Equalization occurs after the colours modulation thus setting both arguments to *True* will generate a spectrum strip where each wavelength colour is modulated by the spectral distribution amplitude. The usual 5% margin above the spectral distribution is also omitted. Other Parameters ---------------- kwargs {:func:`colour.plotting.artist`, :func:`colour.plotting.render`}, See the documentation of the previously listed definitions. Returns ------- :class:`tuple` Current figure and axes. References ---------- :cite:`Spiker2015a` Examples -------- >>> from colour import SpectralDistribution >>> data = { ... 500: 0.0651, ... 520: 0.0705, ... 540: 0.0772, ... 560: 0.0870, ... 580: 0.1128, ... 600: 0.1360 ... } >>> sd = SpectralDistribution(data, name='Custom') >>> plot_single_sd(sd) # doctest: +ELLIPSIS (<Figure size ... with 1 Axes>, <...AxesSubplot...>) .. image:: ../_static/Plotting_Plot_Single_SD.png :align: center :alt: plot_single_sd """ _figure, axes = artist(**kwargs) cmfs = cast(MultiSpectralDistributions, first_item(filter_cmfs(cmfs).values())) sd = cast(SpectralDistribution, sd.copy()) sd.interpolator = LinearInterpolator wavelengths = cmfs.wavelengths[np.logical_and( cmfs.wavelengths >= max(min(cmfs.wavelengths), min(sd.wavelengths)), cmfs.wavelengths <= min(max(cmfs.wavelengths), max(sd.wavelengths)), )] values = as_float_array(sd[wavelengths]) RGB = XYZ_to_plotting_colourspace( wavelength_to_XYZ(wavelengths, cmfs), CCS_ILLUMINANTS["CIE 1931 2 Degree Standard Observer"]["E"], apply_cctf_encoding=False, ) if not out_of_gamut_clipping: RGB += np.abs(np.min(RGB)) RGB = normalise_maximum(RGB) if modulate_colours_with_sd_amplitude: RGB *= (values / np.max(values))[..., np.newaxis] RGB = CONSTANTS_COLOUR_STYLE.colour.colourspace.cctf_encoding(RGB) if equalize_sd_amplitude: values = ones(values.shape) margin = 0 if equalize_sd_amplitude else 0.05 x_min, x_max = min(wavelengths), max(wavelengths) y_min, y_max = 0, max(values) + max(values) * margin polygon = Polygon( np.vstack([ (x_min, 0), tstack([wavelengths, values]), (x_max, 0), ]), facecolor="none", edgecolor="none", zorder=CONSTANTS_COLOUR_STYLE.zorder.background_polygon, ) axes.add_patch(polygon) padding = 0.1 axes.bar( x=wavelengths - padding, height=max(values), width=1 + padding, color=RGB, align="edge", clip_path=polygon, zorder=CONSTANTS_COLOUR_STYLE.zorder.background_polygon, ) axes.plot( wavelengths, values, color=CONSTANTS_COLOUR_STYLE.colour.dark, zorder=CONSTANTS_COLOUR_STYLE.zorder.midground_line, ) settings: Dict[str, Any] = { "axes": axes, "bounding_box": (x_min, x_max, y_min, y_max), "title": f"{sd.strict_name} - {cmfs.strict_name}", "x_label": "Wavelength $\\lambda$ (nm)", "y_label": "Spectral Distribution", } settings.update(kwargs) return render(**settings)
def XYZ_to_Hunter_Lab( XYZ, XYZ_n=TVS_ILLUMINANTS_HUNTERLAB['CIE 1931 2 Degree Standard Observer'] ['D65'].XYZ_n, K_ab=TVS_ILLUMINANTS_HUNTERLAB['CIE 1931 2 Degree Standard Observer'] ['D65'].K_ab): """ Converts from *CIE XYZ* tristimulus values to *Hunter L,a,b* colour scale. Parameters ---------- XYZ : array_like *CIE XYZ* tristimulus values. XYZ_n : array_like, optional Reference *illuminant* tristimulus values. K_ab : array_like, optional Reference *illuminant* chromaticity coefficients, if ``K_ab`` is set to *None* it will be computed using :func:`colour.XYZ_to_K_ab_HunterLab1966`. Returns ------- ndarray *Hunter L,a,b* colour scale array. Notes ----- +------------+-----------------------+-----------------+ | **Domain** | **Scale - Reference** | **Scale - 1** | +============+=======================+=================+ | ``XYZ`` | [0, 100] | [0, 1] | +------------+-----------------------+-----------------+ | ``XYZ_n`` | [0, 100] | [0, 1] | +------------+-----------------------+-----------------+ +------------+-----------------------+-----------------+ | **Range** | **Scale - Reference** | **Scale - 1** | +============+=======================+=================+ | ``Lab`` | ``L`` : [0, 100] | ``L`` : [0, 1] | | | | | | | ``a`` : [-100, 100] | ``a`` : [-1, 1] | | | | | | | ``b`` : [-100, 100] | ``b`` : [-1, 1] | +------------+-----------------------+-----------------+ References ---------- :cite:`HunterLab2008b` Examples -------- >>> XYZ = np.array([0.20654008, 0.12197225, 0.05136952]) * 100 >>> D65 = TVS_ILLUMINANTS_HUNTERLAB[ ... 'CIE 1931 2 Degree Standard Observer']['D65'] >>> XYZ_to_Hunter_Lab(XYZ, D65.XYZ_n, D65.K_ab) # doctest: +ELLIPSIS array([ 34.9245257..., 47.0618985..., 14.3861510...]) """ X, Y, Z = tsplit(to_domain_100(XYZ)) X_n, Y_n, Z_n = tsplit(to_domain_100(XYZ_n)) K_a, K_b = (tsplit(XYZ_to_K_ab_HunterLab1966(XYZ_n)) if K_ab is None else tsplit(K_ab)) Y_Y_n = Y / Y_n sqrt_Y_Y_n = np.sqrt(Y_Y_n) L = 100 * sqrt_Y_Y_n a = K_a * ((X / X_n - Y_Y_n) / sqrt_Y_Y_n) b = K_b * ((Y_Y_n - Z / Z_n) / sqrt_Y_Y_n) Lab = tstack([L, a, b]) return from_range_100(Lab)
def XYZ_to_Hunt(XYZ, XYZ_w, XYZ_b, L_A, surround=HUNT_VIEWING_CONDITIONS['Normal Scenes'], L_AS=None, CCT_w=None, XYZ_p=None, p=None, S=None, S_w=None, helson_judd_effect=False, discount_illuminant=True): """ Computes the *Hunt* colour appearance model correlates. Parameters ---------- XYZ : array_like *CIE XYZ* tristimulus values of test sample / stimulus. XYZ_w : array_like *CIE XYZ* tristimulus values of reference white. XYZ_b : array_like *CIE XYZ* tristimulus values of background. L_A : numeric or array_like Adapting field *luminance* :math:`L_A` in :math:`cd/m^2`. surround : Hunt_InductionFactors, optional Surround viewing conditions induction factors. L_AS : numeric or array_like, optional Scotopic luminance :math:`L_{AS}` of the illuminant, approximated if not specified. CCT_w : numeric or array_like, optional Correlated color temperature :math:`T_{cp}`: of the illuminant, needed to approximate :math:`L_{AS}`. XYZ_p : array_like, optional *CIE XYZ* tristimulus values of proximal field, assumed to be equal to background if not specified. p : numeric or array_like, optional Simultaneous contrast / assimilation factor :math:`p` with value normalised to domain [-1, 0] when simultaneous contrast occurs and normalised to domain [0, 1] when assimilation occurs. S : numeric or array_like, optional Scotopic response :math:`S` to the stimulus, approximated using tristimulus values :math:`Y` of the stimulus if not specified. S_w : numeric or array_like, optional Scotopic response :math:`S_w` for the reference white, approximated using the tristimulus values :math:`Y_w` of the reference white if not specified. helson_judd_effect : bool, optional Truth value indicating whether the *Helson-Judd* effect should be accounted for. discount_illuminant : bool, optional Truth value indicating if the illuminant should be discounted. Returns ------- Hunt_Specification *Hunt* colour appearance model specification. Raises ------ ValueError If an illegal arguments combination is specified. Notes ----- +--------------------------+-----------------------+---------------+ | **Domain** | **Scale - Reference** | **Scale - 1** | +==========================+=======================+===============+ | ``XYZ`` | [0, 100] | [0, 1] | +--------------------------+-----------------------+---------------+ | ``XYZ_w`` | [0, 100] | [0, 1] | +--------------------------+-----------------------+---------------+ | ``XYZ_b`` | [0, 100] | [0, 1] | +--------------------------+-----------------------+---------------+ | ``XYZ_p`` | [0, 100] | [0, 1] | +--------------------------+-----------------------+---------------+ +--------------------------+-----------------------+---------------+ | **Range** | **Scale - Reference** | **Scale - 1** | +==========================+=======================+===============+ | ``Hunt_Specification.h`` | [0, 360] | [0, 1] | +--------------------------+-----------------------+---------------+ References ---------- :cite:`Fairchild2013u`, :cite:`Hunt2004b` Examples -------- >>> XYZ = np.array([19.01, 20.00, 21.78]) >>> XYZ_w = np.array([95.05, 100.00, 108.88]) >>> XYZ_b = np.array([95.05, 100.00, 108.88]) >>> L_A = 318.31 >>> surround = HUNT_VIEWING_CONDITIONS['Normal Scenes'] >>> CCT_w = 6504.0 >>> XYZ_to_Hunt(XYZ, XYZ_w, XYZ_b, L_A, surround, CCT_w=CCT_w) ... # doctest: +ELLIPSIS Hunt_Specification(J=30.0462678..., C=0.1210508..., h=269.2737594..., \ s=0.0199093..., Q=22.2097654..., M=0.1238964..., H=None, HC=None) """ XYZ = to_domain_100(XYZ) XYZ_w = to_domain_100(XYZ_w) XYZ_b = to_domain_100(XYZ_b) _X, Y, _Z = tsplit(XYZ) _X_w, Y_w, _Z_w = tsplit(XYZ_w) X_b, Y_b, _Z_b = tsplit(XYZ_b) # Arguments handling. if XYZ_p is not None: X_p, Y_p, Z_p = tsplit(to_domain_100(XYZ_p)) else: X_p = X_b Y_p = Y_b Z_p = Y_b usage_warning('Unspecified proximal field "XYZ_p" argument, using ' 'background "XYZ_b" as approximation!') if surround.N_cb is None: N_cb = 0.725 * spow(Y_w / Y_b, 0.2) usage_warning('Unspecified "N_cb" argument, using approximation: ' '"{0}"'.format(N_cb)) if surround.N_bb is None: N_bb = 0.725 * spow(Y_w / Y_b, 0.2) usage_warning('Unspecified "N_bb" argument, using approximation: ' '"{0}"'.format(N_bb)) if L_AS is None and CCT_w is None: raise ValueError('Either the scotopic luminance "L_AS" of the ' 'illuminant or its correlated colour temperature ' '"CCT_w" must be specified!') if L_AS is None: L_AS = illuminant_scotopic_luminance(L_A, CCT_w) usage_warning( 'Unspecified "L_AS" argument, using approximation from "CCT": ' '"{0}"'.format(L_AS)) if (S is None and S_w is not None) or (S is not None and S_w is None): raise ValueError('Either both stimulus scotopic response "S" and ' 'reference white scotopic response "S_w" arguments ' 'need to be specified or none of them!') elif S is None and S_w is None: S = Y S_w = Y_w usage_warning( 'Unspecified stimulus scotopic response "S" and reference ' 'white scotopic response "S_w" arguments, using ' 'approximation: "{0}", "{1}"'.format(S, S_w)) if p is None: usage_warning( 'Unspecified simultaneous contrast / assimilation "p" ' 'argument, model will not account for simultaneous chromatic ' 'contrast!') XYZ_p = tstack([X_p, Y_p, Z_p]) # Computing luminance level adaptation factor :math:`F_L`. F_L = luminance_level_adaptation_factor(L_A) # Computing test sample chromatic adaptation. rgb_a = chromatic_adaptation(XYZ, XYZ_w, XYZ_b, L_A, F_L, XYZ_p, p, helson_judd_effect, discount_illuminant) # Computing reference white chromatic adaptation. rgb_aw = chromatic_adaptation(XYZ_w, XYZ_w, XYZ_b, L_A, F_L, XYZ_p, p, helson_judd_effect, discount_illuminant) # Computing opponent colour dimensions. # Computing achromatic post adaptation signals. A_a = achromatic_post_adaptation_signal(rgb_a) A_aw = achromatic_post_adaptation_signal(rgb_aw) # Computing colour difference signals. C = colour_difference_signals(rgb_a) C_w = colour_difference_signals(rgb_aw) # ------------------------------------------------------------------------- # Computing the *hue* angle :math:`h_s`. # ------------------------------------------------------------------------- h = hue_angle(C) # hue_w = hue_angle(C_w) # TODO: Implement hue quadrature & composition computation. # ------------------------------------------------------------------------- # Computing the correlate of *saturation* :math:`s`. # ------------------------------------------------------------------------- # Computing eccentricity factors. e_s = eccentricity_factor(h) # Computing low luminance tritanopia factor :math:`F_t`. F_t = low_luminance_tritanopia_factor(L_A) M_yb = yellowness_blueness_response(C, e_s, surround.N_c, N_cb, F_t) M_rg = redness_greenness_response(C, e_s, surround.N_c, N_cb) M_yb_w = yellowness_blueness_response(C_w, e_s, surround.N_c, N_cb, F_t) M_rg_w = redness_greenness_response(C_w, e_s, surround.N_c, N_cb) # Computing overall chromatic response. M = overall_chromatic_response(M_yb, M_rg) M_w = overall_chromatic_response(M_yb_w, M_rg_w) s = saturation_correlate(M, rgb_a) # ------------------------------------------------------------------------- # Computing the correlate of *brightness* :math:`Q`. # ------------------------------------------------------------------------- # Computing achromatic signal :math:`A`. A = achromatic_signal(L_AS, S, S_w, N_bb, A_a) A_w = achromatic_signal(L_AS, S_w, S_w, N_bb, A_aw) Q = brightness_correlate(A, A_w, M, surround.N_b) brightness_w = brightness_correlate(A_w, A_w, M_w, surround.N_b) # TODO: Implement whiteness-blackness :math:`Q_{wb}` computation. # ------------------------------------------------------------------------- # Computing the correlate of *Lightness* :math:`J`. # ------------------------------------------------------------------------- J = lightness_correlate(Y_b, Y_w, Q, brightness_w) # ------------------------------------------------------------------------- # Computing the correlate of *chroma* :math:`C_{94}`. # ------------------------------------------------------------------------- C_94 = chroma_correlate(s, Y_b, Y_w, Q, brightness_w) # ------------------------------------------------------------------------- # Computing the correlate of *colourfulness* :math:`M_{94}`. # ------------------------------------------------------------------------- M_94 = colourfulness_correlate(F_L, C_94) return Hunt_Specification(J, C_94, from_range_degrees(h), s, Q, M_94, None, None)
def Hunter_Lab_to_XYZ( Lab, XYZ_n=TVS_ILLUMINANTS_HUNTERLAB['CIE 1931 2 Degree Standard Observer'] ['D65'].XYZ_n, K_ab=TVS_ILLUMINANTS_HUNTERLAB['CIE 1931 2 Degree Standard Observer'] ['D65'].K_ab): """ Converts from *Hunter L,a,b* colour scale to *CIE XYZ* tristimulus values. Parameters ---------- Lab : array_like *Hunter L,a,b* colour scale array. XYZ_n : array_like, optional Reference *illuminant* tristimulus values. K_ab : array_like, optional Reference *illuminant* chromaticity coefficients, if ``K_ab`` is set to *None* it will be computed using :func:`colour.XYZ_to_K_ab_HunterLab1966`. Returns ------- ndarray *CIE XYZ* tristimulus values. Notes ----- +------------+-----------------------+-----------------+ | **Domain** | **Scale - Reference** | **Scale - 1** | +============+=======================+=================+ | ``Lab`` | ``L`` : [0, 100] | ``L`` : [0, 1] | | | | | | | ``a`` : [-100, 100] | ``a`` : [-1, 1] | | | | | | | ``b`` : [-100, 100] | ``b`` : [-1, 1] | +------------+-----------------------+-----------------+ | ``XYZ_n`` | [0, 100] | [0, 1] | +------------+-----------------------+-----------------+ +------------+-----------------------+-----------------+ | **Range** | **Scale - Reference** | **Scale - 1** | +============+=======================+=================+ | ``XYZ`` | [0, 100] | [0, 1] | +------------+-----------------------+-----------------+ References ---------- :cite:`HunterLab2008b` Examples -------- >>> Lab = np.array([34.92452577, 47.06189858, 14.38615107]) >>> D65 = TVS_ILLUMINANTS_HUNTERLAB[ ... 'CIE 1931 2 Degree Standard Observer']['D65'] >>> Hunter_Lab_to_XYZ(Lab, D65.XYZ_n, D65.K_ab) array([ 20.654008, 12.197225, 5.136952]) """ L, a, b = tsplit(to_domain_100(Lab)) X_n, Y_n, Z_n = tsplit(to_domain_100(XYZ_n)) K_a, K_b = (tsplit(XYZ_to_K_ab_HunterLab1966(XYZ_n)) if K_ab is None else tsplit(K_ab)) L_100 = L / 100 L_100_2 = L_100**2 Y = L_100_2 * Y_n X = ((a / K_a) * L_100 + L_100_2) * X_n Z = -((b / K_b) * L_100 - L_100_2) * Z_n XYZ = tstack([X, Y, Z]) return from_range_100(XYZ)
def uv_to_Luv(uv, illuminant=CCS_ILLUMINANTS['CIE 1931 2 Degree Standard Observer'] ['D65'], Y=1): """ Returns the *CIE L\\*u\\*v\\** colourspace array from given :math:`uv^p` chromaticity coordinates by extending the array last dimension with given :math:`L` *Lightness*. Parameters ---------- uv : array_like :math:`uv^p` chromaticity coordinates. illuminant : array_like, optional Reference *illuminant* *CIE xy* chromaticity coordinates or *CIE xyY* colourspace array. Y : numeric, optional Optional :math:`Y` *luminance* value used to construct the intermediate *CIE XYZ* colourspace array, the default :math:`Y` *luminance* value is 1. Returns ------- ndarray *CIE L\\*u\\*v\\** colourspace array. Notes ----- +----------------+-----------------------+-----------------+ | **Range** | **Scale - Reference** | **Scale - 1** | +================+=======================+=================+ | ``Luv`` | ``L`` : [0, 100] | ``L`` : [0, 1] | | | | | | | ``u`` : [-100, 100] | ``u`` : [-1, 1] | | | | | | | ``v`` : [-100, 100] | ``v`` : [-1, 1] | +----------------+-----------------------+-----------------+ | ``illuminant`` | [0, 1] | [0, 1] | +----------------+-----------------------+-----------------+ References ---------- :cite:`CIETC1-482004j` Examples -------- >>> import numpy as np >>> uv = np.array([0.37720213, 0.50120264]) >>> uv_to_Luv(uv) # doctest: +ELLIPSIS array([ 100. , 233.1837603..., 42.7474385...]) """ u, v = tsplit(uv) Y = to_domain_1(Y) X = 9 * u / (4 * v) Z = (-5 * Y * v - 3 * u / 4 + 3) / v Y = full(u.shape, Y) return XYZ_to_Luv(from_range_1(tstack([X, Y, Z])), illuminant)
def plot_chromaticity_diagram_colours( samples: Integer = 256, diagram_colours: Optional[Union[ArrayLike, str]] = None, diagram_opacity: Floating = 1, diagram_clipping_path: Optional[ArrayLike] = None, cmfs: Union[MultiSpectralDistributions, str, Sequence[Union[ MultiSpectralDistributions, str]], ] = "CIE 1931 2 Degree Standard Observer", method: Union[Literal["CIE 1931", "CIE 1960 UCS", "CIE 1976 UCS"], str] = "CIE 1931", **kwargs: Any, ) -> Tuple[plt.Figure, plt.Axes]: """ Plot the *Chromaticity Diagram* colours according to given method. Parameters ---------- samples Samples count on one axis when computing the *Chromaticity Diagram* colours. diagram_colours Colours of the *Chromaticity Diagram*, if ``diagram_colours`` is set to *RGB*, the colours will be computed according to the corresponding coordinates. diagram_opacity Opacity of the *Chromaticity Diagram*. diagram_clipping_path Path of points used to clip the *Chromaticity Diagram* colours. cmfs Standard observer colour matching functions used for computing the spectral locus boundaries. ``cmfs`` can be of any type or form supported by the :func:`colour.plotting.filter_cmfs` definition. method *Chromaticity Diagram* method. Other Parameters ---------------- kwargs {:func:`colour.plotting.artist`, :func:`colour.plotting.render`}, See the documentation of the previously listed definitions. Returns ------- :class:`tuple` Current figure and axes. Examples -------- >>> plot_chromaticity_diagram_colours(diagram_colours='RGB') ... # doctest: +ELLIPSIS (<Figure size ... with 1 Axes>, <...AxesSubplot...>) .. image:: ../_static/Plotting_Plot_Chromaticity_Diagram_Colours.png :align: center :alt: plot_chromaticity_diagram_colours """ method = validate_method(method, ["CIE 1931", "CIE 1960 UCS", "CIE 1976 UCS"]) settings: Dict[str, Any] = {"uniform": True} settings.update(kwargs) _figure, axes = artist(**settings) diagram_colours = cast( ArrayLike, optional(diagram_colours, HEX_to_RGB(CONSTANTS_COLOUR_STYLE.colour.average)), ) cmfs = cast(MultiSpectralDistributions, first_item(filter_cmfs(cmfs).values())) illuminant = CONSTANTS_COLOUR_STYLE.colour.colourspace.whitepoint if method == "cie 1931": spectral_locus = XYZ_to_xy(cmfs.values, illuminant) elif method == "cie 1960 ucs": spectral_locus = UCS_to_uv(XYZ_to_UCS(cmfs.values)) elif method == "cie 1976 ucs": spectral_locus = Luv_to_uv(XYZ_to_Luv(cmfs.values, illuminant), illuminant) use_RGB_diagram_colours = str(diagram_colours).upper() == "RGB" if use_RGB_diagram_colours: ii, jj = np.meshgrid(np.linspace(0, 1, samples), np.linspace(1, 0, samples)) ij = tstack([ii, jj]) # NOTE: Various values in the grid have potential to generate # zero-divisions, they could be avoided by perturbing the grid, e.g. # adding a small epsilon. It was decided instead to disable warnings. with suppress_warnings(python_warnings=True): if method == "cie 1931": XYZ = xy_to_XYZ(ij) elif method == "cie 1960 ucs": XYZ = xy_to_XYZ(UCS_uv_to_xy(ij)) elif method == "cie 1976 ucs": XYZ = xy_to_XYZ(Luv_uv_to_xy(ij)) diagram_colours = normalise_maximum(XYZ_to_plotting_colourspace( XYZ, illuminant), axis=-1) polygon = Polygon( spectral_locus if diagram_clipping_path is None else diagram_clipping_path, facecolor="none" if use_RGB_diagram_colours else np.hstack( [diagram_colours, diagram_opacity]), edgecolor="none" if use_RGB_diagram_colours else np.hstack( [diagram_colours, diagram_opacity]), zorder=CONSTANTS_COLOUR_STYLE.zorder.background_polygon, ) axes.add_patch(polygon) if use_RGB_diagram_colours: # Preventing bounding box related issues as per # https://github.com/matplotlib/matplotlib/issues/10529 image = axes.imshow( diagram_colours, interpolation="bilinear", extent=(0, 1, 0, 1), clip_path=None, alpha=diagram_opacity, zorder=CONSTANTS_COLOUR_STYLE.zorder.background_polygon, ) image.set_clip_path(polygon) settings = {"axes": axes} settings.update(kwargs) return render(**kwargs)
def Lab_to_DIN99(Lab, k_E=1, k_CH=1): """ Converts from *CIE L\\*a\\*b\\** colourspace to *DIN99* colourspace. Parameters ---------- Lab : array_like *CIE L\\*a\\*b\\** colourspace array. k_E : numeric, optional Parametric factor :math:`K_E` used to compensate for texture and other specimen presentation effects. k_CH : numeric, optional Parametric factor :math:`K_{CH}` used to compensate for texture and other specimen presentation effects. Returns ------- ndarray *DIN99* colourspace array. Notes ----- +------------+------------------------+--------------------+ | **Domain** | **Scale - Reference** | **Scale - 1** | +============+========================+====================+ | ``Lab`` | ``L`` : [0, 100] | ``L`` : [0, 1] | | | | | | | ``a`` : [-100, 100] | ``a`` : [-1, 1] | | | | | | | ``b`` : [-100, 100] | ``b`` : [-1, 1] | +------------+------------------------+--------------------+ +------------+------------------------+--------------------+ | **Range** | **Scale - Reference** | **Scale - 1** | +============+========================+====================+ | ``Lab_99`` | ``L_99`` : [0, 100] | ``L_99`` : [0, 1] | | | | | | | ``a_99`` : [-100, 100] | ``a_99`` : [-1, 1] | | | | | | | ``b_99`` : [-100, 100] | ``b_99`` : [-1, 1] | +------------+------------------------+--------------------+ References ---------- :cite:`ASTMInternational2007` Examples -------- >>> import numpy as np >>> Lab = np.array([41.52787529, 52.63858304, 26.92317922]) >>> Lab_to_DIN99(Lab) # doctest: +ELLIPSIS array([ 53.2282198..., 28.4163465..., 3.8983955...]) """ L, a, b = tsplit(to_domain_100(Lab)) cos_16 = np.cos(np.radians(16)) sin_16 = np.sin(np.radians(16)) e = cos_16 * a + sin_16 * b f = 0.7 * (-sin_16 * a + cos_16 * b) G = spow(e**2 + f**2, 0.5) h_ef = np.arctan2(f, e) C_99 = (np.log(1 + 0.045 * G)) / (0.045 * k_CH * k_E) # Hue angle is unused currently. # h_99 = np.degrees(h_ef) a_99 = C_99 * np.cos(h_ef) b_99 = C_99 * np.sin(h_ef) L_99 = 105.509 * (np.log(1 + 0.0158 * L)) * k_E Lab_99 = tstack([L_99, a_99, b_99]) return from_range_100(Lab_99)
def demosaicing_CFA_Bayer_bilinear(CFA, pattern='RGGB'): """ Returns the demosaiced *RGB* colourspace array from given *Bayer* CFA using bilinear interpolation. Parameters ---------- CFA : array_like *Bayer* CFA. pattern : unicode, optional **{'RGGB', 'BGGR', 'GRBG', 'GBRG'}**, Arrangement of the colour filters on the pixel array. Returns ------- ndarray *RGB* colourspace array. Notes ----- - The definition output is not clipped in range [0, 1] : this allows for direct HDRI / radiance image generation on *Bayer* CFA data and post demosaicing of the high dynamic range data as showcased in this `Jupyter Notebook <https://github.com/colour-science/colour-hdri/\ blob/develop/colour_hdri/examples/\ examples_merge_from_raw_files_with_post_demosaicing.ipynb>`_. References ---------- :cite:`Losson2010c` Examples -------- >>> import numpy as np >>> CFA = np.array( ... [[0.30980393, 0.36078432, 0.30588236, 0.3764706], ... [0.35686275, 0.39607844, 0.36078432, 0.40000001]]) >>> demosaicing_CFA_Bayer_bilinear(CFA) array([[[ 0.69705884, 0.17941177, 0.09901961], [ 0.46176472, 0.4509804 , 0.19803922], [ 0.45882354, 0.27450981, 0.19901961], [ 0.22941177, 0.5647059 , 0.30000001]], <BLANKLINE> [[ 0.23235295, 0.53529412, 0.29705883], [ 0.15392157, 0.26960785, 0.59411766], [ 0.15294118, 0.4509804 , 0.59705884], [ 0.07647059, 0.18431373, 0.90000002]]]) >>> CFA = np.array( ... [[0.3764706, 0.360784320, 0.40784314, 0.3764706], ... [0.35686275, 0.30980393, 0.36078432, 0.29803923]]) >>> demosaicing_CFA_Bayer_bilinear(CFA, 'BGGR') array([[[ 0.07745098, 0.17941177, 0.84705885], [ 0.15490197, 0.4509804 , 0.5882353 ], [ 0.15196079, 0.27450981, 0.61176471], [ 0.22352942, 0.5647059 , 0.30588235]], <BLANKLINE> [[ 0.23235295, 0.53529412, 0.28235295], [ 0.4647059 , 0.26960785, 0.19607843], [ 0.45588237, 0.4509804 , 0.20392157], [ 0.67058827, 0.18431373, 0.10196078]]]) """ CFA = as_float_array(CFA) R_m, G_m, B_m = masks_CFA_Bayer(CFA.shape, pattern) H_G = as_float_array( [[0, 1, 0], [1, 4, 1], [0, 1, 0]]) / 4 # yapf: disable H_RB = as_float_array( [[1, 2, 1], [2, 4, 2], [1, 2, 1]]) / 4 # yapf: disable R = convolve(CFA * R_m, H_RB) G = convolve(CFA * G_m, H_G) B = convolve(CFA * B_m, H_RB) del R_m, G_m, B_m, H_RB, H_G return tstack([R, G, B])
def xy_to_xyY(xy, Y=1): """ Converts from *CIE xy* chromaticity coordinates to *CIE xyY* colourspace by extending the array last dimension with given :math:`Y` *luminance*. ``xy`` argument with last dimension being equal to 3 will be assumed to be a *CIE xyY* colourspace array argument and will be returned directly by the definition. Parameters ---------- xy : array_like *CIE xy* chromaticity coordinates or *CIE xyY* colourspace array. Y : numeric, optional Optional :math:`Y` *luminance* value used to construct the *CIE xyY* colourspace array, the default :math:`Y` *luminance* value is 1. Returns ------- ndarray *CIE xyY* colourspace array. Notes ----- +------------+-----------------------+---------------+ | **Domain** | **Scale - Reference** | **Scale - 1** | +============+=======================+===============+ | ``xy`` | [0, 1] | [0, 1] | +------------+-----------------------+---------------+ +------------+-----------------------+---------------+ | **Range** | **Scale - Reference** | **Scale - 1** | +============+=======================+===============+ | ``xyY`` | [0, 1] | [0, 1] | +------------+-----------------------+---------------+ - This definition is a convenient object provided to implement support of illuminant argument *luminance* value in various :mod:`colour.models` package objects such as :func:`colour.Lab_to_XYZ` or :func:`colour.Luv_to_XYZ`. References ---------- :cite:`Wikipedia2005` Examples -------- >>> xy = np.array([0.54369557, 0.32107944]) >>> xy_to_xyY(xy) # doctest: +ELLIPSIS array([ 0.5436955..., 0.3210794..., 1. ]) >>> xy = np.array([0.54369557, 0.32107944, 1.00000000]) >>> xy_to_xyY(xy) # doctest: +ELLIPSIS array([ 0.5436955..., 0.3210794..., 1. ]) >>> xy = np.array([0.54369557, 0.32107944]) >>> xy_to_xyY(xy, 100) # doctest: +ELLIPSIS array([ 0.5436955..., 0.3210794..., 100. ]) """ xy = as_float_array(xy) Y = to_domain_1(Y) shape = xy.shape # Assuming ``xy`` is actually a *CIE xyY* colourspace array argument and # returning it directly. if shape[-1] == 3: return xy x, y = tsplit(xy) Y = full(x.shape, from_range_1(Y)) xyY = tstack([x, y, Y]) return xyY