Example #1
0
def visible_spectrum_plot(cmfs='CIE 1931 2 Degree Standard Observer',
                          out_of_gamut_clipping=True,
                          **kwargs):
    """
    Plots the visible colours spectrum using given standard observer *CIE XYZ*
    colour matching functions.

    Parameters
    ----------
    cmfs : unicode, optional
        Standard observer colour matching functions used for spectrum creation.
    out_of_gamut_clipping : bool, optional
        Whether to clip out of gamut colours otherwise, the colours will be
        offset by the absolute minimal colour leading to a rendering on
        gray background, less saturated and smoother.

    Other Parameters
    ----------------
    \**kwargs : dict, optional
        {:func:`colour.plotting.render`},
        Please refer to the documentation of the previously listed definition.

    Returns
    -------
    Figure
        Current figure or None.

    References
    ----------
    -   :cite:`Spiker2015a`

    Examples
    --------
    >>> visible_spectrum_plot()  # doctest: +SKIP
    """

    settings = {'y_label': None, 'y_ticker': False, 'standalone': False}

    single_spd_plot(ones_spd(DEFAULT_SPECTRAL_SHAPE),
                    cmfs=cmfs,
                    out_of_gamut_clipping=out_of_gamut_clipping,
                    **settings)

    cmfs = get_cmfs(cmfs)

    settings = {
        'title': 'The Visible Spectrum - {0}'.format(cmfs.strict_name),
        'x_label': 'Wavelength $\\lambda$ (nm)',
        'standalone': True
    }
    settings.update(kwargs)

    return render(**settings)
Example #2
0
    def test_spectral_to_aces_relative_exposure_values(self):
        """
        Tests :func:`colour.models.rgb.aces_it.
spectral_to_aces_relative_exposure_values` definition.
        """

        shape = ACES_RICD.shape
        grey_reflector = constant_spd(0.18, shape)
        np.testing.assert_almost_equal(
            spectral_to_aces_relative_exposure_values(grey_reflector),
            np.array([0.18, 0.18, 0.18]))

        perfect_reflector = ones_spd(shape)
        np.testing.assert_almost_equal(
            spectral_to_aces_relative_exposure_values(perfect_reflector),
            np.array([0.97783784, 0.97783784, 0.97783784]))

        dark_skin = (COLOURCHECKERS_SPDS['ColorChecker N Ohta']['dark skin'])
        np.testing.assert_almost_equal(
            spectral_to_aces_relative_exposure_values(dark_skin),
            np.array([0.11876978, 0.08708666, 0.0589442]))
Example #3
0
    def test_spectral_to_aces_relative_exposure_values(self):
        """
        Tests :func:`colour.models.rgb.aces_it.
spectral_to_aces_relative_exposure_values` definition.
        """

        shape = ACES_RICD.shape
        grey_reflector = constant_spd(0.18, shape)
        np.testing.assert_almost_equal(
            spectral_to_aces_relative_exposure_values(grey_reflector),
            np.array([0.18, 0.18, 0.18]))

        perfect_reflector = ones_spd(shape)
        np.testing.assert_almost_equal(
            spectral_to_aces_relative_exposure_values(perfect_reflector),
            np.array([0.97783784, 0.97783784, 0.97783784]))

        dark_skin = (
            COLOURCHECKERS_SPDS.get('ColorChecker N Ohta').get('dark skin'))
        np.testing.assert_almost_equal(
            spectral_to_aces_relative_exposure_values(dark_skin),
            np.array([0.11876978, 0.08708666, 0.0589442]))
