colour.appearance.rlab — Colour 0.3.0 documentation

# Source code for colour.appearance.rlab

    #!/usr/bin/env python
    # -*- coding: utf-8 -*-

    """
    RLAB Colour Appearance Model
    ============================

    Defines *RLAB* colour appearance model objects:

    -   :attr:`RLAB_VIEWING_CONDITIONS`
    -   :attr:`RLAB_D_FACTOR`
    -   :class:`RLAB_Specification`
    -   :func:`XYZ_to_RLAB`

    See Also
    --------
    `RLAB Colour Appearance Model IPython Notebook
    <http://nbviewer.ipython.org/github/colour-science/colour-ipython/blob/master/notebooks/appearance/rlab.ipynb>`_  # noqa

    References
    ----------
    .. [1]  **Mark D. Fairchild**,
            *Refinement of the RLAB color space*,
            *Color Research & Application, Volume 21, Issue 5, pages 338–346,
            October 1996*,
            https://ritdml.rit.edu/bitstream/handle/1850/7857/MFairchildArticle12-06-1998.pdf  # noqa
            (Last accessed 16 August 2014)
    .. [2]  **Mark D. Fairchild**, *Color Appearance Models, 3nd Edition*,
            The Wiley-IS&T Series in Imaging Science and Technology,
            published June 2013, ASIN: B00DAYO8E2,
            locations 6019-6178.
    """

    from __future__ import division, unicode_literals

    import math
    import numpy as np
    from collections import namedtuple

    from colour.appearance.hunt import XYZ_to_rgb
    from colour.appearance.hunt import XYZ_TO_HPE_MATRIX
    from colour.utilities import CaseInsensitiveMapping

    __author__ = 'Colour Developers'
    __copyright__ = 'Copyright (C) 2013 - 2014 - Colour Developers'
    __license__ = 'GPL V3.0 - http://www.gnu.org/licenses/'
    __maintainer__ = 'Colour Developers'
    __email__ = 'colour-science@googlegroups.com'
    __status__ = 'Production'

    __all__ = ['R_MATRIX',
               'RLAB_VIEWING_CONDITIONS',
               'RLAB_D_FACTOR',
               'RLAB_ReferenceSpecification',
               'RLAB_Specification',
               'XYZ_to_RLAB']

    R_MATRIX = np.array(
        [[1.9569, -1.1882, 0.2313],
         [0.3612, 0.6388, 0.0000],
         [0.0000, 0.0000, 1.0000]])
    """
    *RLAB* colour appearance model precomputed helper matrix.

    R_MATRIX : array_like, (3, 3)
    """

    RLAB_VIEWING_CONDITIONS = CaseInsensitiveMapping(
        {'Average': 1 / 2.3,
         'Dim': 1 / 2.9,
         'Dark': 1 / 3.5})
    """
    Reference *RLAB* colour appearance model viewing conditions.

    RLAB_VIEWING_CONDITIONS : dict
        ('Average', 'Dim', 'Dark')
    """

    RLAB_D_FACTOR = CaseInsensitiveMapping(
        {'Hard Copy Images': 1,
         'Soft Copy Images': 0,
         'Projected Transparencies, Dark Room': 0.5})
    """
    *RLAB* colour appearance model *Discounting-the-Illuminant* factor values.

    RLAB_D_FACTOR : dict
        ('Hard Copy Images',
        'Soft Copy Images',
        'Projected Transparencies, Dark Room')

    Aliases:

    -   'hard_cp_img': 'Hard Copy Images'
    -   'soft_cp_img': 'Soft Copy Images'
    -   'projected_dark': 'Projected Transparencies, Dark Room'
    """
    RLAB_D_FACTOR['hard_cp_img'] = (
        RLAB_D_FACTOR['Hard Copy Images'])
    RLAB_D_FACTOR['soft_cp_img'] = (
        RLAB_D_FACTOR['Soft Copy Images'])
    RLAB_D_FACTOR['projected_dark'] = (
        RLAB_D_FACTOR['Projected Transparencies, Dark Room'])

    class RLAB_ReferenceSpecification(
        namedtuple('RLAB_ReferenceSpecification',
                   ('LR', 'CR', 'hR', 'sR', 'HR', 'aR', 'bR'))):
        """
        Defines the *RLAB* colour appearance model reference specification.

        This specification has field names consistent with **Mark D. Fairchild**
        reference.

        Parameters
        ----------
        LR : numeric
            Correlate of *Lightness* :math:`L^R`.
        CR : numeric
            Correlate of *achromatic chroma* :math:`C^R`.
        hR : numeric
            *Hue* angle :math:`h^R` in degrees.
        sR : numeric
            Correlate of *saturation* :math:`s^R`.
        HR : numeric
            *Hue* :math:`h` composition :math:`H^R`.
        aR : numeric
            Red–green chromatic response :math:`a^R`.
        bR : numeric
            Yellow–blue chromatic response :math:`b^R`.
        """

    class RLAB_Specification(
        namedtuple('RLAB_Specification',
                   ('J', 'C', 'h', 's', 'HC', 'a', 'b'))):
        """
        Defines the *RLAB* colour appearance model specification.

        This specification has field names consistent with the remaining colour
        appearance models in :mod:`colour.appearance` but diverge from
        **Mark D. Fairchild** reference.

        Parameters
        ----------
        J : numeric
            Correlate of *Lightness* :math:`L^R`.
        C : numeric
            Correlate of *achromatic chroma* :math:`C^R`.
        h : numeric
            *Hue* angle :math:`h^R` in degrees.
        s : numeric
            Correlate of *saturation* :math:`s^R`.
        HC : numeric
            *Hue* :math:`h` composition :math:`H^C`.
        a : numeric
            Red–green chromatic response :math:`a^R`.
        b : numeric
            Yellow–blue chromatic response :math:`b^R`.
        """

    def XYZ_to_RLAB(XYZ,
                    XYZ_n,
                    Y_n,
                    sigma=RLAB_VIEWING_CONDITIONS.get('Average'),
                    D=RLAB_D_FACTOR.get('Hard Copy Images')):
        """
        Computes the RLAB model color appearance correlates.

        Parameters
        ----------
        XYZ : array_like, (3, n)
            *CIE XYZ* colourspace matrix of test sample / stimulus in domain
            [0, 100].
        XYZ_n : array_like, (3,)
            *CIE XYZ* colourspace matrix of reference white in domain [0, 100].
        Y_n : numeric
            Absolute adapting luminance in :math:`cd/m^2`.
        sigma : numeric, optional
            Relative luminance of the surround, see :attr:`RLAB_VIEWING_CONDITIONS`
            for reference.
        D : numeric, optional
            *Discounting-the-Illuminant* factor in domain [0, 1].

        Returns
        -------
        RLAB_Specification
            *RLAB* colour appearance model specification.

        Warning
        -------
        The input domain of that definition is non standard!

        Notes
        -----
        -   Input *CIE XYZ* colourspace matrix is in domain [0, 100].
        -   Input *CIE XYZ_n* colourspace matrix is in domain [0, 100].

        Examples
        --------
        >>> XYZ = np.array([19.01, 20, 21.78])
        >>> XYZ_n = np.array([109.85, 100, 35.58])
        >>> Y_n = 31.83
        >>> sigma = RLAB_VIEWING_CONDITIONS['Average']
        >>> D = RLAB_D_FACTOR['Hard Copy Images']
        >>> XYZ_to_RLAB(XYZ, XYZ_n, Y_n, sigma, D)  # doctest: +ELLIPSIS
        RLAB_Specification(J=49.8347069..., C=54.8700585..., h=286.4860208..., s=1.1010410..., HC=None, a=15.5711021..., b=-52.6142956...)
        """

        X, Y, Z = np.ravel(XYZ)

        # Converting to cone responses.
        LMS = XYZ_to_rgb(XYZ)
        LMS_n = XYZ_to_rgb(XYZ_n)

        # Computing the :math:`A` matrix.
        LMS_l_E = (3 * LMS_n) / (LMS_n[0] + LMS_n[1] + LMS_n[2])
        LMS_p_L = ((1 + (Y_n ** (1 / 3)) + LMS_l_E) /
                   (1 + (Y_n ** (1 / 3)) + (1 / LMS_l_E)))
        LMS_a_L = (LMS_p_L + D * (1 - LMS_p_L)) / LMS_n

        # Special handling here to allow *array_like* variable input.
        if len(np.shape(X)) == 0:
            # *numeric* case.
            # Implementation as per reference.
            aR = np.diag(LMS_a_L)
            XYZ_ref = R_MATRIX.dot(aR).dot(XYZ_TO_HPE_MATRIX).dot(XYZ)
        else:
            # *array_like* case.
            # Constructing huge multidimensional arrays might not be the best idea,
            # we handle each input dimension separately.

            # First figure out how many values we have to deal with.
            dimension = len(X)
            # Then create the output array that will be filled layer by layer.
            XYZ_ref = np.zeros((3, dimension))
            for layer in range(dimension):
                aR = np.diag(LMS_a_L[..., layer])
                XYZ_ref[..., layer] = (
                    R_MATRIX.dot(aR).dot(XYZ_TO_HPE_MATRIX).dot(XYZ[..., layer]))

        X_ref, Y_ref, Z_ref = XYZ_ref

        # -------------------------------------------------------------------------
        # Computing the correlate of *Lightness* :math:`L^R`.
        # -------------------------------------------------------------------------
        LR = 100 * (Y_ref ** sigma)

        # Computing opponent colour dimensions :math:`a^R` and :math:`b^R`.
        aR = 430 * ((X_ref ** sigma) - (Y_ref ** sigma))
        bR = 170 * ((Y_ref ** sigma) - (Z_ref ** sigma))

        # -------------------------------------------------------------------------
        # Computing the *hue* angle :math:`h^R`.
        # -------------------------------------------------------------------------
        hR = math.degrees(np.arctan2(bR, aR)) % 360
        # TODO: Implement hue composition computation.

        # -------------------------------------------------------------------------
        # Computing the correlate of *chroma* :math:`C^R`.
        # -------------------------------------------------------------------------
        CR = np.sqrt((aR ** 2) + (bR ** 2))

        # -------------------------------------------------------------------------
        # Computing the correlate of *saturation* :math:`s^R`.
        # -------------------------------------------------------------------------
        sR = CR / LR

        return RLAB_Specification(LR, CR, hR, sR, None, aR, bR)