From ec387137713f868bf9853d5f723fb4db1966ee57 Mon Sep 17 00:00:00 2001 From: Michael Mauderer Date: Wed, 3 Sep 2014 15:54:35 +0100 Subject: [PATCH 1/5] Implement "IPT" colourspace. Closes #114. --- colour/models/ipt.py | 189 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 189 insertions(+) create mode 100644 colour/models/ipt.py diff --git a/colour/models/ipt.py b/colour/models/ipt.py new file mode 100644 index 0000000000..d06c15b496 --- /dev/null +++ b/colour/models/ipt.py @@ -0,0 +1,189 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +""" +IPT Colourspace +=================== + +Defines the *IPT* colourspace transformations: + +- :func:`XYZ_to_IPT` +- :func:`IPT_to_XYZ` + +and computation of correlates: + +- :func:`IPT_hue_angle` + + +References +---------- +.. [1] **Mark D. Fairchild**, *Color Appearance Models, 3nd Edition*, + The Wiley-IS&T Series in Imaging Science and Technology, + published June 2013, ASIN: B00DAYO8E2, locations 5094-5556, + pages 271-272. + +""" + +from __future__ import division, unicode_literals + +import numpy as np + +__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__ = ['XYZ_to_IPT', + 'IPT_to_XYZ', + 'IPT_hue_angle'] + +IPT_XYZ_TO_LMS_MATRIX = np.array([ + [0.4002, 0.7075, -0.0807], + [-0.2280, 1.1500, 0.0612], + [0.0000, 0.0000, 0.9184]]) +""" +*CIE XYZ* colourspace to *IPT* colourspace normalised cone responses matrix. + +IPT_XYZ_TO_LMS_MATRIX : array_like, (3, 3) +""" + +IPT_LMS_TO_XYZ_MATRIX = np.around( + np.linalg.inv(IPT_XYZ_TO_LMS_MATRIX), + decimals=4) +""" +*IPT* colourspace normalised cone responses to *CIE XYZ* colourspace matrix. + +Notes +----- +- This matrix has been rounded on purpose to 4 decimals so in order to keep available + precision consistent between transformations. + +IPT_LMS_TO_XYZ_MATRIX : array_like, (3, 3) +""" + +IPT_LMS_TO_IPT_MATRIX = np.array([ + [0.4000, 0.4000, 0.2000], + [4.4550, -4.8510, 0.3960], + [0.8056, 0.3572, -1.1628]]) +""" +*IPT* colourspace normalised cone responses to *IPT* colourspace matrix. + +IPT_LMS_TO_IPT_MATRIX : array_like, (3, 3) +""" + +IPT_IPT_TO_LMS_MATRIX = np.around( + np.linalg.inv(IPT_LMS_TO_IPT_MATRIX), + decimals=4) +""" +*IPT* colourspace to *IPT* colourspace normalised cone responses matrix. + +Notes +----- +- This matrix has been rounded on purpose to 4 decimals so in order to keep available + precision consistent between transformations. + +IPT_IPT_TO_LMS_MATRIX : array_like, (3, 3) +""" + + +def XYZ_to_IPT(XYZ): + """ + Converts from *CIE XYZ* colourspace to *IPT* colourspace. + + Parameters + ---------- + XYZ : array_like, (3,) + *CIE XYZ* colourspace matrix. + + Returns + ------- + ndarray, (3,) + *IPT* colourspace matrix. + + Notes + ----- + - Input *CIE XYZ* needs to be adapted for CIE illuminant D65. + + References + ---------- + .. [1] **Mark D. Fairchild**, *Color Appearance Models, 3nd Edition*, + The Wiley-IS&T Series in Imaging Science and Technology, + published June 2013, ASIN: B00DAYO8E2, locations 5094-5556, + pages 271-272. + + Examples + -------- + >>> XYZ_to_IPT(np.array([0.5, 0.5, 0.5])) # doctest: +ELLIPSIS + array([ 0.738192 , 0.0536732..., 0.0359856...]) + """ + + LMS = np.dot(IPT_XYZ_TO_LMS_MATRIX, XYZ) + LMS_prime = np.sign(LMS) * np.abs(LMS) ** 0.43 + IPT = np.dot(IPT_LMS_TO_IPT_MATRIX, LMS_prime) + + return IPT + + +def IPT_TO_XYZ(IPT): + """ + Converts from *IPT* colourspace to *CIE XYZ* colourspace. + + Parameters + ---------- + IPT : array_like, (3,) + *IPT* colourspace matrix. + + Returns + ------- + ndarray, (3,) + *XYZ* colourspace matrix. + + References + ---------- + .. [1] **Mark D. Fairchild**, *Color Appearance Models, 3nd Edition*, + The Wiley-IS&T Series in Imaging Science and Technology, + published June 2013, ASIN: B00DAYO8E2, locations 5094-5556, + pages 271-272. + + Examples + -------- + >>> IPT_TO_XYZ(np.array([0.5, 0.5, 0.5])) # doctest: +ELLIPSIS + array([ 0.4497109..., 0.2694691..., 0.0196303...]) + """ + + LMS = np.dot(IPT_IPT_TO_LMS_MATRIX, IPT) + LMS_prime = np.sign(LMS) * np.abs(LMS) ** (1 / 0.43) + XYZ = np.dot(IPT_LMS_TO_XYZ_MATRIX, LMS_prime) + + return XYZ + + +def IPT_hue_angle(IPT): + """ + Computes the hue angle from *IPT* colourspace. + + Parameters + ---------- + IPT : array_like, (3,) + *IPT* colourspace matrix. + + Returns + ------- + float, + hue angle. + + References + ---------- + .. [1] **Mark D. Fairchild**, *Color Appearance Models, 3nd Edition*, + The Wiley-IS&T Series in Imaging Science and Technology, + published June 2013, ASIN: B00DAYO8E2, locations 5094-5556, + pages 271-272. + + Examples + -------- + >>> IPT_hue_angle(([0.5, 0.5, 0.5])) # doctest: +ELLIPSIS + 0.7853981... + """ + return np.arctan2(IPT[2], IPT[1]) From 1f071623a65ab3255b614033a539e6209c1b743e Mon Sep 17 00:00:00 2001 From: Michael Mauderer Date: Wed, 3 Sep 2014 19:14:48 +0100 Subject: [PATCH 2/5] Fixed docstrings. Aligned first line of reference to next tab. Instead of repeating the main reference in each function, it is now cited using standard reST citations --- colour/models/ipt.py | 33 ++++++--------------------------- 1 file changed, 6 insertions(+), 27 deletions(-) diff --git a/colour/models/ipt.py b/colour/models/ipt.py index d06c15b496..926bd329e0 100644 --- a/colour/models/ipt.py +++ b/colour/models/ipt.py @@ -17,7 +17,7 @@ References ---------- -.. [1] **Mark D. Fairchild**, *Color Appearance Models, 3nd Edition*, +.. [1] **Mark D. Fairchild**, *Color Appearance Models, 3nd Edition*, The Wiley-IS&T Series in Imaging Science and Technology, published June 2013, ASIN: B00DAYO8E2, locations 5094-5556, pages 271-272. @@ -90,7 +90,7 @@ def XYZ_to_IPT(XYZ): """ - Converts from *CIE XYZ* colourspace to *IPT* colourspace. + Converts from *CIE XYZ* colourspace to *IPT* colourspace (see [1]_). Parameters ---------- @@ -106,13 +106,6 @@ def XYZ_to_IPT(XYZ): ----- - Input *CIE XYZ* needs to be adapted for CIE illuminant D65. - References - ---------- - .. [1] **Mark D. Fairchild**, *Color Appearance Models, 3nd Edition*, - The Wiley-IS&T Series in Imaging Science and Technology, - published June 2013, ASIN: B00DAYO8E2, locations 5094-5556, - pages 271-272. - Examples -------- >>> XYZ_to_IPT(np.array([0.5, 0.5, 0.5])) # doctest: +ELLIPSIS @@ -128,7 +121,7 @@ def XYZ_to_IPT(XYZ): def IPT_TO_XYZ(IPT): """ - Converts from *IPT* colourspace to *CIE XYZ* colourspace. + Converts from *IPT* colourspace to *CIE XYZ* colourspace (see [1]_). Parameters ---------- @@ -140,14 +133,7 @@ def IPT_TO_XYZ(IPT): ndarray, (3,) *XYZ* colourspace matrix. - References - ---------- - .. [1] **Mark D. Fairchild**, *Color Appearance Models, 3nd Edition*, - The Wiley-IS&T Series in Imaging Science and Technology, - published June 2013, ASIN: B00DAYO8E2, locations 5094-5556, - pages 271-272. - - Examples + Examples -------- >>> IPT_TO_XYZ(np.array([0.5, 0.5, 0.5])) # doctest: +ELLIPSIS array([ 0.4497109..., 0.2694691..., 0.0196303...]) @@ -162,7 +148,7 @@ def IPT_TO_XYZ(IPT): def IPT_hue_angle(IPT): """ - Computes the hue angle from *IPT* colourspace. + Computes the hue angle from *IPT* colourspace (see [1]_). Parameters ---------- @@ -171,16 +157,9 @@ def IPT_hue_angle(IPT): Returns ------- - float, + numeric, hue angle. - References - ---------- - .. [1] **Mark D. Fairchild**, *Color Appearance Models, 3nd Edition*, - The Wiley-IS&T Series in Imaging Science and Technology, - published June 2013, ASIN: B00DAYO8E2, locations 5094-5556, - pages 271-272. - Examples -------- >>> IPT_hue_angle(([0.5, 0.5, 0.5])) # doctest: +ELLIPSIS From 092b777fffc8b24b93cb0608718c1f3fb5adde39 Mon Sep 17 00:00:00 2001 From: Michael Mauderer Date: Wed, 3 Sep 2014 19:16:05 +0100 Subject: [PATCH 3/5] Fixed typo in function signature IPT_TO_XYZ -> IPT_to_XYZ --- colour/models/ipt.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/colour/models/ipt.py b/colour/models/ipt.py index 926bd329e0..58d953fba2 100644 --- a/colour/models/ipt.py +++ b/colour/models/ipt.py @@ -119,7 +119,7 @@ def XYZ_to_IPT(XYZ): return IPT -def IPT_TO_XYZ(IPT): +def IPT_to_XYZ(IPT): """ Converts from *IPT* colourspace to *CIE XYZ* colourspace (see [1]_). @@ -135,7 +135,7 @@ def IPT_TO_XYZ(IPT): Examples -------- - >>> IPT_TO_XYZ(np.array([0.5, 0.5, 0.5])) # doctest: +ELLIPSIS + >>> IPT_to_XYZ(np.array([0.5, 0.5, 0.5])) # doctest: +ELLIPSIS array([ 0.4497109..., 0.2694691..., 0.0196303...]) """ From 621467f55d81c453abb7c6808984c55ae81a9d04 Mon Sep 17 00:00:00 2001 From: Michael Mauderer Date: Wed, 3 Sep 2014 19:17:35 +0100 Subject: [PATCH 4/5] Added module methods to sub-package __init__.py file --- colour/models/__init__.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/colour/models/__init__.py b/colour/models/__init__.py index 3d3c46610b..d59129f2a4 100644 --- a/colour/models/__init__.py +++ b/colour/models/__init__.py @@ -29,6 +29,7 @@ from .rgb import RGB_to_RGB from .common import XYZ_to_sRGB from .aces_rgb_idt import spectral_to_aces_relative_exposure_values +from .ipt import XYZ_to_IPT, IPT_to_XYZ, IPT_hue_angle __all__ = ['RGB_Colourspace'] __all__ += ['normalised_primary_matrix', @@ -52,3 +53,4 @@ __all__ += ['RGB_to_RGB'] __all__ += ['XYZ_to_sRGB'] __all__ += ['spectral_to_aces_relative_exposure_values'] +__all__ += ['XYZ_to_IPT', 'IPT_to_XYZ', 'IPT_hue_angle'] From 4eb03a4e3d0e7f42806466444cc67776ebfad52f Mon Sep 17 00:00:00 2001 From: Michael Mauderer Date: Wed, 3 Sep 2014 19:31:56 +0100 Subject: [PATCH 5/5] Implements unit tests for colour.models.ipt module --- colour/models/tests/tests_ipt.py | 80 ++++++++++++++++++++++++++++++++ 1 file changed, 80 insertions(+) create mode 100644 colour/models/tests/tests_ipt.py diff --git a/colour/models/tests/tests_ipt.py b/colour/models/tests/tests_ipt.py new file mode 100644 index 0000000000..fae613a766 --- /dev/null +++ b/colour/models/tests/tests_ipt.py @@ -0,0 +1,80 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +""" +Defines unit tests for :mod:`colour.models.ipt` 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.models import XYZ_to_IPT, IPT_to_XYZ, IPT_hue_angle + +__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__ = ['TestXYZ_to_IPT', + 'TestIPT_to_XYZ', + 'TestIPT_hue_angle'] + + +class TestXYZ_to_IPT(unittest.TestCase): + """ + Defines :func:`colour.models.ipt.TestXYZ_to_IPT` definition unit tests + methods. + """ + + def test_XYZ_to_IPT(self): + """ + Tests :func:`colour.models.ipt.XYZ_to_IPT` definition. + """ + + np.testing.assert_almost_equal( + XYZ_to_IPT(np.array([0.5, 0.5, 0.5])), + np.array([0.738192, 0.0536732, 0.0359856]), + decimal=7) + + +class TestIPT_to_XYZ(unittest.TestCase): + """ + Defines :func:`colour.models.ipt.IPT_to_XYZ` definition unit tests + methods. + """ + + def test_IPT_to_XYZ(self): + """ + Tests :func:`colour.models.ipt.IPT_to_XYZ` definition. + """ + + np.testing.assert_almost_equal( + IPT_to_XYZ(np.array([0.5, 0.5, 0.5])), + np.array([0.4497109, 0.2694691, 0.0196303]), + decimal=7) + + +class TestIPT_hue_angle(unittest.TestCase): + """ + Defines :func:`colour.models.ipt.IPT_hue_angle` definition unit tests + methods. + """ + + def test_IPT_hue_angle(self): + """ + Tests :func:`colour.models.ipt.IPT_hue_angle` definition. + """ + + np.testing.assert_almost_equal( + IPT_hue_angle(np.array([0.5, 0.5, 0.5])), + 0.78539812, + decimal=7) \ No newline at end of file