Exemple #1
0
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
Exemple #2
0
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))
Exemple #4
0
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))
Exemple #5
0
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))
Exemple #6
0
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))
Exemple #7
0
    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
Exemple #8
0
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))
Exemple #9
0
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)
Exemple #10
0
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)
Exemple #11
0
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
Exemple #12
0
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)
Exemple #13
0
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))
Exemple #14
0
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)
Exemple #15
0
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)
Exemple #16
0
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
Exemple #17
0
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)
Exemple #18
0
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))
Exemple #19
0
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))
Exemple #20
0
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)
Exemple #21
0
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))
Exemple #22
0
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
Exemple #24
0
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))
Exemple #25
0
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))
Exemple #26
0
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),
            ))
Exemple #27
0
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)
Exemple #28
0
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)
Exemple #29
0
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)
Exemple #30
0
    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