diff --git a/colour/__init__.py b/colour/__init__.py index 3c23a88f26..b8159bfd80 100644 --- a/colour/__init__.py +++ b/colour/__init__.py @@ -25,6 +25,7 @@ - phenomenons: Computation of various optical phenomenons. - plotting: Diagrams, plots, etc... - quality: Colour quality computation. +- recovery: Reflectance recovery. - temperature: Colour temperature and correlated colour temperature computation. - utilities: Various utilities and data structures. @@ -71,6 +72,8 @@ from . import notation from .quality import * # noqa from . import quality +from .recovery import * # noqa +from . import recovery from .temperature import * # noqa from . import temperature from . import plotting @@ -97,6 +100,7 @@ __all__ += phenomenons.__all__ __all__ += notation.__all__ __all__ += quality.__all__ +__all__ += recovery.__all__ __all__ += temperature.__all__ __application_name__ = 'Colour' diff --git a/colour/characterisation/dataset/__init__.py b/colour/characterisation/dataset/__init__.py index ced4129c66..784190ea49 100644 --- a/colour/characterisation/dataset/__init__.py +++ b/colour/characterisation/dataset/__init__.py @@ -3,7 +3,11 @@ from __future__ import absolute_import -from .colour_checkers import COLOURCHECKERS, COLOURCHECKERS_SPDS +from .colour_checkers import ( + COLOURCHECKERS, + COLOURCHECKER_INDEXES_TO_NAMES_MAPPING, + COLOURCHECKERS_SPDS) __all__ = ['COLOURCHECKERS', + 'COLOURCHECKER_INDEXES_TO_NAMES_MAPPING', 'COLOURCHECKERS_SPDS'] diff --git a/colour/characterisation/dataset/colour_checkers/__init__.py b/colour/characterisation/dataset/colour_checkers/__init__.py index 98d861c4e0..85d93de896 100644 --- a/colour/characterisation/dataset/colour_checkers/__init__.py +++ b/colour/characterisation/dataset/colour_checkers/__init__.py @@ -4,7 +4,8 @@ from __future__ import absolute_import from .chromaticity_coordinates import COLOURCHECKERS -from .spds import COLOURCHECKERS_SPDS +from .spds import COLOURCHECKER_INDEXES_TO_NAMES_MAPPING, COLOURCHECKERS_SPDS __all__ = ['COLOURCHECKERS', + 'COLOURCHECKER_INDEXES_TO_NAMES_MAPPING', 'COLOURCHECKERS_SPDS'] diff --git a/colour/examples/recovery/examples_smits1999.py b/colour/examples/recovery/examples_smits1999.py new file mode 100644 index 0000000000..0eaf4dff12 --- /dev/null +++ b/colour/examples/recovery/examples_smits1999.py @@ -0,0 +1,23 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +""" +Showcases reflectance recovery computations using *Brian Smits (1999)* method. +""" + +import colour +from colour.utilities.verbose import message_box + +message_box('"Brian Smits (1999)" - Reflectance Recovery Computations') + +RGB = [0.35505307, 0.47995567, 0.61088035] +message_box(('Recovering reflectance using "Brian Smits (1999)" method from ' + 'given "RGB" colourspace matrix:\n' + '\n\tRGB: {0}'.format(RGB))) +print(colour.RGB_to_spectral_smits1999(RGB)) + +print('\n') + +message_box(('An analysis of "Brian Smits (1999)" method is available at the ' + 'following url : ' + 'http://nbviewer.ipython.org/github/colour-science/colour-website/blob/master/ipython/about_reflectance_recovery.ipynb')) # noqa \ No newline at end of file diff --git a/colour/recovery/__init__.py b/colour/recovery/__init__.py new file mode 100644 index 0000000000..ab4c3d21ff --- /dev/null +++ b/colour/recovery/__init__.py @@ -0,0 +1,12 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +from __future__ import absolute_import + +from .dataset import * # noqa +from . import dataset +from .smits1999 import RGB_to_spectral_smits1999 + +__all__ = [] +__all__ += dataset.__all__ +__all__ += ['RGB_to_spectral_smits1999'] \ No newline at end of file diff --git a/colour/recovery/dataset/__init__.py b/colour/recovery/dataset/__init__.py new file mode 100644 index 0000000000..43cffc9664 --- /dev/null +++ b/colour/recovery/dataset/__init__.py @@ -0,0 +1,8 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +from __future__ import absolute_import + +from .smits1999 import SMITS_1999_SPDS + +__all__ = ['SMITS_1999_SPDS'] diff --git a/colour/recovery/dataset/smits1999.py b/colour/recovery/dataset/smits1999.py new file mode 100644 index 0000000000..4ea0f6b1c7 --- /dev/null +++ b/colour/recovery/dataset/smits1999.py @@ -0,0 +1,130 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +""" +Smits (1999) - Reflectance Recovery Dataset +=========================================== + +Defines the dataset for reflectance recovery using *Smits (1999)* method. + +References +---------- +.. [1] Smits, B. (1999). An RGB-to-Spectrum Conversion for Reflectances. + Journal of Graphics Tools, 4(4), 11–22. + doi:10.1080/10867651.1999.10487511 +""" + +from __future__ import division, unicode_literals + +from colour.colorimetry.spectrum import SpectralPowerDistribution +from colour.utilities import CaseInsensitiveMapping + +__author__ = 'Colour Developers' +__copyright__ = 'Copyright (C) 2013 - 2014 - Colour Developers' +__license__ = 'New BSD License - http://opensource.org/licenses/BSD-3-Clause' +__maintainer__ = 'Colour Developers' +__email__ = 'colour-science@googlegroups.com' +__status__ = 'Production' + +__all__ = ['SMITS_1999_SPDS_DATA', + 'SMITS_1999_SPDS'] + +SMITS_1999_SPDS_DATA = { + 'white': { + 380.0: 1.0, + 417.7778: 1.0, + 455.5556: 0.9999, + 493.3333: 0.9993, + 531.1111: 0.9992, + 568.8889: 0.9998, + 606.6667: 1.0, + 644.4444: 1.0, + 682.2222: 1.0, + 720.0: 1.0}, + 'cyan': { + 380.0: 0.971, + 417.7778: 0.9426, + 455.5556: 1.0007, + 493.3333: 1.0007, + 531.1111: 1.0007, + 568.8889: 1.0007, + 606.6667: 0.1564, + 644.4444: 0.0, + 682.2222: 0.0, + 720.0: 0.0}, + 'magenta': { + 380.0: 1.0, + 417.7778: 1.0, + 455.5556: 0.9685, + 493.3333: 0.2229, + 531.1111: 0.0, + 568.8889: 0.0458, + 606.6667: 0.8369, + 644.4444: 1.0, + 682.2222: 1.0, + 720.0: 0.9959}, + 'yellow': { + 380.0: 0.0001, + 417.7778: 0.0, + 455.5556: 0.1088, + 493.3333: 0.6651, + 531.1111: 1.0, + 568.8889: 1.0, + 606.6667: 0.9996, + 644.4444: 0.9586, + 682.2222: 0.9685, + 720.0: 0.984}, + 'red': { + 380.0: 0.1012, + 417.7778: 0.0515, + 455.5556: 0.0, + 493.3333: 0.0, + 531.1111: 0.0, + 568.8889: 0.0, + 606.6667: 0.8325, + 644.4444: 1.0149, + 682.2222: 1.0149, + 720.0: 1.0149}, + 'green': { + 380.0: 0.0, + 417.7778: 0.0, + 455.5556: 0.0273, + 493.3333: 0.7937, + 531.1111: 1.0, + 568.8889: 0.9418, + 606.6667: 0.1719, + 644.4444: 0.0, + 682.2222: 0.0, + 720.0: 0.0025}, + 'blue': { + 380.0: 1.0, + 417.7778: 1.0, + 455.5556: 0.8916, + 493.3333: 0.3323, + 531.1111: 0.0, + 568.8889: 0.0, + 606.6667: 0.0003, + 644.4444: 0.0369, + 682.2222: 0.0483, + 720.0: 0.0496}} + +SMITS_1999_SPDS = CaseInsensitiveMapping({ + 'white': SpectralPowerDistribution( + 'white', SMITS_1999_SPDS_DATA.get('white')), + 'cyan': SpectralPowerDistribution( + 'cyan', SMITS_1999_SPDS_DATA.get('cyan')), + 'magenta': SpectralPowerDistribution( + 'magenta', SMITS_1999_SPDS_DATA.get('magenta')), + 'yellow': SpectralPowerDistribution( + 'yellow', SMITS_1999_SPDS_DATA.get('yellow')), + 'red': SpectralPowerDistribution( + 'red', SMITS_1999_SPDS_DATA.get('red')), + 'green': SpectralPowerDistribution( + 'green', SMITS_1999_SPDS_DATA.get('green')), + 'blue': SpectralPowerDistribution( + 'blue', SMITS_1999_SPDS_DATA.get('blue'))}) +""" +*Smits (1999)* spectral power distributions. + +SMITS_1999_SPDS : CaseInsensitiveMapping +""" diff --git a/colour/recovery/smits1999.py b/colour/recovery/smits1999.py new file mode 100644 index 0000000000..9cc9d49e89 --- /dev/null +++ b/colour/recovery/smits1999.py @@ -0,0 +1,168 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +""" +Smits (1999) - Reflectance Recovery +=================================== + +Defines objects for reflectance recovery using *Smits (1999)* method. + +See Also +-------- +`Smits (1999) - Reflectance Recovery IPython Notebook +`_ # noqa + +References +---------- +.. [1] Smits, B. (1999). An RGB-to-Spectrum Conversion for Reflectances. + Journal of Graphics Tools, 4(4), 11–22. + doi:10.1080/10867651.1999.10487511 +""" + +from __future__ import division, unicode_literals + +import numpy as np + +from colour.colorimetry import ILLUMINANTS, zeros_spd +from colour.models import ( + XYZ_to_RGB, + sRGB_COLOURSPACE, + normalised_primary_matrix) +from colour.recovery import SMITS_1999_SPDS + +__author__ = 'Colour Developers' +__copyright__ = 'Copyright (C) 2013 - 2014 - Colour Developers' +__license__ = 'New BSD License - http://opensource.org/licenses/BSD-3-Clause' +__maintainer__ = 'Colour Developers' +__email__ = 'colour-science@googlegroups.com' +__status__ = 'Production' + +__all__ = ['SMITS1999_PRIMARIES', + 'SMITS1999_WHITEPOINT', + 'SMITS1999_XYZ_TO_RGB_MATRIX', + 'XYZ_to_RGB_smits1999', + 'RGB_to_spectral_smits1999'] + +SMITS1999_PRIMARIES = sRGB_COLOURSPACE.primaries +""" +Current *Brian Smits (1999)* method implementation colourspace primaries. + +SMITS1999_PRIMARIES : ndarray, (3, 2) +""" + +SMITS1999_WHITEPOINT = ILLUMINANTS.get( + 'CIE 1931 2 Degree Standard Observer').get('E') +""" +Current *Brian Smits (1999)* method implementation colourspace whitepoint. + +SMITS1999_WHITEPOINT : tuple +""" + +SMITS1999_XYZ_TO_RGB_MATRIX = np.linalg.inv( + normalised_primary_matrix(SMITS1999_PRIMARIES, SMITS1999_WHITEPOINT)) +""" +Current *Brian Smits (1999)* method implementation *RGB* colourspace to +*CIE XYZ* colourspace matrix. + +SMITS1999_XYZ_TO_RGB_MATRIX : array_like, (3, 3) +""" + + +def XYZ_to_RGB_smits1999(XYZ, chromatic_adaptation_transform='Bradford'): + """ + Convenient object to convert from *CIE XYZ* colourspace to *RGB* + colourspace in conditions required by the current *Smits (1999)* method + implementation. + + Parameters + ---------- + XYZ : array_like, (3,) + *CIE XYZ* colourspace matrix. + chromatic_adaptation_method : unicode, optional + {'CAT02', 'XYZ Scaling', 'Von Kries', 'Bradford', 'Sharp', 'Fairchild, + 'CMCCAT97', 'CMCCAT2000', 'Bianco', 'Bianco PC'}, + *Chromatic adaptation* method. + + Returns + ------- + ndarray, (3,) + *RGB* colour matrix. + + Notes + ----- + - Input *CIE XYZ* colourspace matrix is in domain [0, 1]. + + Examples + -------- + >>> XYZ = np.array([0.07049534, 0.1008, 0.09558313]) + >>> XYZ_to_RGB_smits1999(XYZ) # doctest: +ELLIPSIS + array([ 0.0214496..., 0.1315460..., 0.0928760...]) + """ + + return XYZ_to_RGB(XYZ, + SMITS1999_WHITEPOINT, + SMITS1999_WHITEPOINT, + SMITS1999_XYZ_TO_RGB_MATRIX, + chromatic_adaptation_transform, + transfer_function=None) + + +def RGB_to_spectral_smits1999(RGB): + """ + Recovers the spectral power distribution of given *RGB* colourspace matrix + using *Smits (1999)* method. + + Parameters + ---------- + RGB : array_like, (3,) + *RGB* colourspace matrix. + + Returns + ------- + SpectralPowerDistribution + Recovered spectral power distribution. + + Examples + -------- + >>> RGB = np.array([0.02144962, 0.13154603, 0.09287601]) + >>> RGB_to_spectral_smits1999(RGB) # doctest: +ELLIPSIS + <...SpectralPowerDistribution object at 0x...> + """ + + white_spd = SMITS_1999_SPDS.get('white').clone() + cyan_spd = SMITS_1999_SPDS.get('cyan').clone() + magenta_spd = SMITS_1999_SPDS.get('magenta').clone() + yellow_spd = SMITS_1999_SPDS.get('yellow').clone() + red_spd = SMITS_1999_SPDS.get('red').clone() + green_spd = SMITS_1999_SPDS.get('green').clone() + blue_spd = SMITS_1999_SPDS.get('blue').clone() + + R, G, B = np.ravel(RGB) + spd = zeros_spd(SMITS_1999_SPDS.get('white').shape) + + if R <= G and R <= B: + spd += white_spd * R + if G <= B: + spd += cyan_spd * (G - R) + spd += blue_spd * (B - G) + else: + spd += cyan_spd * (B - R) + spd += green_spd * (G - B) + elif G <= R and G <= B: + spd += white_spd * G + if R <= B: + spd += magenta_spd * (R - G) + spd += blue_spd * (B - R) + else: + spd += magenta_spd * (B - G) + spd += red_spd * (R - B) + else: + spd += white_spd * B + if R <= G: + spd += yellow_spd * (R - B) + spd += green_spd * (G - R) + else: + spd += yellow_spd * (G - B) + spd += red_spd * (R - G) + + return spd diff --git a/colour/recovery/tests/__init__.py b/colour/recovery/tests/__init__.py new file mode 100644 index 0000000000..faa18be5bb --- /dev/null +++ b/colour/recovery/tests/__init__.py @@ -0,0 +1,2 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- diff --git a/colour/recovery/tests/tests_smits1999.py b/colour/recovery/tests/tests_smits1999.py new file mode 100644 index 0000000000..1fa59a1cbe --- /dev/null +++ b/colour/recovery/tests/tests_smits1999.py @@ -0,0 +1,87 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +""" +Defines unit tests for :mod:`colour.recovery.smits1999` module. +""" + +from __future__ import division, unicode_literals + +import numpy as np +import sys + +if sys.version_info[:2] <= (2, 6): + import unittest2 as unittest +else: + import unittest + +from colour.recovery import RGB_to_spectral_smits1999 + +__author__ = 'Colour Developers' +__copyright__ = 'Copyright (C) 2013 - 2014 - Colour Developers' +__license__ = 'New BSD License - http://opensource.org/licenses/BSD-3-Clause' +__maintainer__ = 'Colour Developers' +__email__ = 'colour-science@googlegroups.com' +__status__ = 'Production' + +__all__ = ['TestIsWithinMacadamLimits'] + + +class TestRGB_to_spd_smits1999(unittest.TestCase): + """ + Defines :func:`colour.recovery.smits1999.RGB_to_spectral_smits1999` + definition unit tests methods. + """ + + def test_RGB_to_spd_smits1999(self): + """ + Tests :func:`colour.recovery.smits1999.RGB_to_spectral_smits1999` + definition. + """ + + np.testing.assert_almost_equal( + RGB_to_spectral_smits1999( + [0.45293517, 0.31732158, 0.26414773]).values, + np.array([0.27787714, + 0.27113183, + 0.26990663, + 0.29932875, + 0.31711026, + 0.31726875, + 0.43019862, + 0.45275442, + 0.45328084, + 0.45410503]), + decimal=7) + np.testing.assert_almost_equal( + RGB_to_spectral_smits1999( + np.array([0.77875824, 0.5772645, 0.50453169])).values, + np.array([0.52493013, + 0.51490862, + 0.51239457, + 0.55255311, + 0.57686087, + 0.57716359, + 0.74497895, + 0.77874936, + 0.77946941, + 0.78059677]), + decimal=7) + np.testing.assert_almost_equal( + RGB_to_spectral_smits1999( + np.array([0.35505307, 0.47995567, 0.61088035])).values, + np.array([0.60725817, + 0.60371094, + 0.59674004, + 0.52330084, + 0.47975906, + 0.47997209, + 0.37462711, + 0.35988419, + 0.36137673, + 0.36154693]), + decimal=7) + + +if __name__ == '__main__': + unittest.main()