Source code for colour.plotting.models

"""
Colour Models Plotting
======================

Define the colour models plotting objects:

-   :func:`colour.plotting.lines_pointer_gamut`
-   :func:`colour.plotting.\
plot_RGB_colourspaces_in_chromaticity_diagram_CIE1931`
-   :func:`colour.plotting.\
plot_RGB_colourspaces_in_chromaticity_diagram_CIE1960UCS`
-   :func:`colour.plotting.\
plot_RGB_colourspaces_in_chromaticity_diagram_CIE1976UCS`
-   :func:`colour.plotting.\
plot_RGB_chromaticities_in_chromaticity_diagram_CIE1931`
-   :func:`colour.plotting.\
plot_RGB_chromaticities_in_chromaticity_diagram_CIE1960UCS`
-   :func:`colour.plotting.\
plot_RGB_chromaticities_in_chromaticity_diagram_CIE1976UCS`
-   :func:`colour.plotting.\
plot_ellipses_MacAdam1942_in_chromaticity_diagram_CIE1931`
-   :func:`colour.plotting.\
plot_ellipses_MacAdam1942_in_chromaticity_diagram_CIE1960UCS`
-   :func:`colour.plotting.\
plot_ellipses_MacAdam1942_in_chromaticity_diagram_CIE1976UCS`
-   :func:`colour.plotting.plot_single_cctf`
-   :func:`colour.plotting.plot_multi_cctfs`
-   :func:`colour.plotting.plot_constant_hue_loci`

References
----------
-   :cite:`Ebner1998` : Ebner, F., & Fairchild, M. D. (1998). Finding constant
    hue surfaces in color space. In G. B. Beretta & R. Eschbach (Eds.), Proc.
    SPIE 3300, Color Imaging: Device-Independent Color, Color Hardcopy, and
    Graphic Arts III, (2 January 1998) (pp. 107-117). doi:10.1117/12.298269
-   :cite:`Hung1995` : Hung, P.-C., & Berns, R. S. (1995). Determination of
    constant Hue Loci for a CRT gamut and their predictions using color
    appearance spaces. Color Research & Application, 20(5), 285-295.
    doi:10.1002/col.5080200506
-   :cite:`Mansencal2019` : Mansencal, T. (2019). Colour - Datasets.
    doi:10.5281/zenodo.3362520
"""

from __future__ import annotations

import typing

import numpy as np
import scipy.optimize
from matplotlib.collections import LineCollection
from matplotlib.patches import Ellipse
from matplotlib.path import Path

from colour.adaptation import chromatic_adaptation_VonKries
from colour.algebra import normalise_maximum
from colour.colorimetry import MultiSpectralDistributions
from colour.constants import DTYPE_FLOAT_DEFAULT, EPSILON
from colour.geometry import (
    ellipse_coefficients_canonical_form,
    ellipse_fitting,
    point_at_angle_on_ellipse,
)
from colour.graph import convert

if typing.TYPE_CHECKING:
    from matplotlib.axes import Axes
    from matplotlib.figure import Figure
    from colour.hints import (
        Any,
        ArrayLike,
        Callable,
        Dict,
        Literal,
        LiteralColourspaceModel,
        LiteralRGBColourspace,
        NDArray,
        NDArrayFloat,
        Sequence,
        Tuple,
    )

from colour.hints import List, cast
from colour.models import LCHab_to_Lab  # pyright: ignore
from colour.models import (
    CCS_ILLUMINANT_POINTER_GAMUT,
    CCS_POINTER_GAMUT_BOUNDARY,
    CCTF_DECODINGS,
    CCTF_ENCODINGS,
    COLOURSPACE_MODELS_AXIS_LABELS,
    COLOURSPACE_MODELS_DOMAIN_RANGE_SCALE_1_TO_REFERENCE,
    DATA_MACADAM_1942_ELLIPSES,
    DATA_POINTER_GAMUT_VOLUME,
    Lab_to_XYZ,
    RGB_Colourspace,
    RGB_to_RGB,
    RGB_to_XYZ,
    XYZ_to_RGB,
    XYZ_to_xy,
    xy_to_XYZ,
)
from colour.plotting import (
    CONSTANTS_COLOUR_STYLE,
    METHODS_CHROMATICITY_DIAGRAM,
    XYZ_to_plotting_colourspace,
    artist,
    colour_cycle,
    colour_style,
    filter_cmfs,
    filter_passthrough,
    filter_RGB_colourspaces,
    override_style,
    plot_chromaticity_diagram_CIE1931,
    plot_chromaticity_diagram_CIE1960UCS,
    plot_chromaticity_diagram_CIE1976UCS,
    plot_multi_functions,
    render,
    update_settings_collection,
)
from colour.plotting.diagrams import plot_chromaticity_diagram
from colour.utilities import (
    CanonicalMapping,
    as_array,
    as_float_array,
    as_int_array,
    domain_range_scale,
    first_item,
    optional,
    tsplit,
    validate_method,
    zeros,
)

__author__ = "Colour Developers"
__copyright__ = "Copyright 2013 Colour Developers"
__license__ = "BSD-3-Clause - https://opensource.org/licenses/BSD-3-Clause"
__maintainer__ = "Colour Developers"
__email__ = "colour-developers@colour-science.org"
__status__ = "Production"

__all__ = [
    "COLOURSPACE_MODELS_AXIS_ORDER",
    "colourspace_model_axis_reorder",
    "lines_pointer_gamut",
    "plot_pointer_gamut",
    "plot_RGB_colourspaces_in_chromaticity_diagram",
    "plot_RGB_colourspaces_in_chromaticity_diagram_CIE1931",
    "plot_RGB_colourspaces_in_chromaticity_diagram_CIE1960UCS",
    "plot_RGB_colourspaces_in_chromaticity_diagram_CIE1976UCS",
    "plot_RGB_chromaticities_in_chromaticity_diagram",
    "plot_RGB_chromaticities_in_chromaticity_diagram_CIE1931",
    "plot_RGB_chromaticities_in_chromaticity_diagram_CIE1960UCS",
    "plot_RGB_chromaticities_in_chromaticity_diagram_CIE1976UCS",
    "ellipses_MacAdam1942",
    "plot_ellipses_MacAdam1942_in_chromaticity_diagram",
    "plot_ellipses_MacAdam1942_in_chromaticity_diagram_CIE1931",
    "plot_ellipses_MacAdam1942_in_chromaticity_diagram_CIE1960UCS",
    "plot_ellipses_MacAdam1942_in_chromaticity_diagram_CIE1976UCS",
    "plot_single_cctf",
    "plot_multi_cctfs",
    "plot_constant_hue_loci",
]

COLOURSPACE_MODELS_AXIS_ORDER: CanonicalMapping = CanonicalMapping(
    {
        "CAM02LCD": (1, 2, 0),
        "CAM02SCD": (1, 2, 0),
        "CAM02UCS": (1, 2, 0),
        "CAM16LCD": (1, 2, 0),
        "CAM16SCD": (1, 2, 0),
        "CAM16UCS": (1, 2, 0),
        "CIE 1931": (0, 1, 2),
        "CIE 1960 UCS": (0, 1, 2),
        "CIE 1976 UCS": (0, 1, 2),
        "CIE LCHab": (1, 2, 0),
        "CIE LCHuv": (1, 2, 0),
        "CIE Lab": (1, 2, 0),
        "CIE Luv": (1, 2, 0),
        "CIE UCS": (0, 1, 2),
        "CIE UVW": (1, 2, 0),
        "CIE XYZ": (0, 1, 2),
        "CIE xyY": (0, 1, 2),
        "DIN99": (1, 2, 0),
        "HCL": (0, 1, 2),
        "HSL": (0, 1, 2),
        "HSV": (0, 1, 2),
        "Hunter Lab": (1, 2, 0),
        "Hunter Rdab": (1, 2, 0),
        "ICaCb": (1, 2, 0),
        "ICtCp": (1, 2, 0),
        "IHLS": (0, 2, 1),
        "IPT Ragoo 2021": (1, 2, 0),
        "IPT": (1, 2, 0),
        "IgPgTg": (1, 2, 0),
        "Jzazbz": (1, 2, 0),
        "OSA UCS": (1, 2, 0),
        "Oklab": (1, 2, 0),
        "RGB": (0, 1, 2),
        "YCbCr": (1, 2, 0),
        "YCoCg": (1, 2, 0),
        "Yrg": (1, 2, 0),
        "hdr-CIELAB": (1, 2, 0),
        "hdr-IPT": (1, 2, 0),
    }
)
"""Colourspace models axis order."""


