def exponent_hdr_CIELab( Y_s: FloatingOrArrayLike, Y_abs: FloatingOrArrayLike, method: Union[Literal["Fairchild 2011", "Fairchild 2010"], str] = "Fairchild 2011", ) -> FloatingOrNDArray: """ Compute *hdr-CIELAB* colourspace *Lightness* :math:`\\epsilon` exponent using *Fairchild and Wyble (2010)* or *Fairchild and Chen (2011)* method. Parameters ---------- Y_s Relative luminance :math:`Y_s` of the surround. Y_abs Absolute luminance :math:`Y_{abs}` of the scene diffuse white in :math:`cd/m^2`. method Computation method. Returns ------- :class:`numpy.floating` or :class:`numpy.ndarray` *hdr-CIELAB* colourspace *Lightness* :math:`\\epsilon` exponent. Notes ----- +------------+-----------------------+---------------+ | **Domain** | **Scale - Reference** | **Scale - 1** | +============+=======================+===============+ | ``Y_s`` | [0, 1] | [0, 1] | +------------+-----------------------+---------------+ Examples -------- >>> exponent_hdr_CIELab(0.2, 100) # doctest: +ELLIPSIS 0.4738510... >>> exponent_hdr_CIELab(0.2, 100, method='Fairchild 2010') ... # doctest: +ELLIPSIS 1.8360198... """ Y_s = to_domain_1(Y_s) Y_abs = as_float_array(Y_abs) method = validate_method(method, HDR_CIELAB_METHODS) if method == "fairchild 2010": epsilon = 1.50 else: epsilon = 0.58 sf = 1.25 - 0.25 * (Y_s / 0.184) lf = np.log(318) / np.log(Y_abs) if method == "fairchild 2010": epsilon *= sf * lf else: epsilon /= sf * lf return epsilon
def sd_single_led( peak_wavelength: Floating, fwhm: Floating, shape: SpectralShape = SPECTRAL_SHAPE_DEFAULT, method: Union[Literal["Ohno 2005"], str] = "Ohno 2005", **kwargs: Any, ) -> SpectralDistribution: """ Return a single *LED* spectral distribution of given spectral shape at given peak wavelength and full width at half maximum according to given method. Parameters ---------- peak_wavelength Wavelength the single *LED* spectral distribution will peak at. fwhm Full width at half maximum, i.e. width of the underlying gaussian spectral distribution measured between those points on the *y* axis which are half the maximum amplitude. shape Spectral shape used to create the spectral distribution. method Computation method. Other Parameters ---------------- kwargs {:func:`colour.colorimetry.sd_single_led_Ohno2005`}, See the documentation of the previously listed definition. Returns ------- :class:`colour.SpectralDistribution` Single *LED* spectral distribution. Notes ----- - By default, the spectral distribution will use the shape given by :attr:`colour.SPECTRAL_SHAPE_DEFAULT` attribute. References ---------- :cite:`Ohno2005`, :cite:`Ohno2008a` Examples -------- >>> sd = sd_single_led(555, 25) >>> sd.shape SpectralShape(360.0, 780.0, 1.0) >>> sd[555] # doctest: +ELLIPSIS 1.0000000... """ method = validate_method(method, SD_SINGLE_LED_METHODS) return SD_SINGLE_LED_METHODS[method](peak_wavelength, fwhm, shape, **kwargs)
def substrate_concentration_MichaelisMenten( v: FloatingOrArrayLike, V_max: FloatingOrArrayLike, K_m: FloatingOrArrayLike, method: Union[Literal["Michaelis 1913", "Abebe 2017"], str] = "Michaelis 1913", **kwargs: Any, ) -> FloatingOrNDArray: """ Describe the rate of enzymatic reactions, by relating concentration of a substrate :math:`S` to reaction rate :math:`v` according to given method. Parameters ---------- v Reaction rate :math:`v`. V_max Maximum rate :math:`V_{max}` achieved by the system, at saturating substrate concentration. K_m Substrate concentration :math:`K_m` at which the reaction rate is half of :math:`V_{max}`. method Computation method. Other Parameters ---------------- b_m {:func:`colour.biochemistry.\ substrate_concentration_MichaelisMenten_Abebe2017`}, Bias factor :math:`b_m`. Returns ------- :class:`numpy.floating` or :class:`numpy.ndarray` Concentration of a substrate :math:`S`. References ---------- :cite:`Wikipedia2003d`, :cite:`Abebe2017a` Examples -------- >>> substrate_concentration_MichaelisMenten(0.961538461538461, 2.5, 0.8) ... # doctest: +ELLIPSIS 0.4999999... >>> substrate_concentration_MichaelisMenten( ... 1.036054703688355, 2.5, 0.8, method='Abebe 2017', b_m=0.813) ... # doctest: +ELLIPSIS 0.5000000... """ method = validate_method(method, SUBSTRATE_CONCENTRATION_MICHAELISMENTEN_METHODS) function = SUBSTRATE_CONCENTRATION_MICHAELISMENTEN_METHODS[method] return function(v, V_max, K_m, **filter_kwargs(function, **kwargs))
def oetf_inverse(value: FloatingOrArrayLike, function: Union[Literal["ARIB STD-B67", "Blackmagic Film Generation 5", "DaVinci Intermediate", "ITU-R BT.2100 HLG", "ITU-R BT.2100 PQ", "ITU-R BT.601", "ITU-R BT.709", ], str, ] = "ITU-R BT.709", **kwargs: Any) -> FloatingOrNDArray: """ Decode :math:`R'G'B'` video component signal value to tristimulus values at the display using given inverse opto-electronic transfer function (OETF). Parameters ---------- value Value. function Inverse opto-electronic transfer function (OETF). Other Parameters ---------------- kwargs {:func:`colour.models.oetf_inverse_ARIBSTDB67`, :func:`colour.models.oetf_inverse_BlackmagicFilmGeneration5`, :func:`colour.models.oetf_inverse_DaVinciIntermediate`, :func:`colour.models.oetf_inverse_HLG_BT2100`, :func:`colour.models.oetf_inverse_PQ_BT2100`, :func:`colour.models.oetf_inverse_BT601`, :func:`colour.models.oetf_inverse_BT709`}, See the documentation of the previously listed definitions. Returns ------- :class:`numpy.floating` or :class:`numpy.ndarray` Tristimulus values at the display. Examples -------- >>> oetf_inverse(0.409007728864150) # doctest: +ELLIPSIS 0.1... >>> oetf_inverse( # doctest: +ELLIPSIS ... 0.409007728864150, function='ITU-R BT.601') 0.1... """ function = validate_method( function, OETF_INVERSES, '"{0}" inverse "OETF" is invalid, it must be one of {1}!', ) callable_ = OETF_INVERSES[function] return callable_(value, **filter_kwargs(callable_, **kwargs))
def polynomial_expansion( a: ArrayLike, method: Union[Literal["Cheung 2004", "Finlayson 2015", "Vandermonde"], str] = "Cheung 2004", **kwargs: Any, ) -> NDArray: """ Perform polynomial expansion of given :math:`a` array. Parameters ---------- a :math:`a` array to expand. method Computation method. Other Parameters ---------------- degree {:func:`colour.characterisation.polynomial_expansion_Finlayson2015`, :func:`colour.characterisation.polynomial_expansion_Vandermonde`}, Expanded polynomial degree, must be one of *[1, 2, 3, 4]* for :func:`colour.characterisation.polynomial_expansion_Finlayson2015` definition. root_polynomial_expansion {:func:`colour.characterisation.polynomial_expansion_Finlayson2015`}, Whether to use the root-polynomials set for the expansion. terms {:func:`colour.characterisation.matrix_augmented_Cheung2004`}, Number of terms of the expanded polynomial. Returns ------- :class:`numpy.ndarray` Expanded :math:`a` array. References ---------- :cite:`Cheung2004`, :cite:`Finlayson2015`, :cite:`Westland2004`, :cite:`Wikipedia2003e` Examples -------- >>> RGB = np.array([0.17224810, 0.09170660, 0.06416938]) >>> polynomial_expansion(RGB) # doctest: +ELLIPSIS array([ 0.1722481..., 0.0917066..., 0.0641693...]) >>> polynomial_expansion(RGB, 'Cheung 2004', terms=5) # doctest: +ELLIPSIS array([ 0.1722481..., 0.0917066..., 0.0641693..., 0.0010136..., 1...]) """ method = validate_method(method, POLYNOMIAL_EXPANSION_METHODS) function = POLYNOMIAL_EXPANSION_METHODS[method] return function(a, **filter_kwargs(function, **kwargs))
def eotf(value: Union[FloatingOrArrayLike, IntegerOrArrayLike], function: Union[Literal["DCDM", "DICOM GSDF", "ITU-R BT.1886", "ITU-R BT.2020", "ITU-R BT.2100 HLG", "ITU-R BT.2100 PQ", "SMPTE 240M", "ST 2084", "sRGB", ], str, ] = "ITU-R BT.1886", **kwargs: Any) -> FloatingOrNDArray: """ Decode :math:`R'G'B'` video component signal value to tristimulus values at the display using given electro-optical transfer function (EOTF). Parameters ---------- value Value. function Electro-optical transfer function (EOTF). Other Parameters ---------------- kwargs {:func:`colour.models.eotf_DCDM`, :func:`colour.models.eotf_DICOMGSDF`, :func:`colour.models.eotf_BT1886`, :func:`colour.models.eotf_BT2020`, :func:`colour.models.eotf_HLG_BT2100`, :func:`colour.models.eotf_PQ_BT2100`, :func:`colour.models.eotf_SMPTE240M`, :func:`colour.models.eotf_ST2084`, :func:`colour.models.eotf_sRGB`}, See the documentation of the previously listed definitions. Returns ------- :class:`numpy.floating` or :class:`numpy.ndarray` Tristimulus values at the display. Examples -------- >>> eotf(0.461356129500442) # doctest: +ELLIPSIS 0.1... >>> eotf(0.409007728864150, function='ITU-R BT.2020') ... # doctest: +ELLIPSIS 0.1... >>> eotf(0.182011532850008, function='ST 2084', L_p=1000) ... # doctest: +ELLIPSIS 0.1... """ function = validate_method( function, EOTFS, '"{0}" "EOTF" is invalid, it must be one of {1}!') callable_ = EOTFS[function] return callable_(value, **filter_kwargs(callable_, **kwargs))
def method(self, value: Union[Literal["Linear", "Constant"], str]): """Setter for the **self.method** property.""" attest( is_string(value), f'"method" property: "{value}" type is not "str"!', ) value = validate_method(value, ["Linear", "Constant"]) self._method = value
def eotf_inverse(value: FloatingOrArrayLike, function: Union[Literal["DCDM", "DICOM GSDF", "ITU-R BT.1886", "ITU-R BT.2020", "ITU-R BT.2100 HLG", "ITU-R BT.2100 PQ", "ST 2084", "sRGB", ], str, ] = "ITU-R BT.1886", **kwargs) -> Union[FloatingOrNDArray, IntegerOrNDArray]: """ Encode estimated tristimulus values in a scene to :math:`R'G'B'` video component signal value using given inverse electro-optical transfer function (EOTF). Parameters ---------- value Value. function Inverse electro-optical transfer function (EOTF). Other Parameters ---------------- kwargs {:func:`colour.models.eotf_inverse_DCDM`, :func:`colour.models.eotf_inverse_DICOMGSDF`, :func:`colour.models.eotf_inverse_BT1886`, :func:`colour.models.eotf_inverse_BT2020`, :func:`colour.models.eotf_inverse_HLG_BT2100`, :func:`colour.models.eotf_inverse_PQ_BT2100`, :func:`colour.models.eotf_inverse_ST2084`, :func:`colour.models.eotf_inverse_sRGB`}, See the documentation of the previously listed definitions. Returns ------- :class:`numpy.floating` or :class:`numpy.integer` or :class:`numpy.ndarray` :math:`R'G'B'` video component signal value. Examples -------- >>> eotf_inverse(0.11699185725296059) # doctest: +ELLIPSIS 0.4090077... >>> eotf_inverse( # doctest: +ELLIPSIS ... 0.11699185725296059, function='ITU-R BT.1886') 0.4090077... """ function = validate_method( function, EOTF_INVERSES, '"{0}" inverse "EOTF" is invalid, it must be one of {1}!', ) callable_ = EOTF_INVERSES[function] return callable_(value, **filter_kwargs(callable_, **kwargs))
def bandpass_correction( sd: SpectralDistribution, method: Union[Literal["Stearns 1988"], str] = "Stearns 1988", ) -> SpectralDistribution: """ Implement spectral bandpass dependence correction on given spectral distribution using given method. Parameters ---------- sd Spectral distribution. method Correction method. Returns ------- :class:`colour.SpectralDistribution` Spectral bandpass dependence corrected spectral distribution. References ---------- :cite:`Stearns1988a`, :cite:`Westland2012f` Examples -------- >>> from colour import SpectralDistribution >>> from colour.utilities import numpy_print_options >>> data = { ... 500: 0.0651, ... 520: 0.0705, ... 540: 0.0772, ... 560: 0.0870, ... 580: 0.1128, ... 600: 0.1360 ... } >>> with numpy_print_options(suppress=True): ... bandpass_correction(SpectralDistribution(data)) ... # doctest: +ELLIPSIS SpectralDistribution([[ 500. , 0.0646518...], [ 520. , 0.0704293...], [ 540. , 0.0769485...], [ 560. , 0.0856928...], [ 580. , 0.1129644...], [ 600. , 0.1379256...]], interpolator=SpragueInterpolator, interpolator_kwargs={}, extrapolator=Extrapolator, extrapolator_kwargs={...}) """ method = validate_method(method, BANDPASS_CORRECTION_METHODS) return BANDPASS_CORRECTION_METHODS[method](sd)
def xy_to_CCT( xy: ArrayLike, method: Union[ Literal[ "CIE Illuminant D Series", "Kang 2002", "Hernandez 1999", "McCamy 1992", ], str, ] = "CIE Illuminant D Series", ) -> FloatingOrNDArray: """ Return the correlated colour temperature :math:`T_{cp}` from given *CIE xy* chromaticity coordinates using given method. Parameters ---------- xy *CIE xy* chromaticity coordinates. method Computation method. Other Parameters ---------------- optimisation_kwargs {:func:`colour.temperature.xy_to_CCT_CIE_D`, :func:`colour.temperature.xy_to_CCT_Kang2002`}, Parameters for :func:`scipy.optimize.minimize` definition. Returns ------- :class:`numpy.floating` or :class:`numpy.ndarray` Correlated colour temperature :math:`T_{cp}`. References ---------- :cite:`Hernandez-Andres1999a`, :cite:`Kang2002a`, :cite:`Wikipedia2001`, :cite:`Wikipedia2001a`, :cite:`Wyszecki2000z` Examples -------- >>> import numpy as np >>> xy_to_CCT(np.array([0.31270, 0.32900])) # doctest: +ELLIPSIS 6508.1175148... >>> xy_to_CCT(np.array([0.31270, 0.32900]), 'Hernandez 1999') ... # doctest: +ELLIPSIS 6500.7420431... """ method = validate_method(method, XY_TO_CCT_METHODS) return XY_TO_CCT_METHODS[method](xy)
def HelmholtzKohlrausch_effect_object_Nayatani1997( uv: ArrayLike, uv_c: ArrayLike, L_a: FloatingOrArrayLike, method: Union[Literal["VAC", "VCC"], str] = "VCC", ) -> FloatingOrNDArray: """ Return the *HKE* value for object colours using *Nayatani (1997)* method. Parameters ---------- uv *CIE uv* chromaticity coordinates of samples. uv_c *CIE uv* chromaticity coordinates of reference white. L_a Adapting luminance in :math:`cd/m^2`. method Which estimation method to use, *VCC* or *VAC*. Returns ------- :class:`numpy.floating` or :class:`numpy.ndarray` Luminance factor (:math:`\\Gamma`) value(s) computed with Nayatani object colour estimation method. References ---------- :cite:`Nayatani1997` Examples -------- >>> import colour >>> white = colour.xy_to_Luv_uv(colour.temperature.CCT_to_xy_CIE_D(6504)) >>> colours = colour.XYZ_to_xy( ... [colour.wavelength_to_XYZ(430 + i * 50) for i in range(5)]) >>> L_adapting = 65 >>> HelmholtzKohlrausch_effect_object_Nayatani1997( # doctest: +ELLIPSIS ... colour.xy_to_Luv_uv(colours), white, L_adapting) array([ 2.2468383..., 1.4619799..., 1.1801658..., 0.9031318..., \ 1.7999376...]) """ u, v = tsplit(uv) u_c, v_c = tsplit(uv_c) method = validate_method(method, HKE_NAYATANI1997_METHODS) K_Br = coefficient_K_Br_Nayatani1997(L_a) q = coefficient_q_Nayatani1997(np.arctan2(v - v_c, u - u_c)) S_uv = 13 * np.sqrt((u - u_c)**2 + (v - v_c)**2) return 1 + (HKE_NAYATANI1997_METHODS[method] * q + 0.0872 * K_Br) * S_uv
def CCT_to_xy( CCT: FloatingOrArrayLike, method: Union[ Literal[ "CIE Illuminant D Series", "Kang 2002", "Hernandez 1999", "McCamy 1992", ], str, ] = "CIE Illuminant D Series", ) -> NDArray: """ Return the *CIE xy* chromaticity coordinates from given correlated colour temperature :math:`T_{cp}` using given method. Parameters ---------- CCT Correlated colour temperature :math:`T_{cp}`. method Computation method. Other Parameters ---------------- optimisation_kwargs {:func:`colour.temperature.CCT_to_xy_Hernandez1999`, :func:`colour.temperature.CCT_to_xy_McCamy1992`}, Parameters for :func:`scipy.optimize.minimize` definition. Returns ------- :class:`numpy.ndarray` *CIE xy* chromaticity coordinates. References ---------- :cite:`Hernandez-Andres1999a`, :cite:`Kang2002a`, :cite:`Wikipedia2001`, :cite:`Wikipedia2001a`, :cite:`Wyszecki2000z` Examples -------- >>> CCT_to_xy(6504.38938305) # doctest: +ELLIPSIS array([ 0.3127077..., 0.3291128...]) >>> CCT_to_xy(6504.38938305, 'Kang 2002') ... # doctest: +ELLIPSIS array([ 0.313426 ..., 0.3235959...]) """ method = validate_method(method, CCT_TO_XY_METHODS) return CCT_TO_XY_METHODS[method](CCT)
def oetf(value: FloatingOrArrayLike, function: Union[Literal["ARIB STD-B67", "Blackmagic Film Generation 5", "DaVinci Intermediate", "ITU-R BT.2100 HLG", "ITU-R BT.2100 PQ", "ITU-R BT.601", "ITU-R BT.709", "SMPTE 240M", ], str, ] = "ITU-R BT.709", **kwargs: Any) -> FloatingOrNDArray: """ Encode estimated tristimulus values in a scene to :math:`R'G'B'` video component signal value using given opto-electronic transfer function (OETF). Parameters ---------- value Value. function Opto-electronic transfer function (OETF). Other Parameters ---------------- kwargs {:func:`colour.models.oetf_ARIBSTDB67`, :func:`colour.models.oetf_BlackmagicFilmGeneration5`, :func:`colour.models.oetf_DaVinciIntermediate`, :func:`colour.models.oetf_HLG_BT2100`, :func:`colour.models.oetf_PQ_BT2100`, :func:`colour.models.oetf_BT601`, :func:`colour.models.oetf_BT709`, :func:`colour.models.oetf_SMPTE240M`}, See the documentation of the previously listed definitions. Returns ------- :class:`numpy.floating` or :class:`numpy.ndarray` :math:`R'G'B'` video component signal value. Examples -------- >>> oetf(0.18) # doctest: +ELLIPSIS 0.4090077... >>> oetf(0.18, function='ITU-R BT.601') # doctest: +ELLIPSIS 0.4090077... """ function = validate_method( function, OETFS, '"{0}" "OETF" is invalid, it must be one of {1}!') callable_ = OETFS[function] return callable_(value, **filter_kwargs(callable_, **kwargs))
def plot_single_sd_colour_quality_scale_bars( sd: SpectralDistribution, method: Union[ Literal["NIST CQS 7.4", "NIST CQS 9.0"], str ] = "NIST CQS 9.0", **kwargs: Any, ) -> Tuple[plt.Figure, plt.Axes]: """ Plot the *Colour Quality Scale* (CQS) of given illuminant or light source spectral distribution. Parameters ---------- sd Illuminant or light source spectral distribution to plot the *Colour Quality Scale* (CQS). method *Colour Quality Scale* (CQS) computation method. Other Parameters ---------------- kwargs {:func:`colour.plotting.artist`, :func:`colour.plotting.quality.plot_colour_quality_bars`, :func:`colour.plotting.render`}, See the documentation of the previously listed definitions. Returns ------- :class:`tuple` Current figure and axes. Examples -------- >>> from colour import SDS_ILLUMINANTS >>> illuminant = SDS_ILLUMINANTS['FL2'] >>> plot_single_sd_colour_quality_scale_bars(illuminant) ... # doctest: +ELLIPSIS (<Figure size ... with 1 Axes>, <...AxesSubplot...>) .. image:: ../_static/Plotting_\ Plot_Single_SD_Colour_Quality_Scale_Bars.png :align: center :alt: plot_single_sd_colour_quality_scale_bars """ method = validate_method(method, COLOUR_QUALITY_SCALE_METHODS) return plot_multi_sds_colour_quality_scales_bars([sd], method, **kwargs)
def log_decoding_Log3G10( y, method: Union[Literal["v1", "v2", "v3"], str] = "v3" ) -> FloatingOrNDArray: """ Define the *Log3G10* log decoding curve / electro-optical transfer function. Parameters ---------- y Non-linear data :math:`y`. method Computation method. Returns ------- :class:`numpy.floating` or :class:`numpy.ndarray` Linear data :math:`x`. Notes ----- +------------+-----------------------+---------------+ | **Domain** | **Scale - Reference** | **Scale - 1** | +============+=======================+===============+ | ``y`` | [0, 1] | [0, 1] | +------------+-----------------------+---------------+ +------------+-----------------------+---------------+ | **Range** | **Scale - Reference** | **Scale - 1** | +============+=======================+===============+ | ``x`` | [0, 1] | [0, 1] | +------------+-----------------------+---------------+ References ---------- :cite:`Nattress2016a`, :cite:`REDDigitalCinema2017` Examples -------- >>> log_decoding_Log3G10(1.0) # doctest: +ELLIPSIS 184.3223476... >>> log_decoding_Log3G10(1.0 / 3, method='v1') # doctest: +ELLIPSIS 0.1799994... """ method = validate_method(method, LOG3G10_DECODING_METHODS) return LOG3G10_DECODING_METHODS[method](y)
def power_function_Huang2015( d_E: FloatingOrArrayLike, coefficients: Union[Literal["CIE 1976", "CIE 1994", "CIE 2000", "CMC", "CAM02-LCD", "CAM02-SCD", "CAM16-UCS", "DIN99d", "OSA", "OSA-GP-Euclidean", "ULAB", ], str, ] = "CIE 2000", ) -> FloatingOrNDArray: """ Improve the performance of the :math:`\\Delta E` value for given coefficients using *Huang, Cui, Melgosa, Sanchez-Maranon, Li, Luo and Liu (2015)* power-function: :math:`d_E^{\\prime}=a*d_{E^b}`. Parameters ---------- d_E Computed colour difference array :math:`\\Delta E`. coefficients Coefficients for the power-function. Returns ------- :class:`numpy.floating` or :class:`numpy.ndarray` Improved math:`\\Delta E` value. References ---------- :cite:`Huang2015`, :cite:`Li2017` Examples -------- >>> d_E = np.array([2.0425, 2.8615, 3.4412]) >>> power_function_Huang2015(d_E) # doctest: +ELLIPSIS array([ 2.3574879..., 2.9850503..., 3.3965106...]) """ coefficients = validate_method( coefficients, COEFFICIENTS_HUANG2015, '"{0}" coefficients are invalid, ' "they must be one of {1}!", ) a, b = tsplit(COEFFICIENTS_HUANG2015[coefficients]) d_E_p = a * d_E**b return d_E_p
def ellipse_fitting( a: ArrayLike, method: Union[Literal["Halir 1998"], str] = "Halir 1998") -> NDArray: """ Return the coefficients of the implicit second-order polynomial/quadratic curve that fits given point array :math:`a` using given method. The implicit second-order polynomial is expressed as follows: :math:`F\\left(x, y\\right)` = ax^2 + bxy + cy^2 + dx + ey + f = 0` with an ellipse-specific constraint such as :math:`b^2 -4ac < 0` and where :math:`a, b, c, d, e, f` are coefficients of the ellipse and :math:`F\\left(x, y\\right)` are coordinates of points lying on it. Parameters ---------- a Point array :math:`a` to be fitted. method Computation method. Returns ------- :class:`numpy.ndarray` Coefficients of the implicit second-order polynomial/quadratic curve that fits given point array :math:`a`. References ---------- :cite:`Halir1998` Examples -------- >>> a = np.array([[2, 0], [0, 1], [-2, 0], [0, -1]]) >>> ellipse_fitting(a) # doctest: +ELLIPSIS array([ 0.2425356..., 0. , 0.9701425..., 0. , 0. , -0.9701425...]) >>> ellipse_coefficients_canonical_form(ellipse_fitting(a)) array([-0., -0., 2., 1., 0.]) """ method = validate_method(method, ELLIPSE_FITTING_METHODS) function = ELLIPSE_FITTING_METHODS[method] return function(a)
def CCT_to_uv( CCT_D_uv: ArrayLike, method: Union[ Literal["Krystek 1985", "Ohno 2013", "Robertson 1968"], str ] = "Ohno 2013", **kwargs: Any, ) -> NDArray: """ Return the *CIE UCS* colourspace *uv* chromaticity coordinates from given correlated colour temperature :math:`T_{cp}` using given method. Parameters ---------- CCT_D_uv Correlated colour temperature :math:`T_{cp}`, :math:`\\Delta_{uv}`. method Computation method. Other Parameters ---------------- cmfs {:func:`colour.temperature.CCT_to_uv_Ohno2013`}, Standard observer colour matching functions. Returns ------- :class:`numpy.ndarray` *CIE UCS* colourspace *uv* chromaticity coordinates. References ---------- :cite:`AdobeSystems2013`, :cite:`AdobeSystems2013a`, :cite:`Krystek1985b`, :cite:`Ohno2014a`, :cite:`Wyszecki2000y` Examples -------- >>> import numpy as np >>> CCT_D_uv = np.array([6507.47380460, 0.00322335]) >>> CCT_to_uv(CCT_D_uv) # doctest: +ELLIPSIS array([ 0.1977999..., 0.3121999...]) """ method = validate_method(method, CCT_TO_UV_METHODS) function = CCT_TO_UV_METHODS[method] return function(CCT_D_uv, **filter_kwargs(function, **kwargs))
def ootf_inverse(value: FloatingOrArrayLike, function: Union[Literal["ITU-R BT.2100 HLG", "ITU-R BT.2100 PQ"], str] = "ITU-R BT.2100 PQ", **kwargs: Any) -> FloatingOrNDArray: """ Map relative display linear light to scene linear light using given inverse opto-optical transfer function (OOTF / OOCF). Parameters ---------- value Value. function Inverse opto-optical transfer function (OOTF / OOCF). Other Parameters ---------------- kwargs {:func:`colour.models.ootf_inverse_HLG_BT2100`, :func:`colour.models.ootf_inverse_PQ_BT2100`}, See the documentation of the previously listed definitions. Returns ------- :class:`numpy.floating` or :class:`numpy.ndarray` Luminance of scene linear light. Examples -------- >>> ootf_inverse(779.988360834115840) # doctest: +ELLIPSIS 0.1000000... >>> ootf_inverse( # doctest: +ELLIPSIS ... 63.095734448019336, function='ITU-R BT.2100 HLG') 0.1000000... """ function = validate_method( function, OOTF_INVERSES, '"{0}" inverse "OOTF" is invalid, it must be one of {1}!', ) callable_ = OOTF_INVERSES[function] return callable_(value, **filter_kwargs(callable_, **kwargs))
def colour_fidelity_index( sd_test: SpectralDistribution, additional_data=False, method: Union[Literal["CIE 2017", "ANSI/IES TM-30-18"], str] = "CIE 2017", ) -> Union[Floating, ColourRendering_Specification_CIE2017, ColourQuality_Specification_ANSIIESTM3018, ]: """ Return the *Colour Fidelity Index* (CFI) :math:`R_f` of given spectral distribution using given method. Parameters ---------- sd_test Test spectral distribution. additional_data Whether to output additional data. method Computation method. Returns ------- :class:`numpy.floating` or \ :class:`colour.quality.ColourRendering_Specification_CIE2017` or \ :class:`colour.quality.ColourQuality_Specification_ANSIIESTM3018` *Colour Fidelity Index* (CFI) :math:`R_f`. References ---------- :cite:`CIETC1-902017`, :cite:`ANSI2018` Examples -------- >>> from colour.colorimetry import SDS_ILLUMINANTS >>> sd = SDS_ILLUMINANTS['FL2'] >>> colour_fidelity_index(sd) # doctest: +ELLIPSIS 70.1208254... """ method = validate_method(method, COLOUR_FIDELITY_INDEX_METHODS) function = COLOUR_FIDELITY_INDEX_METHODS[method] return function(sd_test, additional_data)
def ootf(value: FloatingOrArrayLike, function: Union[Literal["ITU-R BT.2100 HLG", "ITU-R BT.2100 PQ"], str] = "ITU-R BT.2100 PQ", **kwargs: Any) -> FloatingOrNDArray: """ Map relative scene linear light to display linear light using given opto-optical transfer function (OOTF / OOCF). Parameters ---------- value Value. function Opto-optical transfer function (OOTF / OOCF). Other Parameters ---------------- kwargs {:func:`colour.models.ootf_HLG_BT2100`, :func:`colour.models.ootf_PQ_BT2100`}, See the documentation of the previously listed definitions. Returns ------- :class:`numpy.floating` or :class:`numpy.ndarray` Luminance of a displayed linear component. Examples -------- >>> ootf(0.1) # doctest: +ELLIPSIS 779.9883608... >>> ootf(0.1, function='ITU-R BT.2100 HLG') # doctest: +ELLIPSIS 63.0957344... """ function = validate_method( function, OOTFS, '"{0}" "OOTF" is invalid, it must be one of {1}!') callable_ = OOTFS[function] return callable_(value, **filter_kwargs(callable_, **kwargs))
def index_stress( d_E: FloatingOrArrayLike, d_V: FloatingOrArrayLike, method: Union[Literal["Garcia 2007"], str] = "Garcia 2007", ) -> FloatingOrNDArray: """ Compute the *Kruskal's Standardized Residual Sum of Squares (:math:`STRESS`)* index according to given method. Parameters ---------- d_E Computed colour difference array :math:`\\Delta E`. d_V Computed colour difference array :math:`\\Delta V`. method Computation method. Returns ------- :class:`numpy.floating` or :class:`numpy.ndarray` :math:`STRESS` index. References ---------- :cite:`Garcia2007` Examples -------- >>> d_E = np.array([2.0425, 2.8615, 3.4412]) >>> d_V = np.array([1.2644, 1.2630, 1.8731]) >>> index_stress(d_E, d_V) # doctest: +ELLIPSIS 0.1211709... """ method = validate_method(method, INDEX_STRESS_METHODS) function = INDEX_STRESS_METHODS[method] return function(d_E, d_V)
def _XYZ_optimal_colour_stimuli( illuminant: Union[Literal["A", "C", "D65"], str] = "D65") -> NDArray: """ Return given illuminant *Optimal Colour Stimuli* in *CIE XYZ* tristimulus values and caches it if not existing. Parameters ---------- illuminant Illuminant name. Returns ------- :class:`numpy.ndarray` Illuminant *Optimal Colour Stimuli*. """ illuminant = validate_method( illuminant, list(OPTIMAL_COLOUR_STIMULI_ILLUMINANTS.keys()), '"{0}" illuminant is invalid, it must be one of {1}!', ) optimal_colour_stimuli = OPTIMAL_COLOUR_STIMULI_ILLUMINANTS.get(illuminant) if optimal_colour_stimuli is None: raise KeyError( f'"{illuminant}" not found in factory "Optimal Colour Stimuli": ' f'"{sorted(OPTIMAL_COLOUR_STIMULI_ILLUMINANTS.keys())}".') vertices = _CACHE_OPTIMAL_COLOUR_STIMULI_XYZ.get(illuminant) if vertices is None: _CACHE_OPTIMAL_COLOUR_STIMULI_XYZ[illuminant] = vertices = ( xyY_to_XYZ(optimal_colour_stimuli) / 100) return vertices
def XYZ_to_sd( XYZ: ArrayLike, method: Union[Literal["Jakob 2019", "Mallett 2019", "Meng 2015", "Otsu 2018", "Smits 1999", ], str, ] = "Meng 2015", **kwargs: Any, ) -> SpectralDistribution: """ Recover the spectral distribution of given *CIE XYZ* tristimulus values using given method. Parameters ---------- XYZ *CIE XYZ* tristimulus values to recover the spectral distribution from. method Computation method. Other Parameters ---------------- additional_data {:func:`colour.recovery.XYZ_to_sd_Jakob2019`}, If *True*, ``error`` will be returned alongside ``sd``. basis_functions {:func:`colour.recovery.RGB_to_sd_Mallett2019`}, Basis functions for the method. The default is to use the built-in *sRGB* basis functions, i.e. :attr:`colour.recovery.MSDS_BASIS_FUNCTIONS_sRGB_MALLETT2019`. clip {:func:`colour.recovery.XYZ_to_sd_Otsu2018`}, If *True*, the default, values below zero and above unity in the recovered spectral distributions will be clipped. This ensures that the returned reflectance is physical and conserves energy, but will cause noticeable colour differences in case of very saturated colours. cmfs {:func:`colour.recovery.XYZ_to_sd_Meng2015`}, Standard observer colour matching functions. colourspace {:func:`colour.recovery.XYZ_to_sd_Jakob2019`}, *RGB* colourspace of the target colour. Note that no chromatic adaptation is performed between ``illuminant`` and the colourspace whitepoint. dataset {:func:`colour.recovery.XYZ_to_sd_Otsu2018`}, Dataset to use for reconstruction. The default is to use the published data. illuminant {:func:`colour.recovery.XYZ_to_sd_Jakob2019`, :func:`colour.recovery.XYZ_to_sd_Meng2015`}, Illuminant spectral distribution, default to *CIE Standard Illuminant D65*. interval {:func:`colour.recovery.XYZ_to_sd_Meng2015`}, Wavelength :math:`\\lambda_{i}` range interval in nm. The smaller ``interval`` is, the longer the computations will be. optimisation_kwargs {:func:`colour.recovery.XYZ_to_sd_Jakob2019`, :func:`colour.recovery.XYZ_to_sd_Meng2015`}, Parameters for :func:`scipy.optimize.minimize` and :func:`colour.recovery.find_coefficients_Jakob2019` definitions. Returns ------- :class:`colour.SpectralDistribution` Recovered spectral distribution. Notes ----- +------------+-----------------------+---------------+ | **Domain** | **Scale - Reference** | **Scale - 1** | +============+=======================+===============+ | ``XYZ`` | [0, 1] | [0, 1] | +------------+-----------------------+---------------+ - *Smits (1999)* method will internally convert given *CIE XYZ* tristimulus values to *sRGB* colourspace array assuming equal energy illuminant *E*. References ---------- :cite:`Jakob2019`, :cite:`Mallett2019`, :cite:`Meng2015c`, :cite:`Otsu2018`, :cite:`Smits1999a` Examples -------- *Jakob and Hanika (2009)* reflectance recovery: >>> import numpy as np >>> from colour import MSDS_CMFS, SDS_ILLUMINANTS, SpectralShape >>> from colour.colorimetry import sd_to_XYZ_integration >>> from colour.utilities import numpy_print_options >>> XYZ = np.array([0.20654008, 0.12197225, 0.05136952]) >>> cmfs = ( ... MSDS_CMFS['CIE 1931 2 Degree Standard Observer']. ... copy().align(SpectralShape(360, 780, 10)) ... ) >>> illuminant = SDS_ILLUMINANTS['D65'].copy().align(cmfs.shape) >>> sd = XYZ_to_sd( ... XYZ, method='Jakob 2019', cmfs=cmfs, illuminant=illuminant) >>> with numpy_print_options(suppress=True): ... sd # doctest: +ELLIPSIS SpectralDistribution([[ 360. , 0.4893773...], [ 370. , 0.3258214...], [ 380. , 0.2147792...], [ 390. , 0.1482413...], [ 400. , 0.1086169...], [ 410. , 0.0841255...], [ 420. , 0.0683114...], [ 430. , 0.0577144...], [ 440. , 0.0504267...], [ 450. , 0.0453552...], [ 460. , 0.0418520...], [ 470. , 0.0395259...], [ 480. , 0.0381430...], [ 490. , 0.0375741...], [ 500. , 0.0377685...], [ 510. , 0.0387432...], [ 520. , 0.0405871...], [ 530. , 0.0434783...], [ 540. , 0.0477225...], [ 550. , 0.0538256...], [ 560. , 0.0626314...], [ 570. , 0.0755869...], [ 580. , 0.0952675...], [ 590. , 0.1264265...], [ 600. , 0.1779272...], [ 610. , 0.2649393...], [ 620. , 0.4039779...], [ 630. , 0.5832105...], [ 640. , 0.7445440...], [ 650. , 0.8499970...], [ 660. , 0.9094792...], [ 670. , 0.9425378...], [ 680. , 0.9616376...], [ 690. , 0.9732481...], [ 700. , 0.9806562...], [ 710. , 0.9855873...], [ 720. , 0.9889903...], [ 730. , 0.9914117...], [ 740. , 0.9931801...], [ 750. , 0.9945009...], [ 760. , 0.9955066...], [ 770. , 0.9962855...], [ 780. , 0.9968976...]], interpolator=SpragueInterpolator, interpolator_kwargs={}, extrapolator=Extrapolator, extrapolator_kwargs={...}) >>> sd_to_XYZ_integration(sd, cmfs, illuminant) / 100 # doctest: +ELLIPSIS array([ 0.2066217..., 0.1220128..., 0.0513958...]) *Mallett and Yuksel (2019)* reflectance recovery: >>> cmfs = ( ... MSDS_CMFS['CIE 1931 2 Degree Standard Observer']. ... copy().align(SPECTRAL_SHAPE_sRGB_MALLETT2019) ... ) >>> illuminant = SDS_ILLUMINANTS['D65'].copy().align(cmfs.shape) >>> sd = XYZ_to_sd(XYZ, method='Mallett 2019') >>> with numpy_print_options(suppress=True): ... sd # doctest: +ELLIPSIS SpectralDistribution([[ 380. , 0.1735531...], [ 385. , 0.1720357...], [ 390. , 0.1677721...], [ 395. , 0.1576605...], [ 400. , 0.1372829...], [ 405. , 0.1170849...], [ 410. , 0.0895694...], [ 415. , 0.0706232...], [ 420. , 0.0585765...], [ 425. , 0.0523959...], [ 430. , 0.0497598...], [ 435. , 0.0476057...], [ 440. , 0.0465079...], [ 445. , 0.0460337...], [ 450. , 0.0455839...], [ 455. , 0.0452872...], [ 460. , 0.0450981...], [ 465. , 0.0448895...], [ 470. , 0.0449257...], [ 475. , 0.0448987...], [ 480. , 0.0446834...], [ 485. , 0.0441372...], [ 490. , 0.0417137...], [ 495. , 0.0373832...], [ 500. , 0.0357657...], [ 505. , 0.0348263...], [ 510. , 0.0341953...], [ 515. , 0.0337683...], [ 520. , 0.0334979...], [ 525. , 0.0332991...], [ 530. , 0.0331909...], [ 535. , 0.0332181...], [ 540. , 0.0333387...], [ 545. , 0.0334970...], [ 550. , 0.0337381...], [ 555. , 0.0341847...], [ 560. , 0.0346447...], [ 565. , 0.0353993...], [ 570. , 0.0367367...], [ 575. , 0.0392007...], [ 580. , 0.0445902...], [ 585. , 0.0625633...], [ 590. , 0.2965381...], [ 595. , 0.4215576...], [ 600. , 0.4347139...], [ 605. , 0.4385134...], [ 610. , 0.4385184...], [ 615. , 0.4385249...], [ 620. , 0.4374694...], [ 625. , 0.4384672...], [ 630. , 0.4368251...], [ 635. , 0.4340867...], [ 640. , 0.4303219...], [ 645. , 0.4243257...], [ 650. , 0.4159482...], [ 655. , 0.4057443...], [ 660. , 0.3919874...], [ 665. , 0.3742784...], [ 670. , 0.3518421...], [ 675. , 0.3240127...], [ 680. , 0.2955145...], [ 685. , 0.2625658...], [ 690. , 0.2343423...], [ 695. , 0.2174830...], [ 700. , 0.2060461...], [ 705. , 0.1977437...], [ 710. , 0.1916846...], [ 715. , 0.1861020...], [ 720. , 0.1823908...], [ 725. , 0.1807923...], [ 730. , 0.1795571...], [ 735. , 0.1785623...], [ 740. , 0.1775758...], [ 745. , 0.1771614...], [ 750. , 0.1767431...], [ 755. , 0.1764319...], [ 760. , 0.1762597...], [ 765. , 0.1762209...], [ 770. , 0.1761803...], [ 775. , 0.1761195...], [ 780. , 0.1760763...]], interpolator=SpragueInterpolator, interpolator_kwargs={}, extrapolator=Extrapolator, extrapolator_kwargs={...}) >>> sd_to_XYZ_integration(sd, cmfs, illuminant) / 100 ... # doctest: +ELLIPSIS array([ 0.2065436..., 0.1219996..., 0.0513764...]) *Meng (2015)* reflectance recovery: >>> cmfs = ( ... MSDS_CMFS['CIE 1931 2 Degree Standard Observer']. ... copy().align(SpectralShape(360, 780, 10)) ... ) >>> illuminant = SDS_ILLUMINANTS['D65'].copy().align(cmfs.shape) >>> sd = XYZ_to_sd( ... XYZ, method='Meng 2015', cmfs=cmfs, illuminant=illuminant) >>> with numpy_print_options(suppress=True): ... sd # doctest: +SKIP SpectralDistribution([[ 360. , 0.0762005...], [ 370. , 0.0761792...], [ 380. , 0.0761363...], [ 390. , 0.0761194...], [ 400. , 0.0762539...], [ 410. , 0.0761671...], [ 420. , 0.0754649...], [ 430. , 0.0731519...], [ 440. , 0.0676701...], [ 450. , 0.0577800...], [ 460. , 0.0441993...], [ 470. , 0.0285064...], [ 480. , 0.0138728...], [ 490. , 0.0033585...], [ 500. , 0. ...], [ 510. , 0. ...], [ 520. , 0. ...], [ 530. , 0. ...], [ 540. , 0.0055767...], [ 550. , 0.0317581...], [ 560. , 0.0754491...], [ 570. , 0.1314115...], [ 580. , 0.1937649...], [ 590. , 0.2559311...], [ 600. , 0.3123173...], [ 610. , 0.3584966...], [ 620. , 0.3927335...], [ 630. , 0.4159458...], [ 640. , 0.4306660...], [ 650. , 0.4391040...], [ 660. , 0.4439497...], [ 670. , 0.4463618...], [ 680. , 0.4474625...], [ 690. , 0.4479868...], [ 700. , 0.4482116...], [ 710. , 0.4482800...], [ 720. , 0.4483472...], [ 730. , 0.4484251...], [ 740. , 0.4484633...], [ 750. , 0.4485071...], [ 760. , 0.4484969...], [ 770. , 0.4484853...], [ 780. , 0.4485134...]], interpolator=SpragueInterpolator, interpolator_kwargs={}, extrapolator=Extrapolator, extrapolator_kwargs={...}) >>> sd_to_XYZ_integration(sd, cmfs, illuminant) / 100 # doctest: +ELLIPSIS array([ 0.2065400..., 0.1219722..., 0.0513695...]) *Otsu, Yamamoto and Hachisuka (2018)* reflectance recovery: >>> cmfs = ( ... MSDS_CMFS['CIE 1931 2 Degree Standard Observer']. ... copy().align(SPECTRAL_SHAPE_OTSU2018) ... ) >>> illuminant = SDS_ILLUMINANTS['D65'].copy().align(cmfs.shape) >>> sd = XYZ_to_sd( ... XYZ, method='Otsu 2018', cmfs=cmfs, illuminant=illuminant) >>> with numpy_print_options(suppress=True): ... sd # doctest: +ELLIPSIS SpectralDistribution([[ 380. , 0.0601939...], [ 390. , 0.0568063...], [ 400. , 0.0517429...], [ 410. , 0.0495841...], [ 420. , 0.0502007...], [ 430. , 0.0506489...], [ 440. , 0.0510020...], [ 450. , 0.0493782...], [ 460. , 0.0468046...], [ 470. , 0.0437132...], [ 480. , 0.0416957...], [ 490. , 0.0403783...], [ 500. , 0.0405197...], [ 510. , 0.0406031...], [ 520. , 0.0416912...], [ 530. , 0.0430956...], [ 540. , 0.0444474...], [ 550. , 0.0459336...], [ 560. , 0.0507631...], [ 570. , 0.0628967...], [ 580. , 0.0844661...], [ 590. , 0.1334277...], [ 600. , 0.2262428...], [ 610. , 0.3599330...], [ 620. , 0.4885571...], [ 630. , 0.5752546...], [ 640. , 0.6193023...], [ 650. , 0.6450744...], [ 660. , 0.6610548...], [ 670. , 0.6688673...], [ 680. , 0.6795426...], [ 690. , 0.6887933...], [ 700. , 0.7003469...], [ 710. , 0.7084128...], [ 720. , 0.7154674...], [ 730. , 0.7234334...]], interpolator=SpragueInterpolator, interpolator_kwargs={}, extrapolator=Extrapolator, extrapolator_kwargs={...}) >>> sd_to_XYZ_integration(sd, cmfs, illuminant) / 100 # doctest: +ELLIPSIS array([ 0.2065494..., 0.1219712..., 0.0514002...]) *Smits (1999)* reflectance recovery: >>> cmfs = ( ... MSDS_CMFS['CIE 1931 2 Degree Standard Observer']. ... copy().align(SpectralShape(360, 780, 10)) ... ) >>> illuminant = SDS_ILLUMINANTS['E'].copy().align(cmfs.shape) >>> sd = XYZ_to_sd(XYZ, method='Smits 1999') >>> with numpy_print_options(suppress=True): ... sd # doctest: +ELLIPSIS SpectralDistribution([[ 380. , 0.0787830...], [ 417.7778 , 0.0622018...], [ 455.5556 , 0.0446206...], [ 493.3333 , 0.0352220...], [ 531.1111 , 0.0324149...], [ 568.8889 , 0.0330105...], [ 606.6667 , 0.3207115...], [ 644.4444 , 0.3836164...], [ 682.2222 , 0.3836164...], [ 720. , 0.3835649...]], interpolator=LinearInterpolator, interpolator_kwargs={}, extrapolator=Extrapolator, extrapolator_kwargs={...}) >>> sd_to_XYZ_integration(sd, cmfs, illuminant) / 100 # doctest: +ELLIPSIS array([ 0.1894770..., 0.1126470..., 0.0474420...]) """ a = as_float_array(XYZ) method = validate_method(method, XYZ_TO_SD_METHODS) function = XYZ_TO_SD_METHODS[method] if function is RGB_to_sd_Smits1999: from colour.recovery.smits1999 import XYZ_to_RGB_Smits1999 a = XYZ_to_RGB_Smits1999(XYZ) elif function is RGB_to_sd_Mallett2019: from colour.models import XYZ_to_sRGB a = XYZ_to_sRGB(XYZ, apply_cctf_encoding=False) return function(a, **filter_kwargs(function, **kwargs))
def exponent_function_basic( x: FloatingOrArrayLike, exponent: FloatingOrArrayLike = 1, style: Union[Literal["basicFwd", "basicRev", "basicMirrorFwd", "basicMirrorRev", "basicPassThruFwd", "basicPassThruRev", ], str, ] = "basicFwd", ) -> FloatingOrNDArray: """ Define the *basic* exponent transfer function. Parameters ---------- x Data to undergo the basic exponent conversion. exponent Exponent value used for the conversion. style Defines the behaviour for the transfer function to operate: - *basicFwd*: *Basic Forward* exponential behaviour where the definition applies a basic power law using the exponent. Values less than zero are clamped. - *basicRev*: *Basic Reverse* exponential behaviour where the definition applies a basic power law using the exponent. Values less than zero are clamped. - *basicMirrorFwd*: *Basic Mirror Forward* exponential behaviour where the definition applies a basic power law using the exponent for values greater than or equal to zero and mirrors the function for values less than zero (i.e. rotationally symmetric around the origin). - *basicMirrorRev*: *Basic Mirror Reverse* exponential behaviour where the definition applies a basic power law using the exponent for values greater than or equal to zero and mirrors the function for values less than zero (i.e. rotationally symmetric around the origin). - *basicPassThruFwd*: *Basic Pass Forward* exponential behaviour where the definition applies a basic power law using the exponent for values greater than or equal to zero and passes values less than zero unchanged. - *basicPassThruRev*: *Basic Pass Reverse* exponential behaviour where the definition applies a basic power law using the exponent for values greater than or equal to zero and passes values less than zero unchanged. Returns ------- :class:`numpy.floating` or :class:`numpy.ndarray` Exponentially converted data. Examples -------- >>> exponent_function_basic(0.18, 2.2) # doctest: +ELLIPSIS 0.0229932... >>> exponent_function_basic(-0.18, 2.2) 0.0 >>> exponent_function_basic(0.18, 2.2, 'basicRev') # doctest: +ELLIPSIS 0.4586564... >>> exponent_function_basic(-0.18, 2.2, 'basicRev') 0.0 >>> exponent_function_basic( # doctest: +ELLIPSIS ... 0.18, 2.2, 'basicMirrorFwd') 0.0229932... >>> exponent_function_basic( # doctest: +ELLIPSIS ... -0.18, 2.2, 'basicMirrorFwd') -0.0229932... >>> exponent_function_basic( # doctest: +ELLIPSIS ... 0.18, 2.2, 'basicMirrorRev') 0.4586564... >>> exponent_function_basic( # doctest: +ELLIPSIS ... -0.18, 2.2, 'basicMirrorRev') -0.4586564... >>> exponent_function_basic( # doctest: +ELLIPSIS ... 0.18, 2.2, 'basicPassThruFwd') 0.0229932... >>> exponent_function_basic( # doctest: +ELLIPSIS ... -0.18, 2.2, 'basicPassThruFwd') -0.1799999... >>> exponent_function_basic( # doctest: +ELLIPSIS ... 0.18, 2.2, 'basicPassThruRev') 0.4586564... >>> exponent_function_basic( # doctest: +ELLIPSIS ... -0.18, 2.2, 'basicPassThruRev') -0.1799999... """ x = as_float_array(x) exponent = as_float_array(exponent) style = validate_method( style, [ "basicFwd", "basicRev", "basicMirrorFwd", "basicMirrorRev", "basicPassThruFwd", "basicPassThruRev", ], '"{0}" style is invalid, it must be one of {1}!', ) def exponent_forward(x: NDArray) -> NDArray: """Return the input raised to the exponent value.""" return x**exponent def exponent_reverse(y: NDArray) -> NDArray: """Return the input raised to the inverse exponent value.""" return y**(as_float_array(1) / exponent) if style == "basicfwd": return as_float(np.where(x >= 0, exponent_forward(x), 0)) elif style == "basicrev": return as_float(np.where(x >= 0, exponent_reverse(x), 0)) elif style == "basicmirrorfwd": return as_float( np.where(x >= 0, exponent_forward(x), -exponent_forward(-x))) elif style == "basicmirrorrev": return as_float( np.where(x >= 0, exponent_reverse(x), -exponent_reverse(-x))) elif style == "basicpassthrufwd": return as_float(np.where(x >= 0, exponent_forward(x), x)) else: # style == 'basicpassthrurev' return as_float(np.where(x >= 0, exponent_reverse(x), x))
def exponent_function_monitor_curve( x: FloatingOrArrayLike, exponent: FloatingOrArrayLike = 1, offset: FloatingOrArrayLike = 0, style: Union[Literal["monCurveFwd", "monCurveRev", "monCurveMirrorFwd", "monCurveMirrorRev", ], str, ] = "monCurveFwd", ) -> FloatingOrNDArray: """ Define the *Monitor Curve* exponent transfer function. Parameters ---------- x Data to undergo the monitor curve exponential conversion. exponent Exponent value used for the conversion. offset Offset value used for the conversion. style Defines the behaviour for the transfer function to operate: - *monCurveFwd*: *Monitor Curve Forward* exponential behaviour where the definition applies a power law function with a linear segment near the origin. - *monCurveRev*: *Monitor Curve Reverse* exponential behaviour where the definition applies a power law function with a linear segment near the origin. - *monCurveMirrorFwd*: *Monitor Curve Mirror Forward* exponential behaviour where the definition applies a power law function with a linear segment near the origin and mirrors the function for values less than zero (i.e. rotationally symmetric around the origin). - *monCurveMirrorRev*: *Monitor Curve Mirror Reverse* exponential behaviour where the definition applies a power law function with a linear segment near the origin and mirrors the function for values less than zero (i.e. rotationally symmetric around the origin). Returns ------- :class:`numpy.floating` or :class:`numpy.ndarray` Exponentially converted data. Examples -------- >>> exponent_function_monitor_curve( # doctest: +ELLIPSIS ... 0.18, 2.2, 0.001) 0.0232240... >>> exponent_function_monitor_curve( # doctest: +ELLIPSIS ... -0.18, 2.2, 0.001) -0.0002054... >>> exponent_function_monitor_curve( # doctest: +ELLIPSIS ... 0.18, 2.2, 0.001, 'monCurveRev') 0.4581151... >>> exponent_function_monitor_curve( # doctest: +ELLIPSIS ... -0.18, 2.2, 0.001, 'monCurveRev') -157.7302795... >>> exponent_function_monitor_curve( # doctest: +ELLIPSIS ... 0.18, 2.2, 2, 'monCurveMirrorFwd') 0.1679399... >>> exponent_function_monitor_curve( # doctest: +ELLIPSIS ... -0.18, 2.2, 0.001, 'monCurveMirrorFwd') -0.0232240... >>> exponent_function_monitor_curve( # doctest: +ELLIPSIS ... 0.18, 2.2, 0.001, 'monCurveMirrorRev') 0.4581151... >>> exponent_function_monitor_curve( # doctest: +ELLIPSIS ... -0.18, 2.2, 0.001, 'monCurveMirrorRev') -0.4581151... """ x = as_float_array(x) exponent = as_float_array(exponent) offset = as_float_array(offset) style = validate_method( style, [ "monCurveFwd", "monCurveRev", "monCurveMirrorFwd", "monCurveMirrorRev", ], '"{0}" style is invalid, it must be one of {1}!', ) with suppress_warnings(python_warnings=True): s = as_float_array( ((exponent - 1) / offset) * ((exponent * offset) / ((exponent - 1) * (offset + 1)))**exponent) s[np.isnan(s)] = 1 def monitor_curve_forward(x: NDArray, offset: NDArray, exponent: NDArray) -> NDArray: """Define the *Monitor Curve Forward* function.""" x_break = offset / (exponent - 1) return np.where( x >= x_break, ((x + offset) / (1 + offset))**exponent, x * s, ) def monitor_curve_reverse(y: NDArray, offset: NDArray, exponent: NDArray) -> NDArray: """Define the *Monitor Curve Reverse* function.""" y_break = ((exponent * offset) / ((exponent - 1) * (1 + offset)))**exponent return np.where( y >= y_break, ((1 + offset) * (y**(1 / exponent))) - offset, y / s, ) if style == "moncurvefwd": return as_float(monitor_curve_forward(x, offset, exponent)) elif style == "moncurverev": return as_float(monitor_curve_reverse(x, offset, exponent)) elif style == "moncurvemirrorfwd": return as_float( np.where( x >= 0, monitor_curve_forward(x, offset, exponent), -monitor_curve_forward(-x, offset, exponent), )) else: # style == 'moncurvemirrorrev' return as_float( np.where( x >= 0, monitor_curve_reverse(x, offset, exponent), -monitor_curve_reverse(-x, offset, exponent), ))
def chromatic_adaptation_Zhai2018( XYZ_b: ArrayLike, XYZ_wb: ArrayLike, XYZ_wd: ArrayLike, D_b: FloatingOrArrayLike = 1, D_d: FloatingOrArrayLike = 1, XYZ_wo: ArrayLike = np.array([1, 1, 1]), transform: Union[Literal["CAT02", "CAT16"], str] = "CAT02", ) -> NDArray: """ Adapt given sample colour :math:`XYZ_{\\beta}` tristimulus values from input viewing conditions under :math:`\\beta` illuminant to output viewing conditions under :math:`\\delta` illuminant using *Zhai and Luo (2018)* chromatic adaptation model. According to the definition of :math:`D`, a one-step CAT such as CAT02 can only be used to transform colors from an incomplete adapted field into a complete adapted field. When CAT02 are used to transform an incomplete to incomplete case, :math:`D` has no baseline level to refer to. *Smet et al. (2017)* proposed a new concept of two-step CAT to replace the present CATs such as CAT02 with only one-step transform in order to define :math:`D` more clearly. A two-step CAT involves an illuminant representing the baseline states between the test and reference illuminants for the calculation. In the first step the test color is transformed from test illuminant to the baseline illuminant (:math:`BI`), and it is then transformed to the reference illuminant Degrees of adaptation under the other illuminants should be calculated relative to the adaptation under the :math:`BI`. When :math:`D` becomes lower towards zero, the adaptation point of the observer moves towards the :math:`BI`. Therefore, the chromaticity of the :math:`BI` should be an intrinsic property of the human vision system. Parameters ---------- XYZ_b Sample colour :math:`XYZ_{\\beta}` under input illuminant :math:`\\beta`. XYZ_wb Input illuminant :math:`\\beta`. XYZ_wd Output illuminant :math:`\\delta`. D_b Degree of adaptation :math:`D_{\\beta}` of input illuminant :math:`\\beta`. D_d Degree of adaptation :math:`D_{\\delta}` of output illuminant :math:`\\delta`. XYZ_wo Baseline illuminant (:math:`BI`) :math:`o`. transform Chromatic adaptation transform. Returns ------- :class:`numpy.ndarray` Sample corresponding colour :math:`XYZ_{\\delta}` tristimulus values under output illuminant :math:`D_{\\delta}`. Notes ----- +------------+-----------------------+---------------+ | **Domain** | **Scale - Reference** | **Scale - 1** | +============+=======================+===============+ | ``XYZ_b`` | [0, 1] | [0, 1] | +------------+-----------------------+---------------+ | ``XYZ_wb`` | [0, 1] | [0, 1] | +------------+-----------------------+---------------+ | ``XYZ_wd`` | [0, 1] | [0, 1] | +------------+-----------------------+---------------+ | ``XYZ_wo`` | [0, 1] | [0, 1] | +------------+-----------------------+---------------+ +------------+-----------------------+---------------+ | **Range** | **Scale - Reference** | **Scale - 1** | +============+=======================+===============+ | ``XYZ_d`` | [0, 1] | [0, 1] | +------------+-----------------------+---------------+ References ---------- :cite:`Zhai2018` Examples -------- >>> XYZ_b = np.array([48.900, 43.620, 6.250]) >>> XYZ_wb = np.array([109.850, 100, 35.585]) >>> XYZ_wd = np.array([95.047, 100, 108.883]) >>> D_b = 0.9407 >>> D_d = 0.9800 >>> XYZ_wo = np.array([100, 100, 100]) >>> chromatic_adaptation_Zhai2018( ... XYZ_b, XYZ_wb, XYZ_wd, D_b, D_d, XYZ_wo) # doctest: +ELLIPSIS array([ 39.1856164..., 42.1546179..., 19.2367203...]) >>> XYZ_d = np.array([39.18561644, 42.15461798, 19.23672036]) >>> chromatic_adaptation_Zhai2018( ... XYZ_d, XYZ_wd, XYZ_wb, D_d, D_b, XYZ_wo) # doctest: +ELLIPSIS array([ 48.9 , 43.62, 6.25]) """ XYZ_b = to_domain_100(XYZ_b) XYZ_wb = to_domain_100(XYZ_wb) XYZ_wd = to_domain_100(XYZ_wd) XYZ_wo = to_domain_100(XYZ_wo) D_b = as_float_array(D_b) D_d = as_float_array(D_d) Y_wb = XYZ_wb[..., 1][..., np.newaxis] Y_wd = XYZ_wd[..., 1][..., np.newaxis] Y_wo = XYZ_wo[..., 1][..., np.newaxis] transform = validate_method(transform, ["CAT02", "CAT16"]) M = CHROMATIC_ADAPTATION_TRANSFORMS[transform] RGB_b = vector_dot(M, XYZ_b) RGB_wb = vector_dot(M, XYZ_wb) RGB_wd = vector_dot(M, XYZ_wd) RGB_wo = vector_dot(M, XYZ_wo) D_RGB_b = D_b * (Y_wb / Y_wo) * (RGB_wo / RGB_wb) + 1 - D_b D_RGB_d = D_d * (Y_wd / Y_wo) * (RGB_wo / RGB_wd) + 1 - D_d D_RGB = D_RGB_b / D_RGB_d RGB_d = D_RGB * RGB_b XYZ_d = vector_dot(np.linalg.inv(M), RGB_d) return from_range_100(XYZ_d)
def plot_hull_section_colours( hull: trimesh.Trimesh, # type: ignore[name-defined] # noqa model: Union[Literal["CAM02LCD", "CAM02SCD", "CAM02UCS", "CAM16LCD", "CAM16SCD", "CAM16UCS", "CIE XYZ", "CIE xyY", "CIE Lab", "CIE Luv", "CIE UCS", "CIE UVW", "DIN99", "Hunter Lab", "Hunter Rdab", "ICaCb", "ICtCp", "IPT", "IgPgTg", "Jzazbz", "OSA UCS", "Oklab", "hdr-CIELAB", "hdr-IPT", ], str, ] = "CIE xyY", axis: Union[Literal["+z", "+x", "+y"], str] = "+z", origin: Floating = 0.5, normalise: Boolean = True, section_colours: Optional[Union[ArrayLike, str]] = None, section_opacity: Floating = 1, convert_kwargs: Optional[Dict] = None, samples: Integer = 256, **kwargs: Any, ) -> Tuple[plt.Figure, plt.Axes]: """ Plot the section colours of given *trimesh* hull along given axis and origin. Parameters ---------- hull *Trimesh* hull. model Colourspace model, see :attr:`colour.COLOURSPACE_MODELS` attribute for the list of supported colourspace models. axis Axis the hull section will be normal to. origin Coordinate along ``axis`` at which to plot the hull section. normalise Whether to normalise ``axis`` to the extent of the hull along it. section_colours Colours of the hull section, if ``section_colours`` is set to *RGB*, the colours will be computed according to the corresponding coordinates. section_opacity Opacity of the hull section colours. convert_kwargs Keyword arguments for the :func:`colour.convert` definition. samples Samples count on one axis when computing the hull section colours. 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 -------- >>> from colour.models import RGB_COLOURSPACE_sRGB >>> from colour.utilities import is_trimesh_installed >>> vertices, faces, _outline = primitive_cube(1, 1, 1, 64, 64, 64) >>> XYZ_vertices = RGB_to_XYZ( ... vertices['position'] + 0.5, ... RGB_COLOURSPACE_sRGB.whitepoint, ... RGB_COLOURSPACE_sRGB.whitepoint, ... RGB_COLOURSPACE_sRGB.matrix_RGB_to_XYZ, ... ) >>> if is_trimesh_installed: ... import trimesh ... hull = trimesh.Trimesh(XYZ_vertices, faces, process=False) ... plot_hull_section_colours(hull, section_colours='RGB') ... # doctest: +ELLIPSIS (<Figure size ... with 1 Axes>, <...AxesSubplot...>) .. image:: ../_static/Plotting_Plot_Hull_Section_Colours.png :align: center :alt: plot_hull_section_colours """ axis = validate_method( axis, ["+z", "+x", "+y"], '"{0}" axis is invalid, it must be one of {1}!', ) hull = hull.copy() settings: Dict[str, Any] = {"uniform": True} settings.update(kwargs) _figure, axes = artist(**settings) section_colours = cast( ArrayLike, optional(section_colours, HEX_to_RGB(CONSTANTS_COLOUR_STYLE.colour.average)), ) convert_kwargs = optional(convert_kwargs, {}) # Luminance / Lightness reordered along "z" axis. with suppress_warnings(python_warnings=True): ijk_vertices = colourspace_model_axis_reorder( convert(hull.vertices, "CIE XYZ", model, **convert_kwargs), model) ijk_vertices = np.nan_to_num(ijk_vertices) ijk_vertices *= COLOURSPACE_MODELS_DOMAIN_RANGE_SCALE_1_TO_REFERENCE[ model] hull.vertices = ijk_vertices if axis == "+x": index_origin = 0 elif axis == "+y": index_origin = 1 elif axis == "+z": index_origin = 2 plane = MAPPING_AXIS_TO_PLANE[axis] section = hull_section(hull, axis, origin, normalise) padding = 0.1 * np.mean( COLOURSPACE_MODELS_DOMAIN_RANGE_SCALE_1_TO_REFERENCE[model]) min_x = np.min(ijk_vertices[..., plane[0]]) - padding max_x = np.max(ijk_vertices[..., plane[0]]) + padding min_y = np.min(ijk_vertices[..., plane[1]]) - padding max_y = np.max(ijk_vertices[..., plane[1]]) + padding extent = (min_x, max_x, min_y, max_y) use_RGB_section_colours = str(section_colours).upper() == "RGB" if use_RGB_section_colours: ii, jj = np.meshgrid( np.linspace(min_x, max_x, samples), np.linspace(max_y, min_y, samples), ) ij = tstack([ii, jj]) ijk_section = full((samples, samples, 3), np.median(section[..., index_origin])) ijk_section[..., plane] = ij ijk_section /= COLOURSPACE_MODELS_DOMAIN_RANGE_SCALE_1_TO_REFERENCE[ model] XYZ_section = convert( colourspace_model_axis_reorder(ijk_section, model, "Inverse"), model, "CIE XYZ", **convert_kwargs, ) RGB_section = XYZ_to_plotting_colourspace(XYZ_section) else: section_colours = np.hstack([section_colours, section_opacity]) facecolor = "none" if use_RGB_section_colours else section_colours polygon = Polygon( section[..., plane], facecolor=facecolor, edgecolor="none", zorder=CONSTANTS_COLOUR_STYLE.zorder.background_polygon, ) axes.add_patch(polygon) if use_RGB_section_colours: image = axes.imshow( np.clip(RGB_section, 0, 1), interpolation="bilinear", extent=extent, clip_path=None, alpha=section_opacity, zorder=CONSTANTS_COLOUR_STYLE.zorder.background_polygon, ) image.set_clip_path(polygon) settings = { "axes": axes, "bounding_box": extent, } settings.update(kwargs) return render(**settings)
def plot_multi_sds_colour_quality_scales_bars( sds: Union[ Sequence[Union[SpectralDistribution, MultiSpectralDistributions]], MultiSpectralDistributions, ], method: Union[ Literal["NIST CQS 7.4", "NIST CQS 9.0"], str ] = "NIST CQS 9.0", **kwargs: Any, ) -> Tuple[plt.Figure, plt.Axes]: """ Plot the *Colour Quality Scale* (CQS) of given illuminants or light sources spectral distributions. Parameters ---------- sds Spectral distributions or multi-spectral distributions to plot. `sds` can be a single :class:`colour.MultiSpectralDistributions` class instance, a list of :class:`colour.MultiSpectralDistributions` class instances or a list of :class:`colour.SpectralDistribution` class instances. method *Colour Quality Scale* (CQS) computation method. Other Parameters ---------------- kwargs {:func:`colour.plotting.artist`, :func:`colour.plotting.quality.plot_colour_quality_bars`, :func:`colour.plotting.render`}, See the documentation of the previously listed definitions. Returns ------- :class:`tuple` Current figure and axes. Examples -------- >>> from colour import (SDS_ILLUMINANTS, ... SDS_LIGHT_SOURCES) >>> illuminant = SDS_ILLUMINANTS['FL2'] >>> light_source = SDS_LIGHT_SOURCES['Kinoton 75P'] >>> plot_multi_sds_colour_quality_scales_bars([illuminant, light_source]) ... # doctest: +ELLIPSIS (<Figure size ... with 1 Axes>, <...AxesSubplot...>) .. image:: ../_static/Plotting_\ Plot_Multi_SDS_Colour_Quality_Scales_Bars.png :align: center :alt: plot_multi_sds_colour_quality_scales_bars """ method = validate_method(method, COLOUR_QUALITY_SCALE_METHODS) sds_converted = sds_and_msds_to_sds(sds) settings: Dict[str, Any] = dict(kwargs) settings.update({"standalone": False}) specifications = cast( List[ColourRendering_Specification_CQS], [colour_quality_scale(sd, True, method) for sd in sds_converted], ) _figure, axes = plot_colour_quality_bars(specifications, **settings) title = ( f"Colour Quality Scale - " f"{', '.join([sd.strict_name for sd in sds_converted])}" ) settings = {"axes": axes, "title": title} settings.update(kwargs) return render(**settings)
def fill_nan( self, method: Union[ Literal["Constant", "Interpolation"], str ] = "Interpolation", default: Number = 0, ) -> AbstractContinuousFunction: """ Fill NaNs in independent domain variable :math:`x` and corresponding range variable :math:`y` using given method. Parameters ---------- method *Interpolation* method linearly interpolates through the NaNs, *Constant* method replaces NaNs with ``default``. default Value to use with the *Constant* method. Returns ------- :class:`colour.continuous.Signal` NaNs filled continuous signal. Examples -------- >>> range_ = np.linspace(10, 100, 10) >>> signal = Signal(range_) >>> signal[3:7] = np.nan >>> print(signal) [[ 0. 10.] [ 1. 20.] [ 2. 30.] [ 3. nan] [ 4. nan] [ 5. nan] [ 6. nan] [ 7. 80.] [ 8. 90.] [ 9. 100.]] >>> print(signal.fill_nan()) [[ 0. 10.] [ 1. 20.] [ 2. 30.] [ 3. 40.] [ 4. 50.] [ 5. 60.] [ 6. 70.] [ 7. 80.] [ 8. 90.] [ 9. 100.]] >>> signal[3:7] = np.nan >>> print(signal.fill_nan(method='Constant')) [[ 0. 10.] [ 1. 20.] [ 2. 30.] [ 3. 0.] [ 4. 0.] [ 5. 0.] [ 6. 0.] [ 7. 80.] [ 8. 90.] [ 9. 100.]] """ method = validate_method(method, ["Interpolation", "Constant"]) self._fill_domain_nan(method, default) self._fill_range_nan(method, default) return self