Example #4
0
def spectral_to_XYZ(
        spd,
        cmfs=STANDARD_OBSERVERS_CMFS['CIE 1931 2 Degree Standard Observer'],
        illuminant=ones_spd(ASTME30815_PRACTISE_SHAPE),
        method='ASTM E308-15',
        **kwargs):
    """
    Converts given spectral power distribution to *CIE XYZ* tristimulus values
    using given colour matching functions, illuminant and method.

    Parameters
    ----------
    spd : SpectralPowerDistribution
        Spectral power distribution.
    cmfs : XYZ_ColourMatchingFunctions
        Standard observer colour matching functions.
    illuminant : SpectralPowerDistribution, optional
        Illuminant spectral power distribution.
    method : unicode, optional
        **{'ASTM E308-15', 'Integration'}**,
        Computation method.

    Other Parameters
    ----------------
    use_practice_range : bool, optional
        {:func:`spectral_to_XYZ_ASTME30815`},
        Practise *ASTM E308-15* working wavelengths range is [360, 780],
        if `True` this argument will trim the colour matching functions
        appropriately.
    mi_5nm_omission_method : bool, optional
        {:func:`spectral_to_XYZ_ASTME30815`},
        5 nm measurement intervals spectral power distribution conversion to
        tristimulus values will use a 5 nm version of the colour matching
        functions instead of a table of tristimulus weighting factors.
    mi_20nm_interpolation_method : bool, optional
        {:func:`spectral_to_XYZ_ASTME30815`},
        20 nm measurement intervals spectral power distribution conversion to
        tristimulus values will use a dedicated interpolation method instead
        of a table of tristimulus weighting factors.

    Returns
    -------
    ndarray, (3,)
        *CIE XYZ* tristimulus values.

    Warning
    -------
    The output range of that definition is non standard!

    Notes
    -----
    -   Output *CIE XYZ* tristimulus values are in range [0, 100].

    Examples
    --------
    >>> from colour import (
    ...     CMFS, ILLUMINANTS_RELATIVE_SPDS, SpectralPowerDistribution)
    >>> cmfs = CMFS['CIE 1931 2 Degree Standard Observer']
    >>> data = {
    ...     400: 0.0641,
    ...     420: 0.0645,
    ...     440: 0.0562,
    ...     460: 0.0537,
    ...     480: 0.0559,
    ...     500: 0.0651,
    ...     520: 0.0705,
    ...     540: 0.0772,
    ...     560: 0.0870,
    ...     580: 0.1128,
    ...     600: 0.1360,
    ...     620: 0.1511,
    ...     640: 0.1688,
    ...     660: 0.1996,
    ...     680: 0.2397,
    ...     700: 0.2852}
    >>> spd = SpectralPowerDistribution('Sample', data)
    >>> illuminant = ILLUMINANTS_RELATIVE_SPDS['D50']
    >>> spectral_to_XYZ(  # doctest: +ELLIPSIS
    ...     spd, cmfs, illuminant)
    array([ 11.5290265...,   9.9502091...,   4.7098882...])
    >>> spectral_to_XYZ(  # doctest: +ELLIPSIS
    ...     spd, cmfs, illuminant, use_practice_range=False)
    array([ 11.5291275...,   9.9502369...,   4.7098811...])
    >>> spectral_to_XYZ(  # doctest: +ELLIPSIS
    ...     spd, cmfs, illuminant, method='Integration')
    array([ 11.5296285...,   9.9499467...,   4.7066079...])
    """

    function = SPECTRAL_TO_XYZ_METHODS[method]

    filter_kwargs(function, **kwargs)

    return function(spd, cmfs, illuminant, **kwargs)