[docs] def colourspace_model_axis_reorder( a: ArrayLike, model: LiteralColourspaceModel | str, direction: Literal["Forward", "Inverse"] | str = "Forward", ) -> NDArrayFloat: """ Reorder the axes of given colourspace model :math:`a` array according to the most common volume plotting axes order. Parameters ---------- a Colourspace model :math:`a` array. model Colourspace model, see :attr:`colour.COLOURSPACE_MODELS` attribute for the list of supported colourspace models. direction Reordering direction. Returns ------- :class:`numpy.ndarray` Reordered colourspace model :math:`a` array. Examples -------- >>> a = np.array([0, 1, 2]) >>> colourspace_model_axis_reorder(a, "CIE Lab") array([ 1., 2., 0.]) >>> colourspace_model_axis_reorder(a, "IPT") array([ 1., 2., 0.]) >>> colourspace_model_axis_reorder(a, "OSA UCS") array([ 1., 2., 0.]) >>> b = np.array([1, 2, 0]) >>> colourspace_model_axis_reorder(b, "OSA UCS", "Inverse") array([ 0., 1., 2.]) """ a = as_float_array(a) model = validate_method( model, tuple(COLOURSPACE_MODELS_AXIS_ORDER), '"{0}" model is invalid, it must be one of {1}!', ) direction = validate_method( direction, ("Forward", "Inverse"), '"{0}" direction is invalid, it must be one of {1}!', ) order = COLOURSPACE_MODELS_AXIS_ORDER.get(model, (0, 1, 2)) if direction == "forward": indexes = (order[0], order[1], order[2]) else: indexes = (order.index(0), order.index(1), order.index(2)) return a[..., indexes]
[docs] def lines_pointer_gamut( method: (Literal["CIE 1931", "CIE 1960 UCS", "CIE 1976 UCS"] | str) = "CIE 1931", ) -> tuple[NDArray, NDArray]: """ Return the *Pointer's Gamut* line vertices, i.e., positions, normals and colours, according to given method. Parameters ---------- method *Chromaticity Diagram* method. Returns ------- :class:`tuple` Tuple of *Pointer's Gamut* boundary and volume vertices. Examples -------- >>> lines = lines_pointer_gamut() >>> len(lines) 2 >>> lines[0].dtype dtype([('position', '<f8', (2,)), ('normal', '<f8', (2,)), \ ('colour', '<f8', (3,))]) >>> lines[1].dtype dtype([('position', '<f8', (2,)), ('normal', '<f8', (2,)), \ ('colour', '<f8', (3,))]) """ method = validate_method(method, ("CIE 1931", "CIE 1960 UCS", "CIE 1976 UCS")) illuminant = CONSTANTS_COLOUR_STYLE.colour.colourspace.whitepoint XYZ_to_ij = METHODS_CHROMATICITY_DIAGRAM[method]["XYZ_to_ij"] ij_to_XYZ = METHODS_CHROMATICITY_DIAGRAM[method]["ij_to_XYZ"] XYZ = xy_to_XYZ(CCS_POINTER_GAMUT_BOUNDARY) XYZ = chromatic_adaptation_VonKries( XYZ, xy_to_XYZ(CCS_ILLUMINANT_POINTER_GAMUT), xy_to_XYZ(illuminant) ) ij_b = XYZ_to_ij(XYZ) ij_b = np.vstack([ij_b, ij_b[0]]) colours_b = normalise_maximum( XYZ_to_plotting_colourspace(ij_to_XYZ(ij_b, illuminant), illuminant), axis=-1, ) lines_b = zeros( ij_b.shape[0], [ ("position", DTYPE_FLOAT_DEFAULT, 2), ("normal", DTYPE_FLOAT_DEFAULT, 2), ("colour", DTYPE_FLOAT_DEFAULT, 3), ], # pyright: ignore ) lines_b["position"] = ij_b lines_b["colour"] = colours_b XYZ = Lab_to_XYZ( LCHab_to_Lab(DATA_POINTER_GAMUT_VOLUME), CCS_ILLUMINANT_POINTER_GAMUT ) XYZ = chromatic_adaptation_VonKries( XYZ, xy_to_XYZ(CCS_ILLUMINANT_POINTER_GAMUT), xy_to_XYZ(illuminant) ) ij_v = XYZ_to_ij(XYZ) colours_v = normalise_maximum( XYZ_to_plotting_colourspace(ij_to_XYZ(ij_v, illuminant), illuminant), axis=-1, ) lines_v = zeros( ij_v.shape[0], [ ("position", DTYPE_FLOAT_DEFAULT, 2), ("normal", DTYPE_FLOAT_DEFAULT, 2), ("colour", DTYPE_FLOAT_DEFAULT, 3), ], # pyright: ignore ) lines_v["position"] = ij_v lines_v["colour"] = colours_v return lines_b, lines_v
[docs] @override_style() def plot_pointer_gamut( pointer_gamut_colours: ArrayLike | str | None = None, pointer_gamut_opacity: float = 1, method: (Literal["CIE 1931", "CIE 1960 UCS", "CIE 1976 UCS"] | str) = "CIE 1931", **kwargs: Any, ) -> Tuple[Figure, Axes]: """ Plot *Pointer's Gamut* according to given method. Parameters ---------- pointer_gamut_colours Colours of the *Pointer's Gamut*. pointer_gamut_opacity Opacity of the *Pointer's Gamut*. method Plotting method. Other Parameters ---------------- kwargs {:func:`colour.plotting.artist`, :func:`colour.plotting.render`}, See the documentation of the previously listed definitions. Returns ------- :class:`tuple` Current figure and axes. Examples -------- >>> plot_pointer_gamut(pointer_gamut_colours="RGB") # doctest: +ELLIPSIS (<Figure size ... with 1 Axes>, <...Axes...>) .. image:: ../_static/Plotting_Plot_Pointer_Gamut.png :align: center :alt: plot_pointer_gamut """ method = validate_method(method, ("CIE 1931", "CIE 1960 UCS", "CIE 1976 UCS")) pointer_gamut_colours = optional( pointer_gamut_colours, CONSTANTS_COLOUR_STYLE.colour.dark ) use_RGB_colours = str(pointer_gamut_colours).upper() == "RGB" pointer_gamut_opacity = optional( pointer_gamut_opacity, CONSTANTS_COLOUR_STYLE.opacity.high ) settings: Dict[str, Any] = {"uniform": True} settings.update(kwargs) _figure, axes = artist(**settings) lines_b, lines_v = lines_pointer_gamut(method) axes.add_collection( LineCollection( np.reshape( np.concatenate( [lines_b["position"][:-1], lines_b["position"][1:]], axis=1, # pyright: ignore ), (-1, 2, 2), ), colors=(lines_b["colour"] if use_RGB_colours else pointer_gamut_colours), alpha=pointer_gamut_opacity, zorder=CONSTANTS_COLOUR_STYLE.zorder.foreground_line, ) ) scatter_settings = { "alpha": pointer_gamut_opacity / 2, "c": lines_v["colour"] if use_RGB_colours else pointer_gamut_colours, "marker": "+", "zorder": CONSTANTS_COLOUR_STYLE.zorder.foreground_scatter, } axes.scatter( lines_v["position"][..., 0], lines_v["position"][..., 1], **scatter_settings, ) settings.update({"axes": axes}) settings.update(kwargs) return render(**settings)
[docs] @override_style() def plot_RGB_colourspaces_in_chromaticity_diagram( colourspaces: ( RGB_Colourspace | LiteralRGBColourspace | str | Sequence[RGB_Colourspace | LiteralRGBColourspace | str] ), cmfs: ( MultiSpectralDistributions | str | Sequence[MultiSpectralDistributions | str] ) = "CIE 1931 2 Degree Standard Observer", chromaticity_diagram_callable: Callable = plot_chromaticity_diagram, method: (Literal["CIE 1931", "CIE 1960 UCS", "CIE 1976 UCS"] | str) = "CIE 1931", show_whitepoints: bool = True, show_pointer_gamut: bool = False, chromatically_adapt: bool = False, plot_kwargs: dict | List[dict] | None = None, **kwargs: Any, ) -> Tuple[Figure, Axes]: """ Plot given *RGB* colourspaces in the *Chromaticity Diagram* according to given method. Parameters ---------- colourspaces *RGB* colourspaces to plot. ``colourspaces`` elements can be of any type or form supported by the :func:`colour.plotting.common.filter_RGB_colourspaces` definition. cmfs Standard observer colour matching functions used for computing the spectral locus boundaries. ``cmfs`` can be of any type or form supported by the :func:`colour.plotting.common.filter_cmfs` definition. chromaticity_diagram_callable Callable responsible for drawing the *Chromaticity Diagram*. method *Chromaticity Diagram* method. show_whitepoints Whether to display the *RGB* colourspaces whitepoints. show_pointer_gamut Whether to display the *Pointer's Gamut*. chromatically_adapt Whether to chromatically adapt the *RGB* colourspaces given in ``colourspaces`` to the whitepoint of the default plotting colourspace. plot_kwargs Keyword arguments for the :func:`matplotlib.pyplot.plot` definition, used to control the style of the plotted *RGB* colourspaces. ``plot_kwargs`` can be either a single dictionary applied to all the plotted *RGB* colourspaces with the same settings or a sequence of dictionaries with different settings for each plotted *RGB* colourspace. Other Parameters ---------------- kwargs {:func:`colour.plotting.artist`, :func:`colour.plotting.diagrams.plot_chromaticity_diagram`, :func:`colour.plotting.models.plot_pointer_gamut`, :func:`colour.plotting.render`}, See the documentation of the previously listed definitions. Returns ------- :class:`tuple` Current figure and axes. Examples -------- >>> plot_kwargs = [ ... {"color": "r"}, ... {"linestyle": "dashed"}, ... {"marker": None}, ... ] >>> plot_RGB_colourspaces_in_chromaticity_diagram( ... ["ITU-R BT.709", "ACEScg", "S-Gamut"], plot_kwargs=plot_kwargs ... ) ... # doctest: +ELLIPSIS (<Figure size ... with 1 Axes>, <...Axes...>) .. image:: ../_static/Plotting_\ Plot_RGB_Colourspaces_In_Chromaticity_Diagram.png :align: center :alt: plot_RGB_colourspaces_in_chromaticity_diagram """ method = validate_method(method, ("CIE 1931", "CIE 1960 UCS", "CIE 1976 UCS")) colourspaces = cast( List[RGB_Colourspace], list(filter_RGB_colourspaces(colourspaces).values()), ) # pyright: ignore settings: Dict[str, Any] = {"uniform": True} settings.update(kwargs) _figure, axes = artist(**settings) cmfs = cast(MultiSpectralDistributions, first_item(filter_cmfs(cmfs).values())) title = ( f"{', '.join([colourspace.name for colourspace in colourspaces])}\n" f"{cmfs.name} - {method.upper()} Chromaticity Diagram" ) settings = {"axes": axes, "title": title, "method": method} settings.update(kwargs) settings["show"] = False chromaticity_diagram_callable(**settings) if show_pointer_gamut: settings = {"axes": axes, "method": method} settings.update(kwargs) settings["show"] = False plot_pointer_gamut(**settings) xy_to_ij = METHODS_CHROMATICITY_DIAGRAM[method]["xy_to_ij"] if method == "cie 1931": x_limit_min, x_limit_max = [-0.1], [0.9] y_limit_min, y_limit_max = [-0.1], [0.9] elif method == "cie 1960 ucs": x_limit_min, x_limit_max = [-0.1], [0.7] y_limit_min, y_limit_max = [-0.2], [0.6] elif method == "cie 1976 ucs": x_limit_min, x_limit_max = [-0.1], [0.7] y_limit_min, y_limit_max = [-0.1], [0.7] settings = {"colour_cycle_count": len(colourspaces)} settings.update(kwargs) cycle = colour_cycle(**settings) plotting_colourspace = CONSTANTS_COLOUR_STYLE.colour.colourspace plot_settings_collection = [ { "label": f"{colourspace.name}", "marker": "o", "color": next(cycle)[:3], "zorder": CONSTANTS_COLOUR_STYLE.zorder.foreground_line, } for colourspace in colourspaces ] if plot_kwargs is not None: update_settings_collection( plot_settings_collection, plot_kwargs, len(colourspaces) ) for i, colourspace in enumerate(colourspaces): plot_settings = plot_settings_collection[i] if chromatically_adapt and not np.array_equal( colourspace.whitepoint, plotting_colourspace.whitepoint ): colourspace = colourspace.chromatically_adapt( # noqa: PLW2901 plotting_colourspace.whitepoint, plotting_colourspace.whitepoint_name, ) # RGB colourspaces such as *ACES2065-1* have primaries with # chromaticity coordinates set to 0 thus we prevent nan from being # yield by zero division in later colour transformations. P = np.where( colourspace.primaries == 0, EPSILON, colourspace.primaries, ) P = xy_to_ij(P) W = xy_to_ij(colourspace.whitepoint) P_p = np.vstack([P, P[0]]) axes.plot(P_p[..., 0], P_p[..., 1], **plot_settings) if show_whitepoints: plot_settings["marker"] = "o" plot_settings.pop("label") W_p = np.vstack([W, W]) axes.plot(W_p[..., 0], W_p[..., 1], **plot_settings) x_limit_min.append(cast(float, np.amin(P[..., 0]) - 0.1)) y_limit_min.append(cast(float, np.amin(P[..., 1]) - 0.1)) x_limit_max.append(cast(float, np.amax(P[..., 0]) + 0.1)) y_limit_max.append(cast(float, np.amax(P[..., 1]) + 0.1)) bounding_box = ( min(x_limit_min), max(x_limit_max), min(y_limit_min), max(y_limit_max), ) settings.update( { "show": True, "legend": True, "bounding_box": bounding_box, } ) settings.update(kwargs) return render(**settings)
[docs] @override_style() def plot_RGB_colourspaces_in_chromaticity_diagram_CIE1931( colourspaces: ( RGB_Colourspace | LiteralRGBColourspace | str | Sequence[RGB_Colourspace | LiteralRGBColourspace | str] ), cmfs: ( MultiSpectralDistributions | str | Sequence[MultiSpectralDistributions | str] ) = "CIE 1931 2 Degree Standard Observer", chromaticity_diagram_callable_CIE1931: Callable = ( plot_chromaticity_diagram_CIE1931 ), show_whitepoints: bool = True, show_pointer_gamut: bool = False, chromatically_adapt: bool = False, plot_kwargs: dict | List[dict] | None = None, **kwargs: Any, ) -> Tuple[Figure, Axes]: """ Plot given *RGB* colourspaces in the *CIE 1931 Chromaticity Diagram*. Parameters ---------- colourspaces *RGB* colourspaces to plot. ``colourspaces`` elements can be of any type or form supported by the :func:`colour.plotting.common.filter_RGB_colourspaces` definition. cmfs Standard observer colour matching functions used for computing the spectral locus boundaries. ``cmfs`` can be of any type or form supported by the :func:`colour.plotting.common.filter_cmfs` definition. chromaticity_diagram_callable_CIE1931 Callable responsible for drawing the *CIE 1931 Chromaticity Diagram*. show_whitepoints Whether to display the *RGB* colourspaces whitepoints. show_pointer_gamut Whether to display the *Pointer's Gamut*. chromatically_adapt Whether to chromatically adapt the *RGB* colourspaces given in ``colourspaces`` to the whitepoint of the default plotting colourspace. plot_kwargs Keyword arguments for the :func:`matplotlib.pyplot.plot` definition, used to control the style of the plotted *RGB* colourspaces. ``plot_kwargs`` can be either a single dictionary applied to all the plotted *RGB* colourspaces with the same settings or a sequence of dictionaries with different settings for each plotted *RGB* colourspace. Other Parameters ---------------- kwargs {:func:`colour.plotting.artist`, :func:`colour.plotting.diagrams.plot_chromaticity_diagram`, :func:`colour.plotting.models.plot_pointer_gamut`, :func:`colour.plotting.models.\ plot_RGB_colourspaces_in_chromaticity_diagram`, :func:`colour.plotting.render`}, See the documentation of the previously listed definitions. Returns ------- :class:`tuple` Current figure and axes. Examples -------- >>> plot_RGB_colourspaces_in_chromaticity_diagram_CIE1931( ... ["ITU-R BT.709", "ACEScg", "S-Gamut"] ... ) ... # doctest: +ELLIPSIS (<Figure size ... with 1 Axes>, <...Axes...>) .. image:: ../_static/Plotting_\ Plot_RGB_Colourspaces_In_Chromaticity_Diagram_CIE1931.png :align: center :alt: plot_RGB_colourspaces_in_chromaticity_diagram_CIE1931 """ settings = dict(kwargs) settings.update({"method": "CIE 1931"}) return plot_RGB_colourspaces_in_chromaticity_diagram( colourspaces, cmfs, chromaticity_diagram_callable_CIE1931, show_whitepoints=show_whitepoints, show_pointer_gamut=show_pointer_gamut, chromatically_adapt=chromatically_adapt, plot_kwargs=plot_kwargs, **settings, )
[docs] @override_style() def plot_RGB_colourspaces_in_chromaticity_diagram_CIE1960UCS( colourspaces: ( RGB_Colourspace | LiteralRGBColourspace | str | Sequence[RGB_Colourspace | LiteralRGBColourspace | str] ), cmfs: ( MultiSpectralDistributions | str | Sequence[MultiSpectralDistributions | str] ) = "CIE 1931 2 Degree Standard Observer", chromaticity_diagram_callable_CIE1960UCS: Callable = ( plot_chromaticity_diagram_CIE1960UCS ), show_whitepoints: bool = True, show_pointer_gamut: bool = False, chromatically_adapt: bool = False, plot_kwargs: dict | List[dict] | None = None, **kwargs: Any, ) -> Tuple[Figure, Axes]: """ Plot given *RGB* colourspaces in the *CIE 1960 UCS Chromaticity Diagram*. Parameters ---------- colourspaces *RGB* colourspaces to plot. ``colourspaces`` elements can be of any type or form supported by the :func:`colour.plotting.common.filter_RGB_colourspaces` definition. cmfs Standard observer colour matching functions used for computing the spectral locus boundaries. ``cmfs`` can be of any type or form supported by the :func:`colour.plotting.common.filter_cmfs` definition. chromaticity_diagram_callable_CIE1960UCS Callable responsible for drawing the *CIE 1960 UCS Chromaticity Diagram*. show_whitepoints Whether to display the *RGB* colourspaces whitepoints. show_pointer_gamut Whether to display the *Pointer's Gamut*. chromatically_adapt Whether to chromatically adapt the *RGB* colourspaces given in ``colourspaces`` to the whitepoint of the default plotting colourspace. plot_kwargs Keyword arguments for the :func:`matplotlib.pyplot.plot` definition, used to control the style of the plotted *RGB* colourspaces. ``plot_kwargs`` can be either a single dictionary applied to all the plotted *RGB* colourspaces with the same settings or a sequence of dictionaries with different settings for each plotted *RGB* colourspace. Other Parameters ---------------- kwargs {:func:`colour.plotting.artist`, :func:`colour.plotting.diagrams.plot_chromaticity_diagram`, :func:`colour.plotting.models.plot_pointer_gamut`, :func:`colour.plotting.models.\ plot_RGB_colourspaces_in_chromaticity_diagram`, :func:`colour.plotting.render`}, See the documentation of the previously listed definitions. Returns ------- :class:`tuple` Current figure and axes. Examples -------- >>> plot_RGB_colourspaces_in_chromaticity_diagram_CIE1960UCS( ... ["ITU-R BT.709", "ACEScg", "S-Gamut"] ... ) ... # doctest: +ELLIPSIS (<Figure size ... with 1 Axes>, <...Axes...>) .. image:: ../_static/Plotting_\ Plot_RGB_Colourspaces_In_Chromaticity_Diagram_CIE1960UCS.png :align: center :alt: plot_RGB_colourspaces_in_chromaticity_diagram_CIE1960UCS """ settings = dict(kwargs) settings.update({"method": "CIE 1960 UCS"}) return plot_RGB_colourspaces_in_chromaticity_diagram( colourspaces, cmfs, chromaticity_diagram_callable_CIE1960UCS, show_whitepoints=show_whitepoints, show_pointer_gamut=show_pointer_gamut, chromatically_adapt=chromatically_adapt, plot_kwargs=plot_kwargs, **settings, )
[docs] @override_style() def plot_RGB_colourspaces_in_chromaticity_diagram_CIE1976UCS( colourspaces: ( RGB_Colourspace | LiteralRGBColourspace | str | Sequence[RGB_Colourspace | LiteralRGBColourspace | str] ), cmfs: ( MultiSpectralDistributions | str | Sequence[MultiSpectralDistributions | str] ) = "CIE 1931 2 Degree Standard Observer", chromaticity_diagram_callable_CIE1976UCS: Callable = ( plot_chromaticity_diagram_CIE1976UCS ), show_whitepoints: bool = True, show_pointer_gamut: bool = False, chromatically_adapt: bool = False, plot_kwargs: dict | List[dict] | None = None, **kwargs: Any, ) -> Tuple[Figure, Axes]: """ Plot given *RGB* colourspaces in the *CIE 1976 UCS Chromaticity Diagram*. Parameters ---------- colourspaces *RGB* colourspaces to plot. ``colourspaces`` elements can be of any type or form supported by the :func:`colour.plotting.common.filter_RGB_colourspaces` definition. cmfs Standard observer colour matching functions used for computing the spectral locus boundaries. ``cmfs`` can be of any type or form supported by the :func:`colour.plotting.common.filter_cmfs` definition. chromaticity_diagram_callable_CIE1976UCS Callable responsible for drawing the *CIE 1976 UCS Chromaticity Diagram*. show_whitepoints Whether to display the *RGB* colourspaces whitepoints. show_pointer_gamut Whether to display the *Pointer's Gamut*. chromatically_adapt Whether to chromatically adapt the *RGB* colourspaces given in ``colourspaces`` to the whitepoint of the default plotting colourspace. plot_kwargs Keyword arguments for the :func:`matplotlib.pyplot.plot` definition, used to control the style of the plotted *RGB* colourspaces. ``plot_kwargs`` can be either a single dictionary applied to all the plotted *RGB* colourspaces with the same settings or a sequence of dictionaries with different settings for each plotted *RGB* colourspace. Other Parameters ---------------- kwargs {:func:`colour.plotting.artist`, :func:`colour.plotting.diagrams.plot_chromaticity_diagram`, :func:`colour.plotting.models.plot_pointer_gamut`, :func:`colour.plotting.models.\ plot_RGB_colourspaces_in_chromaticity_diagram`, :func:`colour.plotting.render`}, See the documentation of the previously listed definitions. Returns ------- :class:`tuple` Current figure and axes. Examples -------- >>> plot_RGB_colourspaces_in_chromaticity_diagram_CIE1976UCS( ... ["ITU-R BT.709", "ACEScg", "S-Gamut"] ... ) ... # doctest: +ELLIPSIS (<Figure size ... with 1 Axes>, <...Axes...>) .. image:: ../_static/Plotting_\ Plot_RGB_Colourspaces_In_Chromaticity_Diagram_CIE1976UCS.png :align: center :alt: plot_RGB_colourspaces_in_chromaticity_diagram_CIE1976UCS """ settings = dict(kwargs) settings.update({"method": "CIE 1976 UCS"}) return plot_RGB_colourspaces_in_chromaticity_diagram( colourspaces, cmfs, chromaticity_diagram_callable_CIE1976UCS, show_whitepoints=show_whitepoints, show_pointer_gamut=show_pointer_gamut, chromatically_adapt=chromatically_adapt, plot_kwargs=plot_kwargs, **settings, )
[docs] @override_style() def plot_RGB_chromaticities_in_chromaticity_diagram( RGB: ArrayLike, colourspace: ( RGB_Colourspace | str | Sequence[RGB_Colourspace | LiteralRGBColourspace | str] ) = "sRGB", chromaticity_diagram_callable: Callable = ( plot_RGB_colourspaces_in_chromaticity_diagram ), method: (Literal["CIE 1931", "CIE 1960 UCS", "CIE 1976 UCS"] | str) = "CIE 1931", scatter_kwargs: dict | None = None, **kwargs: Any, ) -> Tuple[Figure, Axes]: """ Plot given *RGB* colourspace array in the *Chromaticity Diagram* according to given method. Parameters ---------- RGB *RGB* colourspace array. colourspace *RGB* colourspace of the *RGB* array. ``colourspace`` can be of any type or form supported by the :func:`colour.plotting.common.filter_RGB_colourspaces` definition. chromaticity_diagram_callable Callable responsible for drawing the *Chromaticity Diagram*. method *Chromaticity Diagram* method. scatter_kwargs Keyword arguments for the :func:`matplotlib.pyplot.scatter` definition. The following special keyword arguments can also be used: - ``c`` : If ``c`` is set to *RGB*, the scatter will use the colours as given by the ``RGB`` argument. - ``apply_cctf_encoding`` : If ``apply_cctf_encoding`` is set to *False*, the encoding colour component transfer function / opto-electronic transfer function is not applied when encoding the samples to the plotting space. Other Parameters ---------------- kwargs {:func:`colour.plotting.artist`, :func:`colour.plotting.diagrams.plot_chromaticity_diagram`, :func:`colour.plotting.models.\ plot_RGB_colourspaces_in_chromaticity_diagram`, :func:`colour.plotting.render`}, See the documentation of the previously listed definitions. Returns ------- :class:`tuple` Current figure and axes. Examples -------- >>> RGB = np.random.random((128, 128, 3)) >>> plot_RGB_chromaticities_in_chromaticity_diagram(RGB, "ITU-R BT.709") ... # doctest: +ELLIPSIS (<Figure size ... with 1 Axes>, <...Axes...>) .. image:: ../_static/Plotting_\ Plot_RGB_Chromaticities_In_Chromaticity_Diagram.png :align: center :alt: plot_RGB_chromaticities_in_chromaticity_diagram """ RGB = np.reshape(as_float_array(RGB)[..., :3], (-1, 3)) method = validate_method(method, ("CIE 1931", "CIE 1960 UCS", "CIE 1976 UCS")) settings: Dict[str, Any] = {"uniform": True} settings.update(kwargs) _figure, axes = artist(**settings) scatter_settings = { "s": 40, "c": "RGB", "marker": "o", "alpha": 0.85, "zorder": CONSTANTS_COLOUR_STYLE.zorder.midground_scatter, "apply_cctf_encoding": True, } if scatter_kwargs is not None: scatter_settings.update(scatter_kwargs) settings = dict(kwargs) settings.update({"axes": axes, "show": False}) colourspace = cast( RGB_Colourspace, first_item(filter_RGB_colourspaces(colourspace).values()), ) settings["colourspaces"] = [colourspace, *settings.get("colourspaces", [])] chromaticity_diagram_callable(**settings) use_RGB_colours = str(scatter_settings["c"]).upper() == "RGB" apply_cctf_encoding = scatter_settings.pop("apply_cctf_encoding") if use_RGB_colours: RGB = RGB[RGB[:, 1].argsort()] scatter_settings["c"] = np.clip( np.reshape( RGB_to_RGB( RGB, colourspace, CONSTANTS_COLOUR_STYLE.colour.colourspace, apply_cctf_encoding=apply_cctf_encoding, ), (-1, 3), ), 0, 1, ) XYZ = RGB_to_XYZ(RGB, colourspace) XYZ_to_ij = METHODS_CHROMATICITY_DIAGRAM[method]["XYZ_to_ij"] ij = XYZ_to_ij(XYZ, colourspace.whitepoint) axes.scatter(ij[..., 0], ij[..., 1], **scatter_settings) settings.update({"show": True}) settings.update(kwargs) return render(**settings)
[docs] @override_style() def plot_RGB_chromaticities_in_chromaticity_diagram_CIE1931( RGB: ArrayLike, colourspace: ( RGB_Colourspace | str | Sequence[RGB_Colourspace | LiteralRGBColourspace | str] ) = "sRGB", chromaticity_diagram_callable_CIE1931: Callable = ( plot_RGB_colourspaces_in_chromaticity_diagram_CIE1931 ), scatter_kwargs: dict | None = None, **kwargs: Any, ) -> Tuple[Figure, Axes]: """ Plot given *RGB* colourspace array in the *CIE 1931 Chromaticity Diagram*. Parameters ---------- RGB *RGB* colourspace array. colourspace *RGB* colourspace of the *RGB* array. ``colourspace`` can be of any type or form supported by the :func:`colour.plotting.common.filter_RGB_colourspaces` definition. chromaticity_diagram_callable_CIE1931 Callable responsible for drawing the *CIE 1931 Chromaticity Diagram*. scatter_kwargs Keyword arguments for the :func:`matplotlib.pyplot.scatter` definition. The following special keyword arguments can also be used: - ``c`` : If ``c`` is set to *RGB*, the scatter will use the colours as given by the ``RGB`` argument. - ``apply_cctf_encoding`` : If ``apply_cctf_encoding`` is set to *False*, the encoding colour component transfer function / opto-electronic transfer function is not applied when encoding the samples to the plotting space. Other Parameters ---------------- kwargs {:func:`colour.plotting.artist`, :func:`colour.plotting.diagrams.plot_chromaticity_diagram`, :func:`colour.plotting.models.\ plot_RGB_colourspaces_in_chromaticity_diagram`, :func:`colour.plotting.render`}, See the documentation of the previously listed definitions. Returns ------- :class:`tuple` Current figure and axes. Examples -------- >>> RGB = np.random.random((128, 128, 3)) >>> plot_RGB_chromaticities_in_chromaticity_diagram_CIE1931( ... RGB, "ITU-R BT.709" ... ) ... # doctest: +ELLIPSIS (<Figure size ... with 1 Axes>, <...Axes...>) .. image:: ../_static/Plotting_\ Plot_RGB_Chromaticities_In_Chromaticity_Diagram_CIE1931.png :align: center :alt: plot_RGB_chromaticities_in_chromaticity_diagram_CIE1931 """ settings = dict(kwargs) settings.update({"method": "CIE 1931"}) return plot_RGB_chromaticities_in_chromaticity_diagram( RGB, colourspace, chromaticity_diagram_callable_CIE1931, scatter_kwargs=scatter_kwargs, **settings, )
[docs] @override_style() def plot_RGB_chromaticities_in_chromaticity_diagram_CIE1960UCS( RGB: ArrayLike, colourspace: ( RGB_Colourspace | str | Sequence[RGB_Colourspace | LiteralRGBColourspace | str] ) = "sRGB", chromaticity_diagram_callable_CIE1960UCS: Callable = ( plot_RGB_colourspaces_in_chromaticity_diagram_CIE1960UCS ), scatter_kwargs: dict | None = None, **kwargs: Any, ) -> Tuple[Figure, Axes]: """ Plot given *RGB* colourspace array in the *CIE 1960 UCS Chromaticity Diagram*. Parameters ---------- RGB *RGB* colourspace array. colourspace *RGB* colourspace of the *RGB* array. ``colourspace`` can be of any type or form supported by the :func:`colour.plotting.common.filter_RGB_colourspaces` definition. chromaticity_diagram_callable_CIE1960UCS Callable responsible for drawing the *CIE 1960 UCS Chromaticity Diagram*. scatter_kwargs Keyword arguments for the :func:`matplotlib.pyplot.scatter` definition. The following special keyword arguments can also be used: - ``c`` : If ``c`` is set to *RGB*, the scatter will use the colours as given by the ``RGB`` argument. - ``apply_cctf_encoding`` : If ``apply_cctf_encoding`` is set to *False*, the encoding colour component transfer function / opto-electronic transfer function is not applied when encoding the samples to the plotting space. Other Parameters ---------------- kwargs {:func:`colour.plotting.artist`, :func:`colour.plotting.diagrams.plot_chromaticity_diagram`, :func:`colour.plotting.models.\ plot_RGB_colourspaces_in_chromaticity_diagram`, :func:`colour.plotting.render`}, See the documentation of the previously listed definitions. Returns ------- :class:`tuple` Current figure and axes. Examples -------- >>> RGB = np.random.random((128, 128, 3)) >>> plot_RGB_chromaticities_in_chromaticity_diagram_CIE1960UCS( ... RGB, "ITU-R BT.709" ... ) ... # doctest: +ELLIPSIS (<Figure size ... with 1 Axes>, <...Axes...>) .. image:: ../_static/Plotting_\ Plot_RGB_Chromaticities_In_Chromaticity_Diagram_CIE1960UCS.png :align: center :alt: plot_RGB_chromaticities_in_chromaticity_diagram_CIE1960UCS """ settings = dict(kwargs) settings.update({"method": "CIE 1960 UCS"}) return plot_RGB_chromaticities_in_chromaticity_diagram( RGB, colourspace, chromaticity_diagram_callable_CIE1960UCS, scatter_kwargs=scatter_kwargs, **settings, )
[docs] @override_style() def plot_RGB_chromaticities_in_chromaticity_diagram_CIE1976UCS( RGB: ArrayLike, colourspace: ( RGB_Colourspace | str | Sequence[RGB_Colourspace | LiteralRGBColourspace | str] ) = "sRGB", chromaticity_diagram_callable_CIE1976UCS: Callable = ( plot_RGB_colourspaces_in_chromaticity_diagram_CIE1976UCS ), scatter_kwargs: dict | None = None, **kwargs: Any, ) -> Tuple[Figure, Axes]: """ Plot given *RGB* colourspace array in the *CIE 1976 UCS Chromaticity Diagram*. Parameters ---------- RGB *RGB* colourspace array. colourspace *RGB* colourspace of the *RGB* array. ``colourspace`` can be of any type or form supported by the :func:`colour.plotting.common.filter_RGB_colourspaces` definition. chromaticity_diagram_callable_CIE1976UCS Callable responsible for drawing the *CIE 1976 UCS Chromaticity Diagram*. scatter_kwargs Keyword arguments for the :func:`matplotlib.pyplot.scatter` definition. The following special keyword arguments can also be used: - ``c`` : If ``c`` is set to *RGB*, the scatter will use the colours as given by the ``RGB`` argument. - ``apply_cctf_encoding`` : If ``apply_cctf_encoding`` is set to *False*, the encoding colour component transfer function / opto-electronic transfer function is not applied when encoding the samples to the plotting space. Other Parameters ---------------- kwargs {:func:`colour.plotting.artist`, :func:`colour.plotting.diagrams.plot_chromaticity_diagram`, :func:`colour.plotting.models.\ plot_RGB_colourspaces_in_chromaticity_diagram`, :func:`colour.plotting.render`}, See the documentation of the previously listed definitions. Returns ------- :class:`tuple` Current figure and axes. Examples -------- >>> RGB = np.random.random((128, 128, 3)) >>> plot_RGB_chromaticities_in_chromaticity_diagram_CIE1976UCS( ... RGB, "ITU-R BT.709" ... ) ... # doctest: +ELLIPSIS (<Figure size ... with 1 Axes>, <...Axes...>) .. image:: ../_static/Plotting_\ Plot_RGB_Chromaticities_In_Chromaticity_Diagram_CIE1976UCS.png :align: center :alt: plot_RGB_chromaticities_in_chromaticity_diagram_CIE1976UCS """ settings = dict(kwargs) settings.update({"method": "CIE 1976 UCS"}) return plot_RGB_chromaticities_in_chromaticity_diagram( RGB, colourspace, chromaticity_diagram_callable_CIE1976UCS, scatter_kwargs=scatter_kwargs, **settings, )
def ellipses_MacAdam1942( method: (Literal["CIE 1931", "CIE 1960 UCS", "CIE 1976 UCS"] | str) = "CIE 1931", ) -> List[NDArrayFloat]: """ Return *MacAdam (1942) Ellipses (Observer PGN)* coefficients according to given method. Parameters ---------- method Computation method. Returns ------- :class:`list` *MacAdam (1942) Ellipses (Observer PGN)* coefficients. Examples -------- >>> ellipses_MacAdam1942()[0] # doctest: +SKIP array([ 1.60000000e-01, 5.70000000e-02, 5.00000023e-03, 1.56666660e-02, -2.77000015e+01]) """ method = validate_method(method, ("CIE 1931", "CIE 1960 UCS", "CIE 1976 UCS")) xy_to_ij = METHODS_CHROMATICITY_DIAGRAM[method]["xy_to_ij"] x, y, _a, _b, _theta, a, b, theta = tsplit(DATA_MACADAM_1942_ELLIPSES) ellipses_coefficients = [] for i in range(len(theta)): xy = point_at_angle_on_ellipse( np.linspace(0, 360, 36), [x[i], y[i], a[i] / 60, b[i] / 60, theta[i]], ) ij = xy_to_ij(xy) ellipses_coefficients.append( ellipse_coefficients_canonical_form(ellipse_fitting(ij)) ) return ellipses_coefficients
[docs] @override_style() def plot_ellipses_MacAdam1942_in_chromaticity_diagram( chromaticity_diagram_callable: Callable = plot_chromaticity_diagram, method: (Literal["CIE 1931", "CIE 1960 UCS", "CIE 1976 UCS"] | str) = "CIE 1931", chromaticity_diagram_clipping: bool = False, ellipse_kwargs: dict | List[dict] | None = None, **kwargs: Any, ) -> Tuple[Figure, Axes]: """ Plot *MacAdam (1942) Ellipses (Observer PGN)* in the *Chromaticity Diagram* according to given method. Parameters ---------- chromaticity_diagram_callable Callable responsible for drawing the *Chromaticity Diagram*. method *Chromaticity Diagram* method. chromaticity_diagram_clipping Whether to clip the *Chromaticity Diagram* colours with the ellipses. ellipse_kwargs Parameters for the :class:`Ellipse` class, ``ellipse_kwargs`` can be either a single dictionary applied to all the ellipses with same settings or a sequence of dictionaries with different settings for each ellipse. Other Parameters ---------------- kwargs {:func:`colour.plotting.artist`, :func:`colour.plotting.diagrams.plot_chromaticity_diagram`, :func:`colour.plotting.render`}, See the documentation of the previously listed definitions. Returns ------- :class:`tuple` Current figure and axes. Examples -------- >>> plot_ellipses_MacAdam1942_in_chromaticity_diagram() ... # doctest: +ELLIPSIS (<Figure size ... with 1 Axes>, <...Axes...>) .. image:: ../_static/\ Plotting_Plot_Ellipses_MacAdam1942_In_Chromaticity_Diagram.png :align: center :alt: plot_ellipses_MacAdam1942_in_chromaticity_diagram """ settings: Dict[str, Any] = {"uniform": True} settings.update(kwargs) _figure, axes = artist(**settings) settings = dict(kwargs) settings.update({"axes": axes, "show": False}) ellipses_coefficients = ellipses_MacAdam1942(method=method) if chromaticity_diagram_clipping: diagram_clipping_path_x = [] diagram_clipping_path_y = [] for coefficients in ellipses_coefficients: coefficients = np.copy(coefficients) # noqa: PLW2901 coefficients[2:4] /= 2 x, y = tsplit( point_at_angle_on_ellipse( np.linspace(0, 360, 36), coefficients, ) ) diagram_clipping_path_x.append(x) diagram_clipping_path_y.append(y) diagram_clipping_path = np.rollaxis( np.array([diagram_clipping_path_x, diagram_clipping_path_y]), 0, 3 ) diagram_clipping_path = Path.make_compound_path_from_polys( diagram_clipping_path ).vertices settings.update({"diagram_clipping_path": diagram_clipping_path}) chromaticity_diagram_callable(**settings) ellipse_settings_collection = [ { "color": CONSTANTS_COLOUR_STYLE.colour.cycle[4], "alpha": 0.4, "linewidth": colour_style()["lines.linewidth"], "zorder": CONSTANTS_COLOUR_STYLE.zorder.midground_polygon, } for _ellipses_coefficient in ellipses_coefficients ] if ellipse_kwargs is not None: update_settings_collection( ellipse_settings_collection, ellipse_kwargs, len(ellipses_coefficients), ) for i, coefficients in enumerate(ellipses_coefficients): x_c, y_c, a_a, a_b, theta_e = coefficients ellipse = Ellipse( (x_c, y_c), a_a, a_b, angle=theta_e, **ellipse_settings_collection[i], ) axes.add_artist(ellipse) settings.update({"show": True}) settings.update(kwargs) return render(**settings)
[docs] @override_style() def plot_ellipses_MacAdam1942_in_chromaticity_diagram_CIE1931( chromaticity_diagram_callable_CIE1931: Callable = ( plot_chromaticity_diagram_CIE1931 ), chromaticity_diagram_clipping: bool = False, ellipse_kwargs: dict | List[dict] | None = None, **kwargs: Any, ) -> Tuple[Figure, Axes]: """ Plot *MacAdam (1942) Ellipses (Observer PGN)* in the *CIE 1931 Chromaticity Diagram*. Parameters ---------- chromaticity_diagram_callable_CIE1931 Callable responsible for drawing the *CIE 1931 Chromaticity Diagram*. chromaticity_diagram_clipping Whether to clip the *CIE 1931 Chromaticity Diagram* colours with the ellipses. ellipse_kwargs Parameters for the :class:`Ellipse` class, ``ellipse_kwargs`` can be either a single dictionary applied to all the ellipses with same settings or a sequence of dictionaries with different settings for each ellipse. Other Parameters ---------------- kwargs {:func:`colour.plotting.artist`, :func:`colour.plotting.diagrams.plot_chromaticity_diagram`, :func:`colour.plotting.models.\ plot_ellipses_MacAdam1942_in_chromaticity_diagram`}, :func:`colour.plotting.render`}, See the documentation of the previously listed definitions. Returns ------- :class:`tuple` Current figure and axes. Examples -------- >>> plot_ellipses_MacAdam1942_in_chromaticity_diagram_CIE1931() ... # doctest: +ELLIPSIS (<Figure size ... with 1 Axes>, <...Axes...>) .. image:: ../_static/\ Plotting_Plot_Ellipses_MacAdam1942_In_Chromaticity_Diagram_CIE1931.png :align: center :alt: plot_ellipses_MacAdam1942_in_chromaticity_diagram_CIE1931 """ settings = dict(kwargs) settings.update({"method": "CIE 1931"}) return plot_ellipses_MacAdam1942_in_chromaticity_diagram( chromaticity_diagram_callable_CIE1931, chromaticity_diagram_clipping=chromaticity_diagram_clipping, ellipse_kwargs=ellipse_kwargs, **settings, )
[docs] @override_style() def plot_ellipses_MacAdam1942_in_chromaticity_diagram_CIE1960UCS( chromaticity_diagram_callable_CIE1960UCS: Callable = ( plot_chromaticity_diagram_CIE1960UCS ), chromaticity_diagram_clipping: bool = False, ellipse_kwargs: dict | List[dict] | None = None, **kwargs: Any, ) -> Tuple[Figure, Axes]: """ Plot *MacAdam (1942) Ellipses (Observer PGN)* in the *CIE 1960 UCS Chromaticity Diagram*. Parameters ---------- chromaticity_diagram_callable_CIE1960UCS Callable responsible for drawing the *CIE 1960 UCS Chromaticity Diagram*. chromaticity_diagram_clipping Whether to clip the *CIE 1960 UCS Chromaticity Diagram* colours with the ellipses. ellipse_kwargs Parameters for the :class:`Ellipse` class, ``ellipse_kwargs`` can be either a single dictionary applied to all the ellipses with same settings or a sequence of dictionaries with different settings for each ellipse. Other Parameters ---------------- kwargs {:func:`colour.plotting.artist`, :func:`colour.plotting.diagrams.plot_chromaticity_diagram`, :func:`colour.plotting.models.\ plot_ellipses_MacAdam1942_in_chromaticity_diagram`}, :func:`colour.plotting.render`}, See the documentation of the previously listed definitions. Returns ------- :class:`tuple` Current figure and axes. Examples -------- >>> plot_ellipses_MacAdam1942_in_chromaticity_diagram_CIE1960UCS() ... # doctest: +ELLIPSIS (<Figure size ... with 1 Axes>, <...Axes...>) .. image:: ../_static/\ Plotting_Plot_Ellipses_MacAdam1942_In_Chromaticity_Diagram_CIE1960UCS.png :align: center :alt: plot_ellipses_MacAdam1942_in_chromaticity_diagram_CIE1960UCS """ settings = dict(kwargs) settings.update({"method": "CIE 1960 UCS"}) return plot_ellipses_MacAdam1942_in_chromaticity_diagram( chromaticity_diagram_callable_CIE1960UCS, chromaticity_diagram_clipping=chromaticity_diagram_clipping, ellipse_kwargs=ellipse_kwargs, **settings, )
[docs] @override_style() def plot_ellipses_MacAdam1942_in_chromaticity_diagram_CIE1976UCS( chromaticity_diagram_callable_CIE1976UCS: Callable = ( plot_chromaticity_diagram_CIE1976UCS ), chromaticity_diagram_clipping: bool = False, ellipse_kwargs: dict | List[dict] | None = None, **kwargs: Any, ) -> Tuple[Figure, Axes]: """ Plot *MacAdam (1942) Ellipses (Observer PGN)* in the *CIE 1976 UCS Chromaticity Diagram*. Parameters ---------- chromaticity_diagram_callable_CIE1976UCS Callable responsible for drawing the *CIE 1976 UCS Chromaticity Diagram*. chromaticity_diagram_clipping Whether to clip the *CIE 1976 UCS Chromaticity Diagram* colours with the ellipses. ellipse_kwargs Parameters for the :class:`Ellipse` class, ``ellipse_kwargs`` can be either a single dictionary applied to all the ellipses with same settings or a sequence of dictionaries with different settings for each ellipse. Other Parameters ---------------- kwargs {:func:`colour.plotting.artist`, :func:`colour.plotting.diagrams.plot_chromaticity_diagram`, :func:`colour.plotting.models.\ plot_ellipses_MacAdam1942_in_chromaticity_diagram`}, :func:`colour.plotting.render`}, See the documentation of the previously listed definitions. Returns ------- :class:`tuple` Current figure and axes. Examples -------- >>> plot_ellipses_MacAdam1942_in_chromaticity_diagram_CIE1976UCS() ... # doctest: +ELLIPSIS (<Figure size ... with 1 Axes>, <...Axes...>) .. image:: ../_static/\ Plotting_Plot_Ellipses_MacAdam1942_In_Chromaticity_Diagram_CIE1976UCS.png :align: center :alt: plot_ellipses_MacAdam1942_in_chromaticity_diagram_CIE1976UCS """ settings = dict(kwargs) settings.update({"method": "CIE 1976 UCS"}) return plot_ellipses_MacAdam1942_in_chromaticity_diagram( chromaticity_diagram_callable_CIE1976UCS, chromaticity_diagram_clipping=chromaticity_diagram_clipping, ellipse_kwargs=ellipse_kwargs, **settings, )
[docs] @override_style() def plot_single_cctf( cctf: Callable | str, cctf_decoding: bool = False, **kwargs: Any ) -> Tuple[Figure, Axes]: """ Plot given colourspace colour component transfer function. Parameters ---------- cctf Colour component transfer function to plot. ``function`` can be of any type or form supported by the :func:`colour.plotting.common.filter_passthrough` definition. cctf_decoding Plot the decoding colour component transfer function instead. Other Parameters ---------------- kwargs {:func:`colour.plotting.artist`, :func:`colour.plotting.plot_multi_functions`, :func:`colour.plotting.render`}, See the documentation of the previously listed definitions. Returns ------- :class:`tuple` Current figure and axes. Examples -------- >>> plot_single_cctf("ITU-R BT.709") # doctest: +ELLIPSIS (<Figure size ... with 1 Axes>, <...Axes...>) .. image:: ../_static/Plotting_Plot_Single_CCTF.png :align: center :alt: plot_single_cctf """ settings: Dict[str, Any] = { "title": f"{cctf} - {'Decoding' if cctf_decoding else 'Encoding'} CCTF" } settings.update(kwargs) return plot_multi_cctfs([cctf], cctf_decoding, **settings)
[docs] @override_style() def plot_multi_cctfs( cctfs: Callable | str | Sequence[Callable | str], cctf_decoding: bool = False, **kwargs: Any, ) -> Tuple[Figure, Axes]: """ Plot given colour component transfer functions. Parameters ---------- cctfs Colour component transfer function to plot. ``cctfs`` elements can be of any type or form supported by the :func:`colour.plotting.common.filter_passthrough` definition. cctf_decoding Plot the decoding colour component transfer function instead. Other Parameters ---------------- kwargs {:func:`colour.plotting.artist`, :func:`colour.plotting.plot_multi_functions`, :func:`colour.plotting.render`}, See the documentation of the previously listed definitions. Returns ------- :class:`tuple` Current figure and axes. Examples -------- >>> plot_multi_cctfs(["ITU-R BT.709", "sRGB"]) # doctest: +ELLIPSIS (<Figure size ... with 1 Axes>, <...Axes...>) .. image:: ../_static/Plotting_Plot_Multi_CCTFs.png :align: center :alt: plot_multi_cctfs """ cctfs_filtered = filter_passthrough( CCTF_DECODINGS if cctf_decoding else CCTF_ENCODINGS, cctfs ) mode = "Decoding" if cctf_decoding else "Encoding" title = f"{', '.join(list(cctfs_filtered))} - {mode} CCTFs" settings: Dict[str, Any] = { "bounding_box": (0, 1, 0, 1), "legend": True, "title": title, "x_label": "Signal Value" if cctf_decoding else "Tristimulus Value", "y_label": "Tristimulus Value" if cctf_decoding else "Signal Value", } settings.update(kwargs) with domain_range_scale("1"): return plot_multi_functions(cctfs_filtered, **settings)
[docs] @override_style() def plot_constant_hue_loci( data: ArrayLike, model: LiteralColourspaceModel | str = "CIE Lab", scatter_kwargs: dict | None = None, convert_kwargs: dict | None = None, **kwargs: Any, ) -> Tuple[Figure, Axes]: """ Plot given constant hue loci colour matches data such as that from :cite:`Hung1995` or :cite:`Ebner1998` that are easily loaded with `Colour - Datasets <https://github.com/colour-science/colour-datasets>`__. Parameters ---------- data Constant hue loci colour matches data expected to be an `ArrayLike` as follows:: [ ('name', XYZ_r, XYZ_cr, (XYZ_ct, XYZ_ct, XYZ_ct, ...), \ {metadata}), ('name', XYZ_r, XYZ_cr, (XYZ_ct, XYZ_ct, XYZ_ct, ...), \ {metadata}), ('name', XYZ_r, XYZ_cr, (XYZ_ct, XYZ_ct, XYZ_ct, ...), \ {metadata}), ... ] where ``name`` is the hue angle or name, ``XYZ_r`` the *CIE XYZ* tristimulus values of the reference illuminant, ``XYZ_cr`` the *CIE XYZ* tristimulus values of the reference colour under the reference illuminant, ``XYZ_ct`` the *CIE XYZ* tristimulus values of the colour matches under the reference illuminant and ``metadata`` the dataset metadata. model Colourspace model, see :attr:`colour.COLOURSPACE_MODELS` attribute for the list of supported colourspace models. scatter_kwargs Keyword arguments for the :func:`matplotlib.pyplot.scatter` definition. The following special keyword arguments can also be used: - ``c`` : If ``c`` is set to *RGB*, the scatter will use the colours as given by the ``RGB`` argument. convert_kwargs Keyword arguments for the :func:`colour.convert` definition. Other Parameters ---------------- kwargs {:func:`colour.plotting.artist`, :func:`colour.plotting.plot_multi_functions`, :func:`colour.plotting.render`}, See the documentation of the previously listed definitions. Returns ------- :class:`tuple` Current figure and axes. References ---------- :cite:`Ebner1998`, :cite:`Hung1995`, :cite:`Mansencal2019` Examples -------- >>> data = [ ... [ ... None, ... np.array([0.95010000, 1.00000000, 1.08810000]), ... np.array([0.40920000, 0.28120000, 0.30600000]), ... np.array( ... [ ... [0.02495100, 0.01908600, 0.02032900], ... [0.10944300, 0.06235900, 0.06788100], ... [0.27186500, 0.18418700, 0.19565300], ... [0.48898900, 0.40749400, 0.44854600], ... ] ... ), ... None, ... ], ... [ ... None, ... np.array([0.95010000, 1.00000000, 1.08810000]), ... np.array([0.30760000, 0.48280000, 0.42770000]), ... np.array( ... [ ... [0.02108000, 0.02989100, 0.02790400], ... [0.06194700, 0.11251000, 0.09334400], ... [0.15255800, 0.28123300, 0.23234900], ... [0.34157700, 0.56681300, 0.47035300], ... ] ... ), ... None, ... ], ... [ ... None, ... np.array([0.95010000, 1.00000000, 1.08810000]), ... np.array([0.39530000, 0.28120000, 0.18450000]), ... np.array( ... [ ... [0.02436400, 0.01908600, 0.01468800], ... [0.10331200, 0.06235900, 0.02854600], ... [0.26311900, 0.18418700, 0.12109700], ... [0.43158700, 0.40749400, 0.39008600], ... ] ... ), ... None, ... ], ... [ ... None, ... np.array([0.95010000, 1.00000000, 1.08810000]), ... np.array([0.20510000, 0.18420000, 0.57130000]), ... np.array( ... [ ... [0.03039800, 0.02989100, 0.06123300], ... [0.08870000, 0.08498400, 0.21843500], ... [0.18405800, 0.18418700, 0.40111400], ... [0.32550100, 0.34047200, 0.50296900], ... [0.53826100, 0.56681300, 0.80010400], ... ] ... ), ... None, ... ], ... [ ... None, ... np.array([0.95010000, 1.00000000, 1.08810000]), ... np.array([0.35770000, 0.28120000, 0.11250000]), ... np.array( ... [ ... [0.03678100, 0.02989100, 0.01481100], ... [0.17127700, 0.11251000, 0.01229900], ... [0.30080900, 0.28123300, 0.21229800], ... [0.52976000, 0.40749400, 0.11720000], ... ] ... ), ... None, ... ], ... ] >>> plot_constant_hue_loci(data, "CIE Lab") # doctest: +ELLIPSIS (<Figure size ... with 1 Axes>, <...Axes...>) .. image:: ../_static/Plotting_Plot_Constant_Hue_Loci.png :align: center :alt: plot_constant_hue_loci """ # TODO: Filter appropriate colour models. # NOTE: "dtype=object" is required for ragged array support # in "Numpy" 1.24.0. data = as_array(data, dtype=object) # pyright: ignore settings: Dict[str, Any] = {"uniform": True} settings.update(kwargs) _figure, axes = artist(**settings) scatter_settings = { "s": 40, "c": "RGB", "marker": "o", "alpha": 0.85, "zorder": CONSTANTS_COLOUR_STYLE.zorder.foreground_scatter, } if scatter_kwargs is not None: scatter_settings.update(scatter_kwargs) convert_kwargs = optional(convert_kwargs, {}) use_RGB_colours = str(scatter_settings["c"]).upper() == "RGB" colourspace = CONSTANTS_COLOUR_STYLE.colour.colourspace for hue_data in data: _name, XYZ_r, XYZ_cr, XYZ_ct, _metadata = hue_data xy_r = XYZ_to_xy(XYZ_r) convert_settings = {"illuminant": xy_r} convert_settings.update(convert_kwargs) ijk_ct = colourspace_model_axis_reorder( convert(XYZ_ct, "CIE XYZ", model, **convert_settings), model ) ijk_cr = colourspace_model_axis_reorder( convert(XYZ_cr, "CIE XYZ", model, **convert_settings), model ) ijk_ct *= COLOURSPACE_MODELS_DOMAIN_RANGE_SCALE_1_TO_REFERENCE[model] ijk_cr *= COLOURSPACE_MODELS_DOMAIN_RANGE_SCALE_1_TO_REFERENCE[model] def _linear_equation( x: NDArrayFloat, a: NDArrayFloat, b: NDArrayFloat ) -> NDArrayFloat: """Define the canonical linear equation for a line.""" return a * x + b popt, _pcov = scipy.optimize.curve_fit( _linear_equation, ijk_ct[..., 0], ijk_ct[..., 1] ) axes.plot( ijk_ct[..., 0], _linear_equation(ijk_ct[..., 0], *popt), c=CONSTANTS_COLOUR_STYLE.colour.average, zorder=CONSTANTS_COLOUR_STYLE.zorder.midground_line, ) if use_RGB_colours: RGB_ct = XYZ_to_RGB(XYZ_ct, colourspace, xy_r, apply_cctf_encoding=True) scatter_settings["c"] = np.clip(RGB_ct, 0, 1) RGB_cr = XYZ_to_RGB(XYZ_cr, colourspace, xy_r, apply_cctf_encoding=True) RGB_cr = np.clip(np.ravel(RGB_cr), 0, 1) else: RGB_cr = scatter_settings["c"] axes.scatter(ijk_ct[..., 0], ijk_ct[..., 1], **scatter_settings) axes.plot( ijk_cr[..., 0], ijk_cr[..., 1], "s", c=RGB_cr, markersize=CONSTANTS_COLOUR_STYLE.geometry.short * 8, zorder=CONSTANTS_COLOUR_STYLE.zorder.midground_line, ) labels = np.array(COLOURSPACE_MODELS_AXIS_LABELS[model])[ as_int_array(colourspace_model_axis_reorder([0, 1, 2], model)) ] settings = { "axes": axes, "title": f"Constant Hue Loci - {model}", "x_label": labels[0], "y_label": labels[1], } settings.update(kwargs) return render(**settings)