def test_closest(self): """ Tests :func:`colour.utilities.array.closest` definition. """ a = np.array([ 24.31357115, 63.62396289, 55.71528816, 62.70988028, 46.84480573, 25.40026416 ]) self.assertEqual(closest(a, 63.05), 62.70988028) self.assertEqual(closest(a, 24.90), 25.40026416) self.assertEqual(closest(a, 51.15), 46.84480573)
def test_closest(self): """ Tests :func:`colour.utilities.array.closest` definition. """ a = np.array([24.31357115, 63.62396289, 55.71528816, 62.70988028, 46.84480573, 25.40026416]) self.assertEqual(closest(a, 63.05), 62.70988028) self.assertEqual(closest(a, 24.90), 25.40026416) self.assertEqual(closest(a, 51.15), 46.84480573)
def mesopic_weighting_function( wavelength, Lp, source='Blue Heavy', method='MOVE', photopic_lef=SDS_LEFS_PHOTOPIC['CIE 1924 Photopic Standard Observer'], scotopic_lef=SDS_LEFS_SCOTOPIC['CIE 1951 Scotopic Standard Observer']): """ Calculates the mesopic weighting function factor at given wavelength :math:`\\lambda` using the photopic luminance :math:`L_p`. Parameters ---------- wavelength : numeric or array_like Wavelength :math:`\\lambda` to calculate the mesopic weighting function factor. Lp : numeric Photopic luminance :math:`L_p`. source : unicode, optional **{'Blue Heavy', 'Red Heavy'}**, Light source colour temperature. method : unicode, optional **{'MOVE', 'LRC'}**, Method to calculate the weighting factor. photopic_lef : SpectralDistribution, optional :math:`V(\\lambda)` photopic luminous efficiency function. scotopic_lef : SpectralDistribution, optional :math:`V^\\prime(\\lambda)` scotopic luminous efficiency function. Returns ------- numeric or ndarray Mesopic weighting function factor. References ---------- :cite:`Wikipedia2005d` Examples -------- >>> mesopic_weighting_function(500, 0.2) # doctest: +ELLIPSIS 0.7052200... """ mesopic_x_luminance_values = sorted(DATA_MESOPIC_X.keys()) index = mesopic_x_luminance_values.index( closest(mesopic_x_luminance_values, Lp)) x = DATA_MESOPIC_X[mesopic_x_luminance_values[index]][source][method] Vm = (1 - x) * scotopic_lef[wavelength] + x * photopic_lef[wavelength] return Vm
def mesopic_weighting_function( wavelength, Lp, source='Blue Heavy', method='MOVE', photopic_lef=PHOTOPIC_LEFS['CIE 1924 Photopic Standard Observer'], scotopic_lef=SCOTOPIC_LEFS['CIE 1951 Scotopic Standard Observer']): """ Calculates the mesopic weighting function factor at given wavelength :math:`\\lambda` using the photopic luminance :math:`L_p`. Parameters ---------- wavelength : numeric or array_like Wavelength :math:`\\lambda` to calculate the mesopic weighting function factor. Lp : numeric Photopic luminance :math:`L_p`. source : unicode, optional **{'Blue Heavy', 'Red Heavy'}**, Light source colour temperature. method : unicode, optional **{'MOVE', 'LRC'}**, Method to calculate the weighting factor. photopic_lef : SpectralDistribution, optional :math:`V(\\lambda)` photopic luminous efficiency function. scotopic_lef : SpectralDistribution, optional :math:`V^\\prime(\\lambda)` scotopic luminous efficiency function. Returns ------- numeric or ndarray Mesopic weighting function factor. References ---------- :cite:`Wikipedia2005d` Examples -------- >>> mesopic_weighting_function(500, 0.2) # doctest: +ELLIPSIS 0.7052200... """ mesopic_x_luminance_values = sorted(MESOPIC_X_DATA.keys()) index = mesopic_x_luminance_values.index( closest(mesopic_x_luminance_values, Lp)) x = MESOPIC_X_DATA[mesopic_x_luminance_values[index]][source][method] Vm = (1 - x) * scotopic_lef[wavelength] + x * photopic_lef[wavelength] return Vm
def mesopic_weighting_function(wavelength, Lp, source='Blue Heavy', method='MOVE', photopic_lef=PHOTOPIC_LEFS.get( 'CIE 1924 Photopic Standard Observer'), scotopic_lef=SCOTOPIC_LEFS.get( 'CIE 1951 Scotopic Standard Observer')): """ Calculates the mesopic weighting function factor at given wavelength :math:`\lambda` using the photopic luminance :math:`L_p`. Parameters ---------- wavelength : numeric or array_like Wavelength :math:`\lambda` to calculate the mesopic weighting function factor. Lp : numeric Photopic luminance :math:`L_p`. source : unicode, optional **{'Blue Heavy', 'Red Heavy'}**, Light source colour temperature. method : unicode, optional **{'MOVE', 'LRC'}**, Method to calculate the weighting factor. photopic_lef : SpectralPowerDistribution, optional :math:`V(\lambda)` photopic luminous efficiency function. scotopic_lef : SpectralPowerDistribution, optional :math:`V^\prime(\lambda)` scotopic luminous efficiency function. Returns ------- numeric or ndarray Mesopic weighting function factor. Examples -------- >>> mesopic_weighting_function(500, 0.2) # doctest: +ELLIPSIS 0.7052200... """ mesopic_x_luminance_values = sorted(MESOPIC_X_DATA.keys()) index = mesopic_x_luminance_values.index( closest(mesopic_x_luminance_values, Lp)) x = MESOPIC_X_DATA.get( mesopic_x_luminance_values[index]).get(source).get(method) Vm = ((1 - x) * scotopic_lef.get(wavelength) + x * photopic_lef.get(wavelength)) return Vm
def test_closest(self): """Test :func:`colour.utilities.array.closest` definition.""" a = np.array([ 24.31357115, 63.62396289, 55.71528816, 62.70988028, 46.84480573, 25.40026416, ]) self.assertEqual(closest(a, 63.05), 62.70988028) self.assertEqual(closest(a, 51.15), 46.84480573) self.assertEqual(closest(a, 24.90), 25.40026416) np.testing.assert_almost_equal( closest(a, np.array([63.05, 51.15, 24.90])), np.array([62.70988028, 46.84480573, 25.40026416]), decimal=7, )
def domain_distance(self, a): """ Returns the euclidean distance between given array and independent domain :math:`x` closest element. Parameters ---------- a : numeric or array_like :math:`a` variable to compute the euclidean distance with independent domain :math:`x` variable. Returns ------- numeric or array_like Euclidean distance between independent domain :math:`x` variable and given :math:`a` variable. """ n = closest(self.domain, a) return as_numeric(np.abs(a - n))
def domain_distance(self, a: FloatingOrArrayLike) -> FloatingOrNDArray: """ Return the euclidean distance between given array and independent domain :math:`x` closest element. Parameters ---------- a Variable :math:`a` to compute the euclidean distance with independent domain variable :math:`x`. Returns ------- :class:`numpy.floating` or :class:`numpy.ndarray` Euclidean distance between independent domain variable :math:`x` and given variable :math:`a`. """ n = closest(self.domain, a) return as_float(np.abs(a - n))
def domain_distance(self, a): """ Returns the euclidean distance between given array and independent domain :math:`x` closest element. Parameters ---------- a : numeric or array_like :math:`a` variable to compute the euclidean distance with independent domain :math:`x` variable. Returns ------- numeric or array_like Euclidean distance between independent domain :math:`x` variable and given :math:`a` variable. """ n = closest(self.domain, a) return as_float(np.abs(a - n))
def matrix_augmented_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]) >>> matrix_augmented_Cheung2004(RGB, terms=5) # doctest: +ELLIPSIS array([ 0.1722481..., 0.0917066..., 0.0641693..., 0.0010136..., 1...]) """ R, G, B = tsplit(RGB) tail = 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, tail, ]) elif terms == 7: return tstack([ R, G, B, R * G, R * B, G * B, tail, ]) elif terms == 8: return tstack([ R, G, B, R * G, R * B, G * B, R * G * B, tail, ]) elif terms == 10: return tstack([ R, G, B, R * G, R * B, G * B, R**2, G**2, B**2, tail, ]) elif terms == 11: return tstack([ R, G, B, R * G, R * B, G * B, R**2, G**2, B**2, R * G * B, tail, ]) 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, tail, ]) 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, tail, ]) 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, tail, ]) 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 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.0641693..., 0.1256832..., \ 0.0767121..., 0.1051335...]) """ 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, spow(R * G, 1 / 2), spow(G * B, 1 / 2), spow(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, spow(R * G, 1 / 2), spow(G * B, 1 / 2), spow(R * B, 1 / 2), spow(R * G**2, 1 / 3), spow(G * B**2, 1 / 3), spow(R * B**2, 1 / 3), spow(G * R**2, 1 / 3), spow(B * G**2, 1 / 3), spow(B * R**2, 1 / 3), spow(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, spow(R * G, 1 / 2), spow(G * B, 1 / 2), spow(R * B, 1 / 2), spow(R * G**2, 1 / 3), spow(G * B**2, 1 / 3), spow(R * B**2, 1 / 3), spow(G * R**2, 1 / 3), spow(B * G**2, 1 / 3), spow(B * R**2, 1 / 3), spow(R * G * B, 1 / 3), spow(R**3 * G, 1 / 4), spow(R**3 * B, 1 / 4), spow(G**3 * R, 1 / 4), spow(G**3 * B, 1 / 4), spow(B**3 * R, 1 / 4), spow(B**3 * G, 1 / 4), spow(R**2 * G * B, 1 / 4), spow(G**2 * R * B, 1 / 4), spow(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 mesopic_weighting_function( wavelength: FloatingOrArrayLike, L_p: Floating, source: Union[Literal["Blue Heavy", "Red Heavy"], str] = "Blue Heavy", method: Union[Literal["MOVE", "LRC"], str] = "MOVE", photopic_lef: Optional[SpectralDistribution] = None, scotopic_lef: Optional[SpectralDistribution] = None, ) -> FloatingOrNDArray: """ Calculate the mesopic weighting function factor :math:`V_m` at given wavelength :math:`\\lambda` using the photopic luminance :math:`L_p`. Parameters ---------- wavelength Wavelength :math:`\\lambda` to calculate the mesopic weighting function factor. L_p Photopic luminance :math:`L_p`. source Light source colour temperature. method Method to calculate the weighting factor. photopic_lef :math:`V(\\lambda)` photopic luminous efficiency function, default to the *CIE 1924 Photopic Standard Observer*. scotopic_lef :math:`V^\\prime(\\lambda)` scotopic luminous efficiency function, default to the *CIE 1951 Scotopic Standard Observer*. Returns ------- :class:`numpy.floating` or :class:`numpy.ndarray` Mesopic weighting function factor :math:`V_m`. References ---------- :cite:`Wikipedia2005d` Examples -------- >>> mesopic_weighting_function(500, 0.2) # doctest: +ELLIPSIS 0.7052200... """ photopic_lef = cast( SpectralDistribution, optional( photopic_lef, SDS_LEFS_PHOTOPIC["CIE 1924 Photopic Standard Observer"], ), ) scotopic_lef = cast( SpectralDistribution, optional( scotopic_lef, SDS_LEFS_SCOTOPIC["CIE 1951 Scotopic Standard Observer"], ), ) source = validate_method( source, ["Blue Heavy", "Red Heavy"], '"{0}" light source colour temperature is invalid, ' "it must be one of {1}!", ) method = validate_method(method, ["MOVE", "LRC"]) mesopic_x_luminance_values = sorted(DATA_MESOPIC_X.keys()) index = mesopic_x_luminance_values.index( closest(mesopic_x_luminance_values, L_p)) x = DATA_MESOPIC_X[mesopic_x_luminance_values[index]][source][method] V_m = (1 - x) * scotopic_lef[wavelength] + x * photopic_lef[wavelength] return V_m
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 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 polynomial_expansion_Finlayson2015( RGB: ArrayLike, degree: Literal[1, 2, 3, 4] = 1, root_polynomial_expansion: Boolean = True, ) -> NDArray: """ Perform polynomial expansion of given *RGB* colourspace array using *Finlayson et al. (2015)* method. Parameters ---------- RGB *RGB* colourspace array to expand. degree Expanded polynomial degree. root_polynomial_expansion Whether to use the root-polynomials set for the expansion. Returns ------- :class:`numpy.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.0641693..., 0.1256832..., \ 0.0767121..., 0.1051335...]) """ RGB = as_float_array(RGB) 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( f'"Finlayson et al. (2015)" method does not define a polynomial ' f"expansion for {degree} degree, closest polynomial expansion is " f"{closest_degree} degree!") if degree == 1: return RGB elif degree == 2: if root_polynomial_expansion: return tstack([ R, G, B, spow(R * G, 1 / 2), spow(G * B, 1 / 2), spow(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, spow(R * G, 1 / 2), spow(G * B, 1 / 2), spow(R * B, 1 / 2), spow(R * G**2, 1 / 3), spow(G * B**2, 1 / 3), spow(R * B**2, 1 / 3), spow(G * R**2, 1 / 3), spow(B * G**2, 1 / 3), spow(B * R**2, 1 / 3), spow(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, spow(R * G, 1 / 2), spow(G * B, 1 / 2), spow(R * B, 1 / 2), spow(R * G**2, 1 / 3), spow(G * B**2, 1 / 3), spow(R * B**2, 1 / 3), spow(G * R**2, 1 / 3), spow(B * G**2, 1 / 3), spow(B * R**2, 1 / 3), spow(R * G * B, 1 / 3), spow(R**3 * G, 1 / 4), spow(R**3 * B, 1 / 4), spow(G**3 * R, 1 / 4), spow(G**3 * B, 1 / 4), spow(B**3 * R, 1 / 4), spow(B**3 * G, 1 / 4), spow(R**2 * G * B, 1 / 4), spow(G**2 * R * B, 1 / 4), spow(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 matrix_augmented_Cheung2004( RGB: ArrayLike, terms: Literal[3, 5, 7, 8, 10, 11, 14, 16, 17, 19, 20, 22] = 3, ) -> NDArray: """ Perform polynomial expansion of given *RGB* colourspace array using *Cheung et al. (2004)* method. Parameters ---------- RGB *RGB* colourspace array to expand. terms Number of terms of the expanded polynomial. Returns ------- :class:`numpy.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]) >>> matrix_augmented_Cheung2004(RGB, terms=5) # doctest: +ELLIPSIS array([ 0.1722481..., 0.0917066..., 0.0641693..., 0.0010136..., 1...]) """ RGB = as_float_array(RGB) R, G, B = tsplit(RGB) tail = 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( f'"Cheung et al. (2004)" method does not define an augmented ' f"matrix with {terms} terms, closest augmented matrix has " f"{closest_terms} terms!") if terms == 3: return RGB elif terms == 5: return tstack([ R, G, B, R * G * B, tail, ]) elif terms == 7: return tstack([ R, G, B, R * G, R * B, G * B, tail, ]) elif terms == 8: return tstack([ R, G, B, R * G, R * B, G * B, R * G * B, tail, ]) elif terms == 10: return tstack([ R, G, B, R * G, R * B, G * B, R**2, G**2, B**2, tail, ]) elif terms == 11: return tstack([ R, G, B, R * G, R * B, G * B, R**2, G**2, B**2, R * G * B, tail, ]) 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, tail, ]) 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, tail, ]) 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, tail, ]) 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, ])