Source code for colour.models.rgb

#!/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.ipython.org/github/colour-science/colour-ipython/blob/master/notebooks/models/rgb.ipynb>`_  # noqa
"""

from __future__ import division, unicode_literals

import numpy as np

from colour.algebra import as_array
from colour.models import xy_to_XYZ
from colour.adaptation import chromatic_adaptation_matrix_VonKries

__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__ = ['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* colourspace. XYZ_to_RGB_matrix : array_like, optional Transformation matrix from *CIE XYZ* colourspace to colourspace. transfer_function : object, optional *RGB* colourspace opto-electronic conversion function from linear to colourspace. inverse_transfer_function : object, optional *RGB* colourspace inverse opto-electronic conversion function from colourspace to linear. """ def __init__(self, name, primaries, whitepoint, illuminant=None, RGB_to_XYZ_matrix=None, XYZ_to_RGB_matrix=None, transfer_function=None, inverse_transfer_function=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.__transfer_function = None self.transfer_function = transfer_function self.__inverse_transfer_function = None self.inverse_transfer_function = inverse_transfer_function @property def name(self): """ Property for **self.__name** private attribute. Returns ------- unicode self.__name. """ return self.__name @name.setter
[docs] def name(self, value): """ Setter for **self.__name** private attribute. Parameters ---------- value : unicode Attribute value. """ if value is not None: assert type(value) in (str, unicode), ( ('"{0}" attribute: "{1}" type is not ' '"str" or "unicode"!').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
[docs] def primaries(self, value): """ Setter for **self.__primaries** private attribute. Parameters ---------- value : array_like, (3, 2) Attribute value. """ if value is not None: value = as_array(value) self.__primaries = value
@property def whitepoint(self): """ Property for **self.__whitepoint** private attribute. Returns ------- array_like self.__whitepoint. """ return self.__whitepoint @whitepoint.setter
[docs] def whitepoint(self, value): """ Setter for **self.__whitepoint** private attribute. Parameters ---------- value : array_like Attribute value. """ if value is not None: assert type(value) in (tuple, list, np.ndarray, np.matrix), ( ('"{0}" attribute: "{1}" type is not "tuple", "list", ' '"ndarray" or "matrix"!').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
[docs] def illuminant(self, value): """ Setter for **self.__illuminant** private attribute. Parameters ---------- value : unicode Attribute value. """ if value is not None: assert type(value) in (str, unicode), ( ('"{0}" attribute: "{1}" type is not ' '"str" or "unicode"!').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
[docs] 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 = as_array(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
[docs] 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 = as_array(value) self.__XYZ_to_RGB_matrix = value
@property def transfer_function(self): """ Property for **self.__transfer_function** private attribute. Returns ------- object self.__transfer_function. """ return self.__transfer_function @transfer_function.setter
[docs] def transfer_function(self, value): """ Setter for **self.__transfer_function** private attribute. Parameters ---------- value : object Attribute value. """ if value is not None: assert hasattr(value, '__call__'), ( '"{0}" attribute: "{1}" is not callable!'.format( 'transfer_function', value)) self.__transfer_function = value
@property def inverse_transfer_function(self): """ Property for **self.__inverse_transfer_function** private attribute. Returns ------- object self.__inverse_transfer_function. """ return self.__inverse_transfer_function @inverse_transfer_function.setter
[docs] def inverse_transfer_function(self, value): """ Setter for **self.__inverse_transfer_function** private attribute. Parameters ---------- value : object Attribute value. """ if value is not None: assert hasattr(value, '__call__'), ( '"{0}" attribute: "{1}" is not callable!'.format( 'inverse_transfer_function', value)) self.__inverse_transfer_function = value
[docs]def XYZ_to_RGB(XYZ, illuminant_XYZ, illuminant_RGB, XYZ_to_RGB_matrix, chromatic_adaptation_transform='CAT02', transfer_function=None): """ Converts from *CIE XYZ* colourspace to *RGB* colourspace using given *CIE XYZ* colourspace matrix, *illuminants*, *chromatic adaptation* method, *normalised primary matrix* and *transfer function*. Parameters ---------- XYZ : array_like, (3,) *CIE XYZ* colourspace matrix. illuminant_XYZ : array_like *CIE XYZ* colourspace *illuminant* *xy* chromaticity coordinates. illuminant_RGB : array_like *RGB* colourspace *illuminant* *xy* chromaticity coordinates. XYZ_to_RGB_matrix : array_like, (3, 3) *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. transfer_function : object, optional *Transfer function*. Returns ------- ndarray, (3,) *RGB* colourspace matrix. Notes ----- - Input *CIE XYZ* colourspace matrix is in domain [0, 1]. - Input *illuminant_XYZ* *xy* chromaticity coordinates are in domain [0, 1]. - Input *illuminant_RGB* *xy* chromaticity coordinates are in domain [0, 1]. - Output *RGB* colourspace matrix is in domain [0, 1]. Examples -------- >>> XYZ = np.array([0.07049534, 0.1008, 0.09558313]) >>> illuminant_XYZ = (0.34567, 0.35850) >>> illuminant_RGB = (0.31271, 0.32902) >>> chromatic_adaptation_transform = 'Bradford' >>> XYZ_to_RGB_matrix = np.array([ ... [3.24100326, -1.53739899, -0.49861587], ... [-0.96922426, 1.87592999, 0.04155422], ... [0.05563942, -0.2040112, 1.05714897]]) >>> XYZ_to_RGB( ... XYZ, ... illuminant_XYZ, ... illuminant_RGB, ... XYZ_to_RGB_matrix, ... chromatic_adaptation_transform) # doctest: +ELLIPSIS array([ 0.0110360..., 0.1273446..., 0.1163103...]) """ XYZ = np.ravel(XYZ) cat = chromatic_adaptation_matrix_VonKries( xy_to_XYZ(illuminant_XYZ), xy_to_XYZ(illuminant_RGB), transform=chromatic_adaptation_transform) XYZ_a = np.dot(cat, XYZ) RGB = np.dot(XYZ_to_RGB_matrix.reshape((3, 3)), XYZ_a.reshape((3, 1))) RGB = np.ravel(RGB) if transfer_function is not None: RGB = np.array([transfer_function(x) for x in RGB]) return RGB
[docs]def RGB_to_XYZ(RGB, illuminant_RGB, illuminant_XYZ, RGB_to_XYZ_matrix, chromatic_adaptation_transform='CAT02', inverse_transfer_function=None): """ Converts from *RGB* colourspace to *CIE XYZ* colourspace using given *RGB* colourspace matrix, *illuminants*, *chromatic adaptation* method, *normalised primary matrix* and *transfer function*. Parameters ---------- RGB : array_like, (3,) *RGB* colourspace matrix. illuminant_RGB : array_like *RGB* colourspace *illuminant* chromaticity coordinates. illuminant_XYZ : array_like *CIE XYZ* colourspace *illuminant* chromaticity coordinates. RGB_to_XYZ_matrix : array_like, (3, 3) *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. inverse_transfer_function : object, optional *Inverse transfer function*. Returns ------- ndarray, (3,) *CIE XYZ* colourspace matrix. Notes ----- - Input *RGB* colourspace matrix is in domain [0, 1]. - Input *illuminant_RGB* *xy* chromaticity coordinates are in domain [0, 1]. - Input *illuminant_XYZ* *xy* chromaticity coordinates are in domain [0, 1]. - Output *CIE XYZ* colourspace matrix is in domain [0, 1]. Examples -------- >>> RGB = np.array([0.01103604, 0.12734466, 0.11631037]) >>> illuminant_RGB = (0.31271, 0.32902) >>> illuminant_XYZ = (0.34567, 0.35850) >>> chromatic_adaptation_transform = 'Bradford' >>> RGB_to_XYZ_matrix = np.array([ ... [0.41238656, 0.35759149, 0.18045049], ... [0.21263682, 0.71518298, 0.0721802], ... [0.01933062, 0.11919716, 0.95037259]]) >>> RGB_to_XYZ( ... RGB, ... illuminant_RGB, ... illuminant_XYZ, ... RGB_to_XYZ_matrix, ... chromatic_adaptation_transform) # doctest: +ELLIPSIS array([ 0.0704953..., 0.1008 , 0.0955831...]) """ RGB = np.ravel(RGB) if inverse_transfer_function is not None: RGB = np.array([inverse_transfer_function(x) for x in RGB]) XYZ = np.dot(RGB_to_XYZ_matrix.reshape((3, 3)), RGB.reshape((3, 1))) cat = chromatic_adaptation_matrix_VonKries( xy_to_XYZ(illuminant_RGB), xy_to_XYZ(illuminant_XYZ), transform=chromatic_adaptation_transform) XYZ_a = np.dot(cat, XYZ.reshape((3, 1))) return np.ravel(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, (3,) *RGB* colourspace matrix. 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. ndarray, (3,) *RGB* colourspace matrix. Notes ----- - *RGB* colourspace matrices are in domain [0, 1]. Examples -------- >>> from colour import sRGB_COLOURSPACE, PROPHOTO_RGB_COLOURSPACE >>> RGB = np.array([0.01103604, 0.12734466, 0.11631037]) >>> RGB_to_RGB( ... RGB, ... sRGB_COLOURSPACE, ... PROPHOTO_RGB_COLOURSPACE) # doctest: +ELLIPSIS array([ 0.0643338..., 0.1157362..., 0.1157614...]) """ RGB = np.ravel(RGB) cat = chromatic_adaptation_matrix_VonKries( xy_to_XYZ(input_colourspace.whitepoint), xy_to_XYZ(output_colourspace.whitepoint), chromatic_adaptation_transform) M = np.dot(output_colourspace.XYZ_to_RGB_matrix, np.dot(cat, input_colourspace.RGB_to_XYZ_matrix)) return np.dot(M, RGB)