Source code for colour.recovery.meng2015

#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
Meng et al. (2015) - Reflectance Recovery
=========================================

Defines objects for reflectance recovery using *Meng, Simon and Hanika (2015)*
method:

-   :func:`XYZ_to_spectral_Meng2015`

See Also
--------
`Meng et al. (2015) - Reflectance Recovery Jupyter Notebook
<http://nbviewer.jupyter.org/github/colour-science/colour-notebooks/\
blob/master/notebooks/recovery/meng2015.ipynb>`_

References
----------
.. [1]  Meng, J., Simon, F., & Hanika, J. (2015). Physically Meaningful
        Rendering using Tristimulus Colours, 34(4). Retrieved from
        http://jo.dreggn.org/home/2015_spectrum.pdf
"""

from __future__ import division, unicode_literals

import numpy as np
from scipy.optimize import minimize

from colour import (STANDARD_OBSERVERS_CMFS, SpectralPowerDistribution,
                    SpectralShape, ones_spd, spectral_to_XYZ_integration)

__author__ = 'Colour Developers'
__copyright__ = 'Copyright (C) 2013-2017 - Colour Developers'
__license__ = 'New BSD License - http://opensource.org/licenses/BSD-3-Clause'
__maintainer__ = 'Colour Developers'
__email__ = 'colour-science@googlegroups.com'
__status__ = 'Production'

__all__ = ['XYZ_to_spectral_Meng2015']


[docs]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 al. (2015)* method. Parameters ---------- XYZ : array_like, (3,) *CIE XYZ* tristimulus values. 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.spectral_to_XYZ_integration` definition because it processes any measurement interval opposed to :func:`colour.spectral_to_XYZ_ASTME30815` definition that handles only measurement interval of 1, 5, 10 or 20nm. Examples -------- >>> XYZ = np.array([0.07049534, 0.10080000, 0.09558313]) >>> spd = XYZ_to_spectral_Meng2015(XYZ) >>> print(spd) SpectralPowerDistribution('Meng (2015) - \ [ 0.07049534 0.1008 0.09558313]', (360.0, 830.0, 5.0)) >>> spectral_to_XYZ_integration(spd) # doctest: +ELLIPSIS array([ 0.0704952..., 0.1007999..., 0.0955824...]) """ XYZ = np.asarray(XYZ) shape = SpectralShape(cmfs.shape.start, cmfs.shape.end, interval) cmfs = cmfs.clone().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('Meng (2015) - {0}'.format(XYZ), dict(zip(wavelengths, result.x)))