Skip to content

Commit

Permalink
Merge pull request #115 from colour-science/feature/ipt
Browse files Browse the repository at this point in the history
PR: Implement support for "IPT" colourspace.
  • Loading branch information
KelSolaar committed Sep 3, 2014
2 parents d44662b + 4eb03a4 commit 23bf4e3
Show file tree
Hide file tree
Showing 3 changed files with 250 additions and 0 deletions.
2 changes: 2 additions & 0 deletions colour/models/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -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',
Expand All @@ -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']
168 changes: 168 additions & 0 deletions colour/models/ipt.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,168 @@
#!/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 (see [1]_).
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.
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 (see [1]_).
Parameters
----------
IPT : array_like, (3,)
*IPT* colourspace matrix.
Returns
-------
ndarray, (3,)
*XYZ* colourspace matrix.
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 (see [1]_).
Parameters
----------
IPT : array_like, (3,)
*IPT* colourspace matrix.
Returns
-------
numeric,
hue angle.
Examples
--------
>>> IPT_hue_angle(([0.5, 0.5, 0.5])) # doctest: +ELLIPSIS
0.7853981...
"""
return np.arctan2(IPT[2], IPT[1])
80 changes: 80 additions & 0 deletions colour/models/tests/tests_ipt.py
Original file line number Diff line number Diff line change
@@ -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)

0 comments on commit 23bf4e3

Please sign in to comment.