Example #5
0
def spectral_to_XYZ_ASTME30815(
        spd,
        cmfs=STANDARD_OBSERVERS_CMFS['CIE 1931 2 Degree Standard Observer'],
        illuminant=ones_spd(ASTME30815_PRACTISE_SHAPE),
        use_practice_range=True,
        mi_5nm_omission_method=True,
        mi_20nm_interpolation_method=True):
    """
    Converts given spectral power distribution to *CIE XYZ* tristimulus values
    using given colour matching functions and illuminant accordingly to
    practise *ASTM E308-15* method [2]_.

    Parameters
    ----------
    spd : SpectralPowerDistribution
        Spectral power distribution.
    cmfs : XYZ_ColourMatchingFunctions
        Standard observer colour matching functions.
    illuminant : SpectralPowerDistribution, optional
        Illuminant spectral power distribution.
    use_practice_range : bool, optional
        Practise *ASTM E308-15* working wavelengths range is [360, 780],
        if `True` this argument will trim the colour matching functions
        appropriately.
    mi_5nm_omission_method : bool, optional
        5 nm measurement intervals spectral power distribution conversion to
        tristimulus values will use a 5 nm version of the colour matching
        functions instead of a table of tristimulus weighting factors.
    mi_20nm_interpolation_method : bool, optional
        20 nm measurement intervals spectral power distribution conversion to
        tristimulus values will use a dedicated interpolation method instead
        of a table of tristimulus weighting factors.

    Returns
    -------
    ndarray, (3,)
        *CIE XYZ* tristimulus values.

    Warning
    -------
    -   The tables of tristimulus weighting factors are cached in
        :attr:`_TRISTIMULUS_WEIGHTING_FACTORS_CACHE` attribute. Their
        identifier key is defined by the colour matching functions and
        illuminant names along the current shape such as:
        `CIE 1964 10 Degree Standard Observer, A, (360.0, 830.0, 10.0)`
        Considering the above, one should be mindful that using similar colour
        matching functions and illuminant names but with different spectral
        data will lead to unexpected behaviour.
    -   The output range of that definition is non standard!

    Notes
    -----
    -   Output *CIE XYZ* tristimulus values are in range [0, 100].

    Examples
    --------
    >>> from colour import (
    ...     CMFS, ILLUMINANTS_RELATIVE_SPDS, SpectralPowerDistribution)
    >>> cmfs = CMFS['CIE 1931 2 Degree Standard Observer']
    >>> data = {
    ...     400: 0.0641,
    ...     420: 0.0645,
    ...     440: 0.0562,
    ...     460: 0.0537,
    ...     480: 0.0559,
    ...     500: 0.0651,
    ...     520: 0.0705,
    ...     540: 0.0772,
    ...     560: 0.0870,
    ...     580: 0.1128,
    ...     600: 0.1360,
    ...     620: 0.1511,
    ...     640: 0.1688,
    ...     660: 0.1996,
    ...     680: 0.2397,
    ...     700: 0.2852}
    >>> spd = SpectralPowerDistribution('Sample', data)
    >>> illuminant = ILLUMINANTS_RELATIVE_SPDS['D50']
    >>> spectral_to_XYZ_ASTME30815(
    ...     spd, cmfs, illuminant)  # doctest: +ELLIPSIS
    array([ 11.5290265...,   9.9502091...,   4.7098882...])
    """

    if spd.shape.interval not in (1, 5, 10, 20):
        raise ValueError(
            'Tristimulus values conversion from spectral data accordingly to '
            'practise "ASTM E308-15" should be performed on spectral data '
            'with measurement interval of 1, 5, 10 or 20nm!')

    if use_practice_range:
        cmfs = cmfs.clone().trim_wavelengths(ASTME30815_PRACTISE_SHAPE)

    method = spectral_to_XYZ_tristimulus_weighting_factors_ASTME30815
    if spd.shape.interval == 1:
        method = spectral_to_XYZ_integration
    elif spd.shape.interval == 5 and mi_5nm_omission_method:
        if cmfs.shape.interval != 5:
            cmfs = cmfs.clone().interpolate(SpectralShape(interval=5))
        method = spectral_to_XYZ_integration
    elif spd.shape.interval == 20 and mi_20nm_interpolation_method:
        spd = spd.clone()
        if spd.shape.boundaries != cmfs.shape.boundaries:
            warning(
                'Trimming "{0}" spectral power distribution shape to "{1}" '
                'colour matching functions shape.'.format(illuminant, cmfs))
            spd.trim_wavelengths(cmfs.shape)

        # Extrapolation of additional 20nm padding intervals.
        spd.align(SpectralShape(spd.shape.start - 20, spd.shape.end + 20, 10))
        for i in range(2):
            spd[spd.wavelengths[i]] = (3 * spd.values[i + 2] -
                                       3 * spd.values[i + 4] +
                                       spd.values[i + 6])
            i_e = len(spd) - 1 - i
            spd[spd.wavelengths[i_e]] = (spd.values[i_e - 6] -
                                         3 * spd.values[i_e - 4] +
                                         3 * spd.values[i_e - 2])

        # Interpolating every odd numbered values.
        # TODO: Investigate code vectorisation.
        for i in range(3, len(spd) - 3, 2):
            spd[spd.wavelengths[i]] = (-0.0625 * spd.values[i - 3] +
                                       0.5625 * spd.values[i - 1] +
                                       0.5625 * spd.values[i + 1] -
                                       0.0625 * spd.values[i + 3])

        # Discarding the additional 20nm padding intervals.
        spd.trim_wavelengths(SpectralShape(spd.shape.start + 20,
                                           spd.shape.end - 20,
                                           10))

    XYZ = method(spd, cmfs, illuminant)

    return XYZ
