colour.convert#

colour.convert(a: Any, source: str, target: str, **kwargs: Any) Any[source]#

Convert given object \(a\) from source colour representation to target colour representation using the automatic colour conversion graph.

The conversion is performed by finding the shortest path in a NetworkX DiGraph class instance.

The conversion path adopts the ‘1’ domain-range scale and the object \(a\) is expected to be soft normalised accordingly. For example, CIE XYZ tristimulus values arguments for use with the CAM16 colour appearance model should be in domain [0, 1] instead of the domain [0, 100] used with the ‘Reference’ domain-range scale. The arguments are typically converted as follows:

  • Scalars in domain-range [0, 10], e.g Munsell Value are scaled by 10.

  • Percentages in domain-range [0, 100] are scaled by 100.

  • Degrees in domain-range [0, 360] are scaled by 360.

  • Integers in domain-range [0, 2**n -1] where n is the bit depth are scaled by 2**n -1.

See the Domain-Range Scales page for more information.

Parameters:
  • a (Any) – Object \(a\) to convert. If \(a\) represents a reflectance, transmittance or absorptance value, the expectation is that it is viewed under CIE Standard Illuminant D Series D65. The illuminant can be changed on a per-definition basis along the conversion path.

  • source (str) – Source colour representation, i.e., the source node in the automatic colour conversion graph.

  • target (str) – Target colour representation, i.e., the target node in the automatic colour conversion graph.

  • kwargs (Any) –

    See the documentation of the supported conversion definitions.

    Arguments for the conversion definitions are passed as keyword arguments whose names is those of the conversion definitions and values set as dictionaries. For example, in the conversion from spectral distribution to sRGB colourspace, passing arguments to the colour.sd_to_XYZ() definition is done as follows:

    convert(sd, "Spectral Distribution", "sRGB", sd_to_XYZ={"illuminant": SDS_ILLUMINANTS["FL2"]})
    

    It is also possible to pass keyword arguments directly to the various conversion definitions irrespective of their name. This is dangerous and could cause unexpected behaviour, consider the following conversion:

    convert(sd, "Spectral Distribution", "sRGB", "illuminant": SDS_ILLUMINANTS["FL2"])
    

    Because both the colour.sd_to_XYZ() and colour.XYZ_to_sRGB() definitions have an illuminant argument, SDS_ILLUMINANTS[“FL2”] will be passed to both of them and will raise an exception in the colour.XYZ_to_sRGB() definition. This will be addressed in the future by either catching the exception and trying a new time without the keyword argument or more elegantly via type checking.

    With that in mind, this mechanism offers some good benefits: For example, it allows defining a conversion from CIE XYZ colourspace to n different colour models while passing an illuminant argument but without having to explicitly define all the explicit conversion definition arguments:

    a = np.array([0.20654008, 0.12197225, 0.05136952])
    illuminant = CCS_ILLUMINANTS["CIE 1931 2 Degree Standard Observer"]["D65"]
    for model in ("CIE xyY", "CIE Lab"):
        convert(a, "CIE XYZ", model, illuminant=illuminant)
    

    Instead of:

    for model in ("CIE xyY", "CIE Lab"):
        convert(a, "CIE XYZ", model, XYZ_to_xyY={"illuminant": illuminant}, XYZ_to_Lab={"illuminant": illuminant})
    

    Mixing both approaches is possible for the brevity benefits. It is made possible because the keyword arguments directly passed are filtered first and then the resulting dict is updated with the explicit conversion definition arguments:

    illuminant = CCS_ILLUMINANTS["CIE 1931 2 Degree Standard Observer"]["D65"]
     convert(sd, "Spectral Distribution", "sRGB", "illuminant": SDS_ILLUMINANTS["FL2"], XYZ_to_sRGB={"illuminant": illuminant})
    

    For inspection purposes, verbose is enabled by passing arguments to the colour.describe_conversion_path() definition via the verbose keyword argument as follows:

    convert(sd, "Spectral Distribution", "sRGB", verbose={"mode": "Long"})
    

Returns:

Converted object \(a\).

Return type:

Any

Warning

The domain-range scale is ‘1’ and cannot be changed.

Notes

  • The RGB colour representation is assumed to be linear and representing scene-referred imagery, i.e., Scene-Referred RGB representation. To encode such RGB values as output-referred (display-referred) imagery, i.e., encode the RGB values using an encoding colour component transfer function (Encoding CCTF) / opto-electronic transfer function (OETF), the Output-Referred RGB representation must be used:

    convert(RGB, "Scene-Referred RGB", "Output-Referred RGB")
    

    Likewise, encoded output-referred RGB values can be decoded with the Scene-Referred RGB representation:

    convert(RGB, "Output-Referred RGB", "Scene-Referred RGB")
    
  • The following defaults have been adopted:

    • The default illuminant for the computation is CIE Standard Illuminant D Series D65. It can be changed on a per-definition basis along the conversion path. Note that the conversion from spectral to CIE XYZ tristimulus values remains unchanged.

    • The default RGB colourspace primaries and whitepoint are that of the BT.709/sRGB colourspace. They can be changed on a per-definition basis along the conversion path.

    • When using sRGB as a source or target colour representation, the convenient colour.sRGB_to_XYZ() and colour.XYZ_to_sRGB() definitions are used, respectively. Thus, decoding and encoding using the sRGB electro-optical transfer function (EOTF) and its inverse will be applied by default.

    • Most of the colour appearance models have defaults set according to IEC 61966-2-1:1999 viewing conditions, i.e., sRGB 64 Lux ambient illumination, 80 \(cd/m^2\), adapting field luminance about 20% of a white object in the scene.

Examples

>>> import numpy as np
>>> from colour import SDS_COLOURCHECKERS, SDS_ILLUMINANTS
>>> sd = SDS_COLOURCHECKERS["ColorChecker N Ohta"]["dark skin"]
>>> convert(
...     sd,
...     "Spectral Distribution",
...     "sRGB",
...     verbose={"mode": "Short", "width": 75},
... )
... 
===========================================================================
*                                                                         *
*   [ Conversion Path ]                                                   *
*                                                                         *
*   "sd_to_XYZ" --> "XYZ_to_sRGB"                                         *
*                                                                         *
===========================================================================
array([ 0.4903477...,  0.3018587...,  0.2358768...])
>>> illuminant = SDS_ILLUMINANTS["FL2"]
>>> convert(
...     sd,
...     "Spectral Distribution",
...     "sRGB",
...     sd_to_XYZ={"illuminant": illuminant},
... )
... 
array([ 0.4792457...,  0.3167696...,  0.1736272...])
>>> a = np.array([0.45675795, 0.30986982, 0.24861924])
>>> convert(a, "Output-Referred RGB", "CAM16UCS")
... 
array([ 0.3999481...,  0.0920655...,  0.0812752...])
>>> a = np.array([0.39994811, 0.09206558, 0.08127526])
>>> convert(a, "CAM16UCS", "sRGB", verbose={"mode": "Short", "width": 75})
... 
===========================================================================
*                                                                         *
*   [ Conversion Path ]                                                   *
*                                                                         *
*   "UCS_Li2017_to_JMh_CAM16" --> "JMh_CAM16_to_CAM16" -->                *
*   "CAM16_to_XYZ" --> "XYZ_to_sRGB"                                      *
*                                                                         *
===========================================================================
array([ 0.4567576...,  0.3098826...,  0.2486222...])