#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
RGB Colourspace & Transformations
=================================
Defines the :class:`RGB_Colourspace` class for the *RGB* colourspaces dataset
from :mod:`colour.models.dataset.aces_rgb`, etc... and the following *RGB*
colourspace transformations:
- :func:`XYZ_to_RGB`
- :func:`RGB_to_XYZ`
- :func:`RGB_to_RGB`
See Also
--------
`RGB Colourspaces IPython Notebook
<http://nbviewer.jupyter.org/github/colour-science/colour-notebooks/blob/\
master/notebooks/models/rgb.ipynb>`_
"""
from __future__ import division, unicode_literals
import numpy as np
from colour.models import xy_to_XYZ, xy_to_xyY, xyY_to_XYZ
from colour.adaptation import chromatic_adaptation_matrix_VonKries
from colour.utilities import dot_matrix, dot_vector
__author__ = 'Colour Developers'
__copyright__ = 'Copyright (C) 2013-2016 - Colour Developers'
__license__ = 'New BSD License - http://opensource.org/licenses/BSD-3-Clause'
__maintainer__ = 'Colour Developers'
__email__ = 'colour-science@googlegroups.com'
__status__ = 'Production'
__all__ = ['RGB_Colourspace',
'XYZ_to_RGB',
'RGB_to_XYZ',
'RGB_to_RGB']
[docs]class RGB_Colourspace(object):
"""
Implements support for the *RGB* colourspaces dataset from
:mod:`colour.models.dataset.aces_rgb`, etc....
Parameters
----------
name : unicode
*RGB* colourspace name.
primaries : array_like
*RGB* colourspace primaries.
whitepoint : array_like
*RGB* colourspace whitepoint.
illuminant : unicode, optional
*RGB* colourspace whitepoint name as illuminant.
RGB_to_XYZ_matrix : array_like, optional
Transformation matrix from colourspace to *CIE XYZ* tristimulus values.
XYZ_to_RGB_matrix : array_like, optional
Transformation matrix from *CIE XYZ* tristimulus values to colourspace.
encoding_cctf : object, optional
Encoding colour component transfer function (Encoding CCTF) /
opto-electronic transfer function (OETF / OECF) that maps estimated
tristimulus values in a scene to :math:`R'G'B'` video component signal
value.
decoding_cctf : object, optional
Decoding colour component transfer function (Decoding CCTF) /
electro-optical transfer function (EOTF / EOCF) that maps an
:math:`R'G'B'` video component signal value to tristimulus values at
the display.
Attributes
----------
name
primaries
whitepoint
illuminant
RGB_to_XYZ_matrix
XYZ_to_RGB_matrix
encoding_cctf
decoding_cctf
"""
def __init__(self,
name,
primaries,
whitepoint,
illuminant=None,
RGB_to_XYZ_matrix=None,
XYZ_to_RGB_matrix=None,
encoding_cctf=None,
decoding_cctf=None):
self._name = None
self.name = name
self._primaries = None
self.primaries = primaries
self._whitepoint = None
self.whitepoint = whitepoint
self._illuminant = None
self.illuminant = illuminant
self._RGB_to_XYZ_matrix = None
self.RGB_to_XYZ_matrix = RGB_to_XYZ_matrix
self._XYZ_to_RGB_matrix = None
self.XYZ_to_RGB_matrix = XYZ_to_RGB_matrix
self._encoding_cctf = None
self.encoding_cctf = encoding_cctf
self._decoding_cctf = None
self.decoding_cctf = decoding_cctf
@property
def name(self):
"""
Property for **self._name** private attribute.
Returns
-------
unicode
self._name.
"""
return self._name
@name.setter
def name(self, value):
"""
Setter for **self._name** private attribute.
Parameters
----------
value : unicode
Attribute value.
"""
if value is not None:
assert isinstance(value, basestring), ( # noqa
('"{0}" attribute: "{1}" is not a '
'"basestring" instance!').format('name', value))
self._name = value
@property
def primaries(self):
"""
Property for **self._primaries** private attribute.
Returns
-------
array_like, (3, 2)
self._primaries.
"""
return self._primaries
@primaries.setter
def primaries(self, value):
"""
Setter for **self._primaries** private attribute.
Parameters
----------
value : array_like, (3, 2)
Attribute value.
"""
if value is not None:
value = np.asarray(value)
self._primaries = value
@property
def whitepoint(self):
"""
Property for **self._whitepoint** private attribute.
Returns
-------
array_like
self._whitepoint.
"""
return self._whitepoint
@whitepoint.setter
def whitepoint(self, value):
"""
Setter for **self._whitepoint** private attribute.
Parameters
----------
value : array_like
Attribute value.
"""
if value is not None:
assert isinstance(value, (tuple, list, np.ndarray, np.matrix)), (
('"{0}" attribute: "{1}" is not a "tuple", "list", "ndarray" '
'or "matrix" instance!').format('whitepoint', value))
self._whitepoint = value
@property
def illuminant(self):
"""
Property for **self._illuminant** private attribute.
Returns
-------
unicode
self._illuminant.
"""
return self._illuminant
@illuminant.setter
def illuminant(self, value):
"""
Setter for **self._illuminant** private attribute.
Parameters
----------
value : unicode
Attribute value.
"""
if value is not None:
assert isinstance(value, basestring), ( # noqa
('"{0}" attribute: "{1}" is not a '
'"basestring" instance!').format('illuminant', value))
self._illuminant = value
@property
def RGB_to_XYZ_matrix(self):
"""
Property for **self._to_XYZ** private attribute.
Returns
-------
array_like, (3, 3)
self._to_XYZ.
"""
return self._RGB_to_XYZ_matrix
@RGB_to_XYZ_matrix.setter
def RGB_to_XYZ_matrix(self, value):
"""
Setter for **self._to_XYZ** private attribute.
Parameters
----------
value : array_like
Attribute value.
"""
if value is not None:
value = np.asarray(value)
self._RGB_to_XYZ_matrix = value
@property
def XYZ_to_RGB_matrix(self):
"""
Property for **self._to_RGB** private attribute.
Returns
-------
array_like, (3, 3)
self._to_RGB.
"""
return self._XYZ_to_RGB_matrix
@XYZ_to_RGB_matrix.setter
def XYZ_to_RGB_matrix(self, value):
"""
Setter for **self._to_RGB** private attribute.
Parameters
----------
value : array_like
Attribute value.
"""
if value is not None:
value = np.asarray(value)
self._XYZ_to_RGB_matrix = value
@property
def encoding_cctf(self):
"""
Property for **self._encoding_cctf** private attribute.
Returns
-------
object
self._encoding_cctf.
"""
return self._encoding_cctf
@encoding_cctf.setter
def encoding_cctf(self, value):
"""
Setter for **self._encoding_cctf** private attribute.
Parameters
----------
value : object
Attribute value.
"""
if value is not None:
assert hasattr(value, '__call__'), (
'"{0}" attribute: "{1}" is not callable!'.format(
'encoding_cctf', value))
self._encoding_cctf = value
@property
def decoding_cctf(self):
"""
Property for **self._decoding_cctf** private attribute.
Returns
-------
object
self._decoding_cctf.
"""
return self._decoding_cctf
@decoding_cctf.setter
def decoding_cctf(self, value):
"""
Setter for **self._decoding_cctf** private attribute.
Parameters
----------
value : object
Attribute value.
"""
if value is not None:
assert hasattr(value, '__call__'), (
'"{0}" attribute: "{1}" is not callable!'.format(
'decoding_cctf', value))
self._decoding_cctf = value
[docs]def XYZ_to_RGB(XYZ,
illuminant_XYZ,
illuminant_RGB,
XYZ_to_RGB_matrix,
chromatic_adaptation_transform='CAT02',
encoding_cctf=None):
"""
Converts from *CIE XYZ* tristimulus values to given *RGB* colourspace.
Parameters
----------
XYZ : array_like
*CIE XYZ* tristimulus values.
illuminant_XYZ : array_like
*CIE XYZ* tristimulus values *illuminant* *xy* chromaticity coordinates
or *CIE xyY* colourspace array.
illuminant_RGB : array_like
*RGB* colourspace *illuminant* *xy* chromaticity coordinates or
*CIE xyY* colourspace array.
XYZ_to_RGB_matrix : array_like
*Normalised primary matrix*.
chromatic_adaptation_transform : unicode, optional
**{'CAT02', 'XYZ Scaling', 'Von Kries', 'Bradford', 'Sharp',
'Fairchild', 'CMCCAT97', 'CMCCAT2000', 'CAT02_BRILL_CAT', 'Bianco',
'Bianco PC'}**,
*Chromatic adaptation* transform.
encoding_cctf : object, optional
Encoding colour component transfer function (Encoding CCTF) or
opto-electronic transfer function (OETF / OECF).
Returns
-------
ndarray
*RGB* colourspace array.
Notes
-----
- Input *CIE XYZ* tristimulus values are in domain [0, 1].
- Input *illuminant_XYZ* *xy* chromaticity coordinates or *CIE xyY*
colourspace array are in domain [0, :math:`\infty`].
- Input *illuminant_RGB* *xy* chromaticity coordinates or *CIE xyY*
colourspace array are in domain [0, :math:`\infty`].
- Output *RGB* colourspace array is in range [0, 1].
Examples
--------
>>> XYZ = np.array([0.07049534, 0.10080000, 0.09558313])
>>> illuminant_XYZ = np.array([0.34570, 0.35850])
>>> illuminant_RGB = np.array([0.31270, 0.32900])
>>> chromatic_adaptation_transform = 'Bradford'
>>> XYZ_to_RGB_matrix = np.array([
... [3.24062548, -1.53720797, -0.49862860],
... [-0.96893071, 1.87575606, 0.04151752],
... [0.05571012, -0.20402105, 1.05699594]])
>>> XYZ_to_RGB(
... XYZ,
... illuminant_XYZ,
... illuminant_RGB,
... XYZ_to_RGB_matrix,
... chromatic_adaptation_transform) # doctest: +ELLIPSIS
array([ 0.0110015..., 0.1273504..., 0.1163271...])
"""
M = chromatic_adaptation_matrix_VonKries(
xyY_to_XYZ(xy_to_xyY(illuminant_XYZ)),
xyY_to_XYZ(xy_to_xyY(illuminant_RGB)),
transform=chromatic_adaptation_transform)
XYZ_a = dot_vector(M, XYZ)
RGB = dot_vector(XYZ_to_RGB_matrix, XYZ_a)
if encoding_cctf is not None:
RGB = encoding_cctf(RGB)
return RGB
[docs]def RGB_to_XYZ(RGB,
illuminant_RGB,
illuminant_XYZ,
RGB_to_XYZ_matrix,
chromatic_adaptation_transform='CAT02',
decoding_cctf=None):
"""
Converts from given *RGB* colourspace to *CIE XYZ* tristimulus values.
Parameters
----------
RGB : array_like
*RGB* colourspace array.
illuminant_RGB : array_like
*RGB* colourspace *illuminant* chromaticity coordinates or *CIE xyY*
colourspace array.
illuminant_XYZ : array_like
*CIE XYZ* tristimulus values *illuminant* chromaticity coordinates or
*CIE xyY* colourspace array.
RGB_to_XYZ_matrix : array_like
*Normalised primary matrix*.
chromatic_adaptation_transform : unicode, optional
**{'CAT02', 'XYZ Scaling', 'Von Kries', 'Bradford', 'Sharp',
'Fairchild', 'CMCCAT97', 'CMCCAT2000', 'CAT02_BRILL_CAT', 'Bianco',
'Bianco PC'}**,
*Chromatic adaptation* transform.
decoding_cctf : object, optional
Decoding colour component transfer function (Decoding CCTF) or
electro-optical transfer function (EOTF / EOCF).
Returns
-------
ndarray
*CIE XYZ* tristimulus values.
Notes
-----
- Input *RGB* colourspace array is in domain [0, 1].
- Input *illuminant_RGB* *xy* chromaticity coordinates or *CIE xyY*
colourspace array are in domain [0, :math:`\infty`].
- Input *illuminant_XYZ* *xy* chromaticity coordinates or *CIE xyY*
colourspace array are in domain [0, :math:`\infty`].
- Output *CIE XYZ* tristimulus values are in range [0, 1].
Examples
--------
>>> RGB = np.array([0.01100154, 0.12735048, 0.11632713])
>>> illuminant_RGB = np.array([0.31270, 0.32900])
>>> illuminant_XYZ = np.array([0.34570, 0.35850])
>>> chromatic_adaptation_transform = 'Bradford'
>>> RGB_to_XYZ_matrix = np.array([
... [0.41240000, 0.35760000, 0.18050000],
... [0.21260000, 0.71520000, 0.07220000],
... [0.01930000, 0.11920000, 0.95050000]])
>>> RGB_to_XYZ(
... RGB,
... illuminant_RGB,
... illuminant_XYZ,
... RGB_to_XYZ_matrix,
... chromatic_adaptation_transform) # doctest: +ELLIPSIS
array([ 0.0704953..., 0.1008 , 0.0955831...])
"""
if decoding_cctf is not None:
RGB = decoding_cctf(RGB)
M = chromatic_adaptation_matrix_VonKries(
xyY_to_XYZ(xy_to_xyY(illuminant_RGB)),
xyY_to_XYZ(xy_to_xyY(illuminant_XYZ)),
transform=chromatic_adaptation_transform)
XYZ = dot_vector(RGB_to_XYZ_matrix, RGB)
XYZ_a = dot_vector(M, XYZ)
return XYZ_a
[docs]def RGB_to_RGB(RGB,
input_colourspace,
output_colourspace,
chromatic_adaptation_transform='CAT02'):
"""
Converts from given input *RGB* colourspace to output *RGB* colourspace
using given *chromatic adaptation* method.
Parameters
----------
RGB : array_like
*RGB* colourspace array.
input_colourspace : RGB_Colourspace
*RGB* input colourspace.
output_colourspace : RGB_Colourspace
*RGB* output colourspace.
chromatic_adaptation_transform : unicode, optional
**{'CAT02', 'XYZ Scaling', 'Von Kries', 'Bradford', 'Sharp',
'Fairchild', 'CMCCAT97', 'CMCCAT2000', 'CAT02_BRILL_CAT', 'Bianco',
'Bianco PC'}**,
*Chromatic adaptation* transform.
Returns
-------
ndarray
*RGB* colourspace array.
Notes
-----
- Input / output *RGB* colourspace arrays are in domain / range [0, 1].
- Input / output *RGB* colourspace arrays are assumed to be representing
linear light values.
Examples
--------
>>> from colour import sRGB_COLOURSPACE, PROPHOTO_RGB_COLOURSPACE
>>> RGB = np.array([0.01103742, 0.12734226, 0.11632971])
>>> RGB_to_RGB(
... RGB,
... sRGB_COLOURSPACE,
... PROPHOTO_RGB_COLOURSPACE) # doctest: +ELLIPSIS
array([ 0.0643538..., 0.1157289..., 0.1158038...])
"""
cat = chromatic_adaptation_matrix_VonKries(
xy_to_XYZ(input_colourspace.whitepoint),
xy_to_XYZ(output_colourspace.whitepoint),
chromatic_adaptation_transform)
M = dot_matrix(cat, input_colourspace.RGB_to_XYZ_matrix)
M = dot_matrix(output_colourspace.XYZ_to_RGB_matrix, M)
RGB = dot_vector(M, RGB)
return RGB