Example #6
0
def spectral_to_XYZ_tristimulus_weighting_factors_ASTME30815(
        spd,
        cmfs=STANDARD_OBSERVERS_CMFS['CIE 1931 2 Degree Standard Observer'],
        illuminant=ones_spd(ASTME30815_PRACTISE_SHAPE)):
    """
    Converts given spectral power distribution to *CIE XYZ* tristimulus values
    using given colour matching functions and illuminant using a table
    of tristimulus weighting factors accordingly to practise
    *ASTM E308-15* method [2]_.

    Parameters
    ----------
    spd : SpectralPowerDistribution
        Spectral power distribution.
    cmfs : XYZ_ColourMatchingFunctions
        Standard observer colour matching functions.
    illuminant : SpectralPowerDistribution, optional
        Illuminant spectral power distribution.

    Returns
    -------
    ndarray, (3,)
        *CIE XYZ* tristimulus values.

    Warning
    -------
    The output range of that definition is non standard!

    Notes
    -----
    -   Output *CIE XYZ* tristimulus values are in range [0, 100].

    Examples
    --------
    >>> from colour import (
    ...     CMFS, ILLUMINANTS_RELATIVE_SPDS, SpectralPowerDistribution)
    >>> cmfs = CMFS['CIE 1931 2 Degree Standard Observer']
    >>> data = {
    ...     400: 0.0641,
    ...     420: 0.0645,
    ...     440: 0.0562,
    ...     460: 0.0537,
    ...     480: 0.0559,
    ...     500: 0.0651,
    ...     520: 0.0705,
    ...     540: 0.0772,
    ...     560: 0.0870,
    ...     580: 0.1128,
    ...     600: 0.1360,
    ...     620: 0.1511,
    ...     640: 0.1688,
    ...     660: 0.1996,
    ...     680: 0.2397,
    ...     700: 0.2852}
    >>> spd = SpectralPowerDistribution('Sample', data)
    >>> illuminant = ILLUMINANTS_RELATIVE_SPDS['D50']
    >>> spectral_to_XYZ_tristimulus_weighting_factors_ASTME30815(
    ...     spd, cmfs, illuminant)  # doctest: +ELLIPSIS
    array([ 11.5296311...,   9.9505845...,   4.7098037...])
    """

    if illuminant.shape != cmfs.shape:
        warning('Aligning "{0}" illuminant shape to "{1}" colour matching '
                'functions shape.'.format(illuminant, cmfs))
        illuminant = illuminant.clone().align(cmfs.shape)

    if spd.shape.boundaries != cmfs.shape.boundaries:
        warning('Trimming "{0}" spectral power distribution shape to "{1}" '
                'colour matching functions shape.'.format(illuminant, cmfs))
        spd = spd.clone().trim_wavelengths(cmfs.shape)

    W = tristimulus_weighting_factors_ASTME202211(
        cmfs, illuminant, SpectralShape(
            cmfs.shape.start, cmfs.shape.end, spd.shape.interval))
    start_w = cmfs.shape.start
    end_w = cmfs.shape.start + spd.shape.interval * (W.shape[0] - 1)
    W = adjust_tristimulus_weighting_factors_ASTME30815(
        W, SpectralShape(start_w, end_w, spd.shape.interval), spd.shape)
    R = spd.values

    XYZ = np.sum(W * R[..., np.newaxis], axis=0)

    return XYZ
Example #7
0
def spectral_to_XYZ_integration(
        spd,
        cmfs=STANDARD_OBSERVERS_CMFS[
            'CIE 1931 2 Degree Standard Observer'],
        illuminant=ones_spd(
            STANDARD_OBSERVERS_CMFS[
                'CIE 1931 2 Degree Standard Observer'].shape)):
    """
    Converts given spectral power distribution to *CIE XYZ* tristimulus values
    using given colour matching functions and illuminant accordingly to
    classical integration method.

    Parameters
    ----------
    spd : SpectralPowerDistribution
        Spectral power distribution.
    cmfs : XYZ_ColourMatchingFunctions
        Standard observer colour matching functions.
    illuminant : SpectralPowerDistribution, optional
        Illuminant spectral power distribution.

    Returns
    -------
    ndarray, (3,)
        *CIE XYZ* tristimulus values.

    Warning
    -------
    The output range of that definition is non standard!

    Notes
    -----
    -   Output *CIE XYZ* tristimulus values are in range [0, 100].

    References
    ----------
    .. [3]  Wyszecki, G., & Stiles, W. S. (2000). Integration Replace by
            Summation. In Color Science: Concepts and Methods, Quantitative
            Data and Formulae (pp. 158–163). Wiley. ISBN:978-0471399186

    Examples
    --------
    >>> from colour import (
    ...     CMFS, ILLUMINANTS_RELATIVE_SPDS, SpectralPowerDistribution)
    >>> cmfs = CMFS['CIE 1931 2 Degree Standard Observer']
    >>> data = {
    ...     400: 0.0641,
    ...     420: 0.0645,
    ...     440: 0.0562,
    ...     460: 0.0537,
    ...     480: 0.0559,
    ...     500: 0.0651,
    ...     520: 0.0705,
    ...     540: 0.0772,
    ...     560: 0.0870,
    ...     580: 0.1128,
    ...     600: 0.1360,
    ...     620: 0.1511,
    ...     640: 0.1688,
    ...     660: 0.1996,
    ...     680: 0.2397,
    ...     700: 0.2852}
    >>> spd = SpectralPowerDistribution('Sample', data)
    >>> illuminant = ILLUMINANTS_RELATIVE_SPDS['D50']
    >>> spectral_to_XYZ_integration(  # doctest: +ELLIPSIS
    ...     spd, cmfs, illuminant)
    array([ 11.5296285...,   9.9499467...,   4.7066079...])
    """

    if illuminant.shape != cmfs.shape:
        warning('Aligning "{0}" illuminant shape to "{1}" colour matching '
                'functions shape.'.format(illuminant, cmfs))
        illuminant = illuminant.clone().align(cmfs.shape)

    if spd.shape != cmfs.shape:
        warning('Aligning "{0}" spectral power distribution shape to "{1}" '
                'colour matching functions shape.'.format(spd, cmfs))
        spd = spd.clone().align(cmfs.shape)

    S = illuminant.values
    x_bar, y_bar, z_bar = tsplit(cmfs.values)
    R = spd.values
    dw = cmfs.shape.interval

    k = 100 / (np.sum(y_bar * S) * dw)

    X_p = R * x_bar * S * dw
    Y_p = R * y_bar * S * dw
    Z_p = R * z_bar * S * dw

    XYZ = k * np.sum(np.array([X_p, Y_p, Z_p]), axis=-1)

    return XYZ
Example #8
0
def spectral_to_XYZ(spd,
                    cmfs=STANDARD_OBSERVERS_CMFS.get(
                        'CIE 1931 2 Degree Standard Observer'),
                    illuminant=ones_spd(
                        STANDARD_OBSERVERS_CMFS.get(
                            'CIE 1931 2 Degree Standard Observer').shape),
                    method='ASTM E308–15',
                    **kwargs):
    """
    Converts given spectral power distribution to *CIE XYZ* tristimulus values
    using given colour matching functions, illuminant and method.

    Parameters
    ----------
    spd : SpectralPowerDistribution
        Spectral power distribution.
    cmfs : XYZ_ColourMatchingFunctions
        Standard observer colour matching functions.
    illuminant : SpectralPowerDistribution, optional
        Illuminant spectral power distribution.
    method : unicode, optional
        **{'ASTM E308–15', 'Integration'}**,
        Computation method.
    \**kwargs : dict, optional
        Keywords arguments.

    Returns
    -------
    ndarray, (3,)
        *CIE XYZ* tristimulus values.

    Warning
    -------
    The output range of that definition is non standard!

    Notes
    -----
    -   Output *CIE XYZ* tristimulus values are in range [0, 100].

    Examples
    --------
    >>> from colour import (
    ...     CMFS, ILLUMINANTS_RELATIVE_SPDS, SpectralPowerDistribution)
    >>> cmfs = CMFS.get('CIE 1931 2 Degree Standard Observer')
    >>> data = {
    ...     400: 0.0641,
    ...     420: 0.0645,
    ...     440: 0.0562,
    ...     460: 0.0537,
    ...     480: 0.0559,
    ...     500: 0.0651,
    ...     520: 0.0705,
    ...     540: 0.0772,
    ...     560: 0.0870,
    ...     580: 0.1128,
    ...     600: 0.1360,
    ...     620: 0.1511,
    ...     640: 0.1688,
    ...     660: 0.1996,
    ...     680: 0.2397,
    ...     700: 0.2852}
    >>> spd = SpectralPowerDistribution('Sample', data)
    >>> illuminant = ILLUMINANTS_RELATIVE_SPDS.get('D50')
    >>> spectral_to_XYZ(  # doctest: +ELLIPSIS
    ...     spd, cmfs, illuminant)
    array([ 11.5290265...,   9.9502091...,   4.7098882...])
    >>> spectral_to_XYZ(  # doctest: +ELLIPSIS
    ...     spd, cmfs, illuminant, use_practice_range=False)
    array([ 11.5291275...,   9.9502369...,   4.7098811...])
    >>> spectral_to_XYZ(  # doctest: +ELLIPSIS
    ...     spd, cmfs, illuminant, method='Integration')
    array([ 11.5296285...,   9.9499467...,   4.7066079...])
    """

    function = SPECTRAL_TO_XYZ_METHODS[method]

    filter_kwargs(function, **kwargs)

    return function(spd, cmfs, illuminant, **kwargs)
Example #9
0
def XYZ_to_spectral_Meng2015(
        XYZ,
        cmfs=STANDARD_OBSERVERS_CMFS['CIE 1931 2 Degree Standard Observer'],
        interval=5,
        tolerance=1e-10,
        maximum_iterations=2000):
    """
    Recovers the spectral power distribution of given *CIE XYZ* tristimulus
    values using *Meng et alii (2015)* method.

    Parameters
    ----------
    XYZ : array_like, (3,)
        *CIE XYZ* tristimulus values to recover the spectral power distribution
        from.
    cmfs : XYZ_ColourMatchingFunctions
        Standard observer colour matching functions.
    interval : numeric, optional
        Wavelength :math:`\lambda_{i}` range interval in nm. The smaller
        ``interval`` is, the longer the computations will be.
    tolerance : numeric, optional
        Tolerance for termination. The lower ``tolerance`` is, the smoother
        the recovered spectral power distribution will be.
    maximum_iterations : int, optional
        Maximum number of iterations to perform.

    Returns
    -------
    SpectralPowerDistribution
        Recovered spectral power distribution.

    Notes
    -----
    -   The definition used to convert spectrum to *CIE XYZ* tristimulus
        values is :func:`colour.colorimetry.spectral_to_XYZ_integration`
        definition because it processes any measurement interval opposed to
        :func:`colour.colorimetry.spectral_to_XYZ_ASTME30815` definition that
        handles only measurement interval of 1, 5, 10 or 20nm.

    References
    ----------
    -   :cite:`Meng2015c`

    Examples
    --------
    >>> from colour.utilities import numpy_print_options
    >>> XYZ = np.array([0.07049534, 0.10080000, 0.09558313])
    >>> spd = XYZ_to_spectral_Meng2015(XYZ, interval=10)
    >>> with numpy_print_options(suppress=True):
    ...     spd  # doctest: +ELLIPSIS
    SpectralPowerDistribution([[ 360.        ,    0.0788075...],
                               [ 370.        ,    0.0788543...],
                               [ 380.        ,    0.0788825...],
                               [ 390.        ,    0.0788714...],
                               [ 400.        ,    0.0788911...],
                               [ 410.        ,    0.07893  ...],
                               [ 420.        ,    0.0797471...],
                               [ 430.        ,    0.0813339...],
                               [ 440.        ,    0.0840145...],
                               [ 450.        ,    0.0892826...],
                               [ 460.        ,    0.0965359...],
                               [ 470.        ,    0.1053176...],
                               [ 480.        ,    0.1150921...],
                               [ 490.        ,    0.1244252...],
                               [ 500.        ,    0.1326083...],
                               [ 510.        ,    0.1390282...],
                               [ 520.        ,    0.1423548...],
                               [ 530.        ,    0.1414636...],
                               [ 540.        ,    0.1365195...],
                               [ 550.        ,    0.1277319...],
                               [ 560.        ,    0.1152622...],
                               [ 570.        ,    0.1004513...],
                               [ 580.        ,    0.0844187...],
                               [ 590.        ,    0.0686863...],
                               [ 600.        ,    0.0543013...],
                               [ 610.        ,    0.0423486...],
                               [ 620.        ,    0.0333861...],
                               [ 630.        ,    0.0273558...],
                               [ 640.        ,    0.0233407...],
                               [ 650.        ,    0.0211208...],
                               [ 660.        ,    0.0197248...],
                               [ 670.        ,    0.0187157...],
                               [ 680.        ,    0.0181510...],
                               [ 690.        ,    0.0179691...],
                               [ 700.        ,    0.0179247...],
                               [ 710.        ,    0.0178665...],
                               [ 720.        ,    0.0178005...],
                               [ 730.        ,    0.0177570...],
                               [ 740.        ,    0.0177090...],
                               [ 750.        ,    0.0175743...],
                               [ 760.        ,    0.0175058...],
                               [ 770.        ,    0.0174492...],
                               [ 780.        ,    0.0174984...],
                               [ 790.        ,    0.0175667...],
                               [ 800.        ,    0.0175657...],
                               [ 810.        ,    0.0175319...],
                               [ 820.        ,    0.0175184...],
                               [ 830.        ,    0.0175390...]],
                              interpolator=SpragueInterpolator,
                              interpolator_args={},
                              extrapolator=Extrapolator,
                              extrapolator_args={...})
    >>> spectral_to_XYZ_integration(spd) / 100  # doctest: +ELLIPSIS
    array([ 0.0705100...,  0.1007987...,  0.0956738...])
    """

    XYZ = np.asarray(XYZ)
    shape = SpectralShape(cmfs.shape.start, cmfs.shape.end, interval)
    cmfs = cmfs.copy().align(shape)
    illuminant = ones_spd(shape)
    spd = ones_spd(shape)

    def function_objective(a):
        """
        Objective function.
        """

        return np.sum(np.diff(a) ** 2)

    def function_constraint(a):
        """
        Function defining the constraint.
        """

        spd[:] = a
        return spectral_to_XYZ_integration(
            spd, cmfs=cmfs, illuminant=illuminant) - XYZ

    wavelengths = spd.wavelengths
    bins = wavelengths.size

    constraints = {'type': 'eq', 'fun': function_constraint}

    bounds = np.tile(np.array([0, 1000]), (bins, 1))

    result = minimize(
        function_objective,
        spd.values,
        method='SLSQP',
        constraints=constraints,
        bounds=bounds,
        options={'ftol': tolerance,
                 'maxiter': maximum_iterations})

    if not result.success:
        raise RuntimeError(
            'Optimization failed for {0} after {1} iterations: "{2}".'.format(
                XYZ, result.nit, result.message))

    return SpectralPowerDistribution(
        dict(zip(wavelengths, result.x * 100)),
        name='Meng (2015) - {0}'.format(XYZ))
Example #10
0
def spectral_to_XYZ(spd,
                    cmfs=STANDARD_OBSERVERS_CMFS.get(
                        'CIE 1931 2 Degree Standard Observer'),
                    illuminant=None):
    """
    Converts given spectral power distribution to *CIE XYZ* tristimulus values
    using given colour matching functions and illuminant.

    Parameters
    ----------
    spd : SpectralPowerDistribution
        Spectral power distribution.
    cmfs : XYZ_ColourMatchingFunctions
        Standard observer colour matching functions.
    illuminant : SpectralPowerDistribution, optional
        *Illuminant* spectral power distribution.

    Returns
    -------
    ndarray, (3,)
        *CIE XYZ* tristimulus values.

    Warning
    -------
    The output domain of that definition is non standard!

    Notes
    -----
    -   Output *CIE XYZ* tristimulus values are in domain [0, 100].

    References
    ----------
    .. [1]  Wyszecki, G., & Stiles, W. S. (2000). Integration Replace by
            Summation. In Color Science: Concepts and Methods, Quantitative
            Data and Formulae (pp. 158–163). Wiley. ISBN:978-0471399186

    Examples
    --------
    >>> from colour import (
    ...     CMFS, ILLUMINANTS_RELATIVE_SPDS, SpectralPowerDistribution)
    >>> cmfs = CMFS.get('CIE 1931 2 Degree Standard Observer')
    >>> data = {380: 0.0600, 390: 0.0600}
    >>> spd = SpectralPowerDistribution('Custom', data)
    >>> illuminant = ILLUMINANTS_RELATIVE_SPDS.get('D50')
    >>> spectral_to_XYZ(spd, cmfs, illuminant)  # doctest: +ELLIPSIS
    array([  4.5764852...e-04,   1.2964866...e-05,   2.1615807...e-03])
    """

    shape = cmfs.shape
    if spd.shape != cmfs.shape:
        spd = spd.clone().zeros(shape)

    if illuminant is None:
        illuminant = ones_spd(shape)
    else:
        if illuminant.shape != cmfs.shape:
            illuminant = illuminant.clone().zeros(shape)

    spd = spd.values
    x_bar, y_bar, z_bar = (cmfs.x_bar.values,
                           cmfs.y_bar.values,
                           cmfs.z_bar.values)
    illuminant = illuminant.values

    x_products = spd * x_bar * illuminant
    y_products = spd * y_bar * illuminant
    z_products = spd * z_bar * illuminant

    normalising_factor = 100 / np.sum(y_bar * illuminant)

    XYZ = np.array([normalising_factor * np.sum(x_products),
                    normalising_factor * np.sum(y_products),
                    normalising_factor * np.sum(z_products)])

    return XYZ
Example #11
0
def spectral_to_XYZ(spd,
                    cmfs=STANDARD_OBSERVERS_CMFS.get(
                        'CIE 1931 2 Degree Standard Observer'),
                    illuminant=None):
    """
    Converts given spectral power distribution to *CIE XYZ* colourspace using
    given colour matching functions and illuminant.

    Parameters
    ----------
    spd : SpectralPowerDistribution
        Spectral power distribution.
    cmfs : XYZ_ColourMatchingFunctions
        Standard observer colour matching functions.
    illuminant : SpectralPowerDistribution, optional
        *Illuminant* spectral power distribution.

    Returns
    -------
    ndarray, (3,)
        *CIE XYZ* colourspace matrix.

    Warning
    -------
    The output domain of that definition is non standard!

    Notes
    -----
    -   Output *CIE XYZ* colourspace matrix is in domain [0, 100].

    References
    ----------
    .. [1]  **Wyszecki & Stiles**,
            *Color Science - Concepts and Methods Data and Formulae -
            Second Edition*,
            Wiley Classics Library Edition, published 2000,
            ISBN-10: 0-471-39918-3,
            page  158.

    Examples
    --------
    >>> from colour import CMFS, ILLUMINANTS_RELATIVE_SPDS, SpectralPowerDistribution  # noqa
    >>> cmfs = CMFS.get('CIE 1931 2 Degree Standard Observer')
    >>> data = {380: 0.0600, 390: 0.0600}
    >>> spd = SpectralPowerDistribution('Custom', data)
    >>> illuminant = ILLUMINANTS_RELATIVE_SPDS.get('D50')
    >>> spectral_to_XYZ(spd, cmfs, illuminant)  # doctest: +ELLIPSIS
    array([  4.5764852...e-04,   1.2964866...e-05,   2.1615807...e-03])
    """

    shape = cmfs.shape
    if spd.shape != cmfs.shape:
        spd = spd.clone().zeros(shape)

    if illuminant is None:
        illuminant = ones_spd(shape)
    else:
        if illuminant.shape != cmfs.shape:
            illuminant = illuminant.clone().zeros(shape)

    illuminant = illuminant.values
    spd = spd.values

    x_bar, y_bar, z_bar = (cmfs.x_bar.values,
                           cmfs.y_bar.values,
                           cmfs.z_bar.values)

    x_products = spd * x_bar * illuminant
    y_products = spd * y_bar * illuminant
    z_products = spd * z_bar * illuminant

    normalising_factor = 100 / np.sum(y_bar * illuminant)

    XYZ = np.array([normalising_factor * np.sum(x_products),
                    normalising_factor * np.sum(y_products),
                    normalising_factor * np.sum(z_products)])

    return XYZ
Example #12
0
def spectral_to_XYZ(spd,
                    cmfs=STANDARD_OBSERVERS_CMFS.get(
                        'CIE 1931 2 Degree Standard Observer'),
                    illuminant=None):
    """
    Converts given spectral power distribution to *CIE XYZ* colourspace using
    given colour matching functions and illuminant.

    Parameters
    ----------
    spd : SpectralPowerDistribution
        Spectral power distribution.
    cmfs : XYZ_ColourMatchingFunctions
        Standard observer colour matching functions.
    illuminant : SpectralPowerDistribution, optional
        *Illuminant* spectral power distribution.

    Returns
    -------
    ndarray, (3,)
        *CIE XYZ* colourspace matrix.

    Warning
    -------
    The output domain of that definition is non standard!

    Notes
    -----
    -   Output *CIE XYZ* colourspace matrix is in domain [0, 100].

    References
    ----------
    .. [1]  **Wyszecki & Stiles**,
            *Color Science - Concepts and Methods Data and Formulae -
            Second Edition*,
            Wiley Classics Library Edition, published 2000,
            ISBN-10: 0-471-39918-3,
            page  158.

    Examples
    --------
    >>> from colour import CMFS, ILLUMINANTS_RELATIVE_SPDS, SpectralPowerDistribution  # noqa
    >>> cmfs = CMFS.get('CIE 1931 2 Degree Standard Observer')
    >>> data = {380: 0.0600, 390: 0.0600}
    >>> spd = SpectralPowerDistribution('Custom', data)
    >>> illuminant = ILLUMINANTS_RELATIVE_SPDS.get('D50')
    >>> spectral_to_XYZ(spd, cmfs, illuminant)  # doctest: +ELLIPSIS
    array([  4.5764852...e-04,   1.2964866...e-05,   2.1615807...e-03])
    """

    shape = cmfs.shape
    if spd.shape != cmfs.shape:
        spd = spd.clone().zeros(shape)

    if illuminant is None:
        illuminant = ones_spd(shape)
    else:
        if illuminant.shape != cmfs.shape:
            illuminant = illuminant.clone().zeros(shape)

    illuminant = illuminant.values
    spd = spd.values

    x_bar, y_bar, z_bar = (cmfs.x_bar.values, cmfs.y_bar.values,
                           cmfs.z_bar.values)

    x_products = spd * x_bar * illuminant
    y_products = spd * y_bar * illuminant
    z_products = spd * z_bar * illuminant

    normalising_factor = 100 / np.sum(y_bar * illuminant)

    XYZ = np.array([
        normalising_factor * np.sum(x_products),
        normalising_factor * np.sum(y_products),
        normalising_factor * np.sum(z_products)
    ])

    return XYZ