Beispiel #1
0
    def test_suppress_warnings(self):
        """
        Tests :func:`colour.utilities.verbose.suppress_warnings` definition.
        """

        with suppress_warnings():
            warning('This is a suppressed unit test warning!')
Beispiel #2
0
def exponent_function_monitor_curve(
    x: FloatingOrArrayLike,
    exponent: FloatingOrArrayLike = 1,
    offset: FloatingOrArrayLike = 0,
    style: Union[Literal["monCurveFwd", "monCurveRev", "monCurveMirrorFwd",
                         "monCurveMirrorRev", ], str, ] = "monCurveFwd",
) -> FloatingOrNDArray:
    """
    Define the *Monitor Curve* exponent transfer function.

    Parameters
    ----------
    x
        Data to undergo the monitor curve exponential conversion.
    exponent
        Exponent value used for the conversion.
    offset
        Offset value used for the conversion.
    style
        Defines the behaviour for the transfer function to operate:

        -   *monCurveFwd*: *Monitor Curve Forward* exponential behaviour
            where the definition applies a power law function with a linear
            segment near the origin.
        -   *monCurveRev*: *Monitor Curve Reverse* exponential behaviour
            where the definition applies a power law function with a linear
            segment near the origin.
        -   *monCurveMirrorFwd*: *Monitor Curve Mirror Forward* exponential
            behaviour where the definition applies a power law function with a
            linear segment near the origin and mirrors the function for values
            less than zero (i.e. rotationally symmetric around the origin).
        -   *monCurveMirrorRev*: *Monitor Curve Mirror Reverse* exponential
            behaviour where the definition applies a power law function with a
            linear segment near the origin and mirrors the function for values
            less than zero (i.e. rotationally symmetric around the origin).

    Returns
    -------
    :class:`numpy.floating` or :class:`numpy.ndarray`
        Exponentially converted data.

    Examples
    --------
    >>> exponent_function_monitor_curve(  # doctest: +ELLIPSIS
    ...     0.18, 2.2, 0.001)
    0.0232240...
    >>> exponent_function_monitor_curve(  # doctest: +ELLIPSIS
    ...     -0.18, 2.2, 0.001)
    -0.0002054...
    >>> exponent_function_monitor_curve(  # doctest: +ELLIPSIS
    ...     0.18, 2.2, 0.001, 'monCurveRev')
    0.4581151...
    >>> exponent_function_monitor_curve(  # doctest: +ELLIPSIS
    ...     -0.18, 2.2, 0.001, 'monCurveRev')
    -157.7302795...
    >>> exponent_function_monitor_curve(  # doctest: +ELLIPSIS
    ...     0.18, 2.2, 2, 'monCurveMirrorFwd')
    0.1679399...
    >>> exponent_function_monitor_curve(  # doctest: +ELLIPSIS
    ...     -0.18, 2.2, 0.001, 'monCurveMirrorFwd')
    -0.0232240...
    >>> exponent_function_monitor_curve(  # doctest: +ELLIPSIS
    ...     0.18, 2.2, 0.001, 'monCurveMirrorRev')
    0.4581151...
    >>> exponent_function_monitor_curve(  # doctest: +ELLIPSIS
    ...     -0.18, 2.2, 0.001, 'monCurveMirrorRev')
    -0.4581151...
    """

    x = as_float_array(x)
    exponent = as_float_array(exponent)
    offset = as_float_array(offset)
    style = validate_method(
        style,
        [
            "monCurveFwd",
            "monCurveRev",
            "monCurveMirrorFwd",
            "monCurveMirrorRev",
        ],
        '"{0}" style is invalid, it must be one of {1}!',
    )

    with suppress_warnings(python_warnings=True):
        s = as_float_array(
            ((exponent - 1) / offset) *
            ((exponent * offset) / ((exponent - 1) * (offset + 1)))**exponent)

        s[np.isnan(s)] = 1

    def monitor_curve_forward(x: NDArray, offset: NDArray,
                              exponent: NDArray) -> NDArray:
        """Define the *Monitor Curve Forward* function."""

        x_break = offset / (exponent - 1)

        return np.where(
            x >= x_break,
            ((x + offset) / (1 + offset))**exponent,
            x * s,
        )

    def monitor_curve_reverse(y: NDArray, offset: NDArray,
                              exponent: NDArray) -> NDArray:
        """Define the *Monitor Curve Reverse* function."""
        y_break = ((exponent * offset) / ((exponent - 1) *
                                          (1 + offset)))**exponent

        return np.where(
            y >= y_break,
            ((1 + offset) * (y**(1 / exponent))) - offset,
            y / s,
        )

    if style == "moncurvefwd":
        return as_float(monitor_curve_forward(x, offset, exponent))
    elif style == "moncurverev":
        return as_float(monitor_curve_reverse(x, offset, exponent))
    elif style == "moncurvemirrorfwd":
        return as_float(
            np.where(
                x >= 0,
                monitor_curve_forward(x, offset, exponent),
                -monitor_curve_forward(-x, offset, exponent),
            ))
    else:  # style == 'moncurvemirrorrev'
        return as_float(
            np.where(
                x >= 0,
                monitor_curve_reverse(x, offset, exponent),
                -monitor_curve_reverse(-x, offset, exponent),
            ))
Beispiel #3
0
def plot_hull_section_colours(
    hull: trimesh.Trimesh,  # type: ignore[name-defined]  # noqa
    model: Union[Literal["CAM02LCD", "CAM02SCD", "CAM02UCS", "CAM16LCD",
                         "CAM16SCD", "CAM16UCS", "CIE XYZ", "CIE xyY",
                         "CIE Lab", "CIE Luv", "CIE UCS", "CIE UVW", "DIN99",
                         "Hunter Lab", "Hunter Rdab", "ICaCb", "ICtCp", "IPT",
                         "IgPgTg", "Jzazbz", "OSA UCS", "Oklab", "hdr-CIELAB",
                         "hdr-IPT", ], str, ] = "CIE xyY",
    axis: Union[Literal["+z", "+x", "+y"], str] = "+z",
    origin: Floating = 0.5,
    normalise: Boolean = True,
    section_colours: Optional[Union[ArrayLike, str]] = None,
    section_opacity: Floating = 1,
    convert_kwargs: Optional[Dict] = None,
    samples: Integer = 256,
    **kwargs: Any,
) -> Tuple[plt.Figure, plt.Axes]:
    """
    Plot the section colours of given *trimesh* hull along given axis and
    origin.

    Parameters
    ----------
    hull
        *Trimesh* hull.
    model
        Colourspace model, see :attr:`colour.COLOURSPACE_MODELS` attribute for
        the list of supported colourspace models.
    axis
        Axis the hull section will be normal to.
    origin
        Coordinate along ``axis`` at which to plot the hull section.
    normalise
        Whether to normalise ``axis`` to the extent of the hull along it.
    section_colours
        Colours of the hull section, if ``section_colours`` is set to *RGB*,
        the colours will be computed according to the corresponding
        coordinates.
    section_opacity
        Opacity of the hull section colours.
    convert_kwargs
        Keyword arguments for the :func:`colour.convert` definition.
    samples
        Samples count on one axis when computing the hull section colours.

    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
    --------
    >>> from colour.models import RGB_COLOURSPACE_sRGB
    >>> from colour.utilities import is_trimesh_installed
    >>> vertices, faces, _outline = primitive_cube(1, 1, 1, 64, 64, 64)
    >>> XYZ_vertices = RGB_to_XYZ(
    ...     vertices['position'] + 0.5,
    ...     RGB_COLOURSPACE_sRGB.whitepoint,
    ...     RGB_COLOURSPACE_sRGB.whitepoint,
    ...     RGB_COLOURSPACE_sRGB.matrix_RGB_to_XYZ,
    ... )
    >>> if is_trimesh_installed:
    ...     import trimesh
    ...     hull = trimesh.Trimesh(XYZ_vertices, faces, process=False)
    ...     plot_hull_section_colours(hull, section_colours='RGB')
    ...     # doctest: +ELLIPSIS
    (<Figure size ... with 1 Axes>, <...AxesSubplot...>)

    .. image:: ../_static/Plotting_Plot_Hull_Section_Colours.png
        :align: center
        :alt: plot_hull_section_colours
    """

    axis = validate_method(
        axis,
        ["+z", "+x", "+y"],
        '"{0}" axis is invalid, it must be one of {1}!',
    )

    hull = hull.copy()

    settings: Dict[str, Any] = {"uniform": True}
    settings.update(kwargs)

    _figure, axes = artist(**settings)

    section_colours = cast(
        ArrayLike,
        optional(section_colours,
                 HEX_to_RGB(CONSTANTS_COLOUR_STYLE.colour.average)),
    )

    convert_kwargs = optional(convert_kwargs, {})

    # Luminance / Lightness reordered along "z" axis.
    with suppress_warnings(python_warnings=True):
        ijk_vertices = colourspace_model_axis_reorder(
            convert(hull.vertices, "CIE XYZ", model, **convert_kwargs), model)
        ijk_vertices = np.nan_to_num(ijk_vertices)
        ijk_vertices *= COLOURSPACE_MODELS_DOMAIN_RANGE_SCALE_1_TO_REFERENCE[
            model]

    hull.vertices = ijk_vertices

    if axis == "+x":
        index_origin = 0
    elif axis == "+y":
        index_origin = 1
    elif axis == "+z":
        index_origin = 2
    plane = MAPPING_AXIS_TO_PLANE[axis]

    section = hull_section(hull, axis, origin, normalise)

    padding = 0.1 * np.mean(
        COLOURSPACE_MODELS_DOMAIN_RANGE_SCALE_1_TO_REFERENCE[model])
    min_x = np.min(ijk_vertices[..., plane[0]]) - padding
    max_x = np.max(ijk_vertices[..., plane[0]]) + padding
    min_y = np.min(ijk_vertices[..., plane[1]]) - padding
    max_y = np.max(ijk_vertices[..., plane[1]]) + padding
    extent = (min_x, max_x, min_y, max_y)

    use_RGB_section_colours = str(section_colours).upper() == "RGB"
    if use_RGB_section_colours:
        ii, jj = np.meshgrid(
            np.linspace(min_x, max_x, samples),
            np.linspace(max_y, min_y, samples),
        )
        ij = tstack([ii, jj])
        ijk_section = full((samples, samples, 3),
                           np.median(section[..., index_origin]))
        ijk_section[..., plane] = ij
        ijk_section /= COLOURSPACE_MODELS_DOMAIN_RANGE_SCALE_1_TO_REFERENCE[
            model]
        XYZ_section = convert(
            colourspace_model_axis_reorder(ijk_section, model, "Inverse"),
            model,
            "CIE XYZ",
            **convert_kwargs,
        )
        RGB_section = XYZ_to_plotting_colourspace(XYZ_section)
    else:
        section_colours = np.hstack([section_colours, section_opacity])

    facecolor = "none" if use_RGB_section_colours else section_colours
    polygon = Polygon(
        section[..., plane],
        facecolor=facecolor,
        edgecolor="none",
        zorder=CONSTANTS_COLOUR_STYLE.zorder.background_polygon,
    )
    axes.add_patch(polygon)
    if use_RGB_section_colours:
        image = axes.imshow(
            np.clip(RGB_section, 0, 1),
            interpolation="bilinear",
            extent=extent,
            clip_path=None,
            alpha=section_opacity,
            zorder=CONSTANTS_COLOUR_STYLE.zorder.background_polygon,
        )
        image.set_clip_path(polygon)

    settings = {
        "axes": axes,
        "bounding_box": extent,
    }
    settings.update(kwargs)

    return render(**settings)
Beispiel #4
0
def plot_hull_section_contour(
    hull: trimesh.Trimesh,  # type: ignore[name-defined]  # noqa
    model: Union[Literal["CAM02LCD", "CAM02SCD", "CAM02UCS", "CAM16LCD",
                         "CAM16SCD", "CAM16UCS", "CIE XYZ", "CIE xyY",
                         "CIE Lab", "CIE Luv", "CIE UCS", "CIE UVW", "DIN99",
                         "Hunter Lab", "Hunter Rdab", "ICaCb", "ICtCp", "IPT",
                         "IgPgTg", "Jzazbz", "OSA UCS", "Oklab", "hdr-CIELAB",
                         "hdr-IPT", ], str, ] = "CIE xyY",
    axis: Union[Literal["+z", "+x", "+y"], str] = "+z",
    origin: Floating = 0.5,
    normalise: Boolean = True,
    contour_colours: Optional[Union[ArrayLike, str]] = None,
    contour_opacity: Floating = 1,
    convert_kwargs: Optional[Dict] = None,
    **kwargs: Any,
) -> Tuple[plt.Figure, plt.Axes]:
    """
    Plot the section contour of given *trimesh* hull along given axis and
    origin.

    Parameters
    ----------
    hull
        *Trimesh* hull.
    model
        Colourspace model, see :attr:`colour.COLOURSPACE_MODELS` attribute for
        the list of supported colourspace models.
    axis
        Axis the hull section will be normal to.
    origin
        Coordinate along ``axis`` at which to plot the hull section.
    normalise
        Whether to normalise ``axis`` to the extent of the hull along it.
    contour_colours
        Colours of the hull section contour, if ``contour_colours`` is set to
        *RGB*, the colours will be computed according to the corresponding
        coordinates.
    contour_opacity
        Opacity of the hull section contour.
    convert_kwargs
        Keyword arguments for the :func:`colour.convert` definition.

    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
    --------
    >>> from colour.models import RGB_COLOURSPACE_sRGB
    >>> from colour.utilities import is_trimesh_installed
    >>> vertices, faces, _outline = primitive_cube(1, 1, 1, 64, 64, 64)
    >>> XYZ_vertices = RGB_to_XYZ(
    ...     vertices['position'] + 0.5,
    ...     RGB_COLOURSPACE_sRGB.whitepoint,
    ...     RGB_COLOURSPACE_sRGB.whitepoint,
    ...     RGB_COLOURSPACE_sRGB.matrix_RGB_to_XYZ,
    ... )
    >>> if is_trimesh_installed:
    ...     import trimesh
    ...     hull = trimesh.Trimesh(XYZ_vertices, faces, process=False)
    ...     plot_hull_section_contour(hull, contour_colours='RGB')
    ...     # doctest: +ELLIPSIS
    (<Figure size ... with 1 Axes>, <...AxesSubplot...>)

    .. image:: ../_static/Plotting_Plot_Hull_Section_Contour.png
        :align: center
        :alt: plot_hull_section_contour
    """

    hull = hull.copy()

    contour_colours = cast(
        Union[ArrayLike, str],
        optional(contour_colours, CONSTANTS_COLOUR_STYLE.colour.dark),
    )

    settings: Dict[str, Any] = {"uniform": True}
    settings.update(kwargs)

    _figure, axes = artist(**settings)

    convert_kwargs = optional(convert_kwargs, {})

    # Luminance / Lightness is re-ordered along "z-up" axis.
    with suppress_warnings(python_warnings=True):
        ijk_vertices = colourspace_model_axis_reorder(
            convert(hull.vertices, "CIE XYZ", model, **convert_kwargs), model)
        ijk_vertices = np.nan_to_num(ijk_vertices)
        ijk_vertices *= COLOURSPACE_MODELS_DOMAIN_RANGE_SCALE_1_TO_REFERENCE[
            model]

    hull.vertices = ijk_vertices

    plane = MAPPING_AXIS_TO_PLANE[axis]

    padding = 0.1 * np.mean(
        COLOURSPACE_MODELS_DOMAIN_RANGE_SCALE_1_TO_REFERENCE[model])
    min_x = np.min(ijk_vertices[..., plane[0]]) - padding
    max_x = np.max(ijk_vertices[..., plane[0]]) + padding
    min_y = np.min(ijk_vertices[..., plane[1]]) - padding
    max_y = np.max(ijk_vertices[..., plane[1]]) + padding
    extent = (min_x, max_x, min_y, max_y)

    use_RGB_contour_colours = str(contour_colours).upper() == "RGB"
    section = hull_section(hull, axis, origin, normalise)
    if use_RGB_contour_colours:
        ijk_section = section / (
            COLOURSPACE_MODELS_DOMAIN_RANGE_SCALE_1_TO_REFERENCE[model])
        XYZ_section = convert(
            colourspace_model_axis_reorder(ijk_section, model, "Inverse"),
            model,
            "CIE XYZ",
            **convert_kwargs,
        )
        contour_colours = np.clip(XYZ_to_plotting_colourspace(XYZ_section), 0,
                                  1)

    section = np.reshape(section[..., plane], (-1, 1, 2))
    line_collection = LineCollection(
        np.concatenate([section[:-1], section[1:]], axis=1),
        colors=contour_colours,
        alpha=contour_opacity,
        zorder=CONSTANTS_COLOUR_STYLE.zorder.background_line,
    )
    axes.add_collection(line_collection)

    settings = {
        "axes": axes,
        "bounding_box": extent,
    }
    settings.update(kwargs)

    return render(**settings)
Beispiel #5
0
def plot_chromaticity_diagram_colours(
        samples=256,
        diagram_opacity=1.0,
        diagram_clipping_path=None,
        cmfs='CIE 1931 2 Degree Standard Observer',
        method='CIE 1931',
        **kwargs):
    """
    Plots the *Chromaticity Diagram* colours according to given method.

    Parameters
    ----------
    samples : numeric, optional
        Samples count on one axis.
    diagram_opacity : numeric, optional
        Opacity of the *Chromaticity Diagram* colours.
    diagram_clipping_path : array_like, optional
        Path of points used to clip the *Chromaticity Diagram* colours.
    cmfs : unicode or XYZ_ColourMatchingFunctions, optional
        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.filter_cmfs` definition.
    method : unicode, optional
        **{'CIE 1931', 'CIE 1960 UCS', 'CIE 1976 UCS'}**,
        *Chromaticity Diagram* method.

    Other Parameters
    ----------------
    \\**kwargs : dict, optional
        {:func:`colour.plotting.artist`, :func:`colour.plotting.render`},
        Please refer to the documentation of the previously listed definitions.

    Returns
    -------
    tuple
        Current figure and axes.

    Examples
    --------
    >>> plot_chromaticity_diagram_colours()  # doctest: +ELLIPSIS
    (<Figure size ... with 1 Axes>, <...AxesSubplot...>)

    .. image:: ../_static/Plotting_Plot_Chromaticity_Diagram_Colours.png
        :align: center
        :alt: plot_chromaticity_diagram_colours
    """

    settings = {'uniform': True}
    settings.update(kwargs)

    _figure, axes = artist(**settings)

    method = method.upper()

    cmfs = first_item(filter_cmfs(cmfs).values())

    illuminant = CONSTANTS_COLOUR_STYLE.colour.colourspace.whitepoint

    ii, jj = np.meshgrid(np.linspace(0, 1, samples),
                         np.linspace(1, 0, samples))
    ij = tstack([ii, jj])

    # NOTE: Various values in the grid have potential to generate
    # zero-divisions, they could be avoided by perturbing the grid, e.g. adding
    # a small epsilon. It was decided instead to disable warnings.
    with suppress_warnings(python_warnings=True):
        if method == 'CIE 1931':
            XYZ = xy_to_XYZ(ij)
            spectral_locus = XYZ_to_xy(cmfs.values, illuminant)
        elif method == 'CIE 1960 UCS':
            XYZ = xy_to_XYZ(UCS_uv_to_xy(ij))
            spectral_locus = UCS_to_uv(XYZ_to_UCS(cmfs.values))
        elif method == 'CIE 1976 UCS':
            XYZ = xy_to_XYZ(Luv_uv_to_xy(ij))
            spectral_locus = Luv_to_uv(XYZ_to_Luv(cmfs.values, illuminant),
                                       illuminant)
        else:
            raise ValueError(
                'Invalid method: "{0}", must be one of '
                '[\'CIE 1931\', \'CIE 1960 UCS\', \'CIE 1976 UCS\']'.format(
                    method))

    RGB = normalise_maximum(XYZ_to_plotting_colourspace(XYZ, illuminant),
                            axis=-1)

    polygon = Polygon(spectral_locus if diagram_clipping_path is None else
                      diagram_clipping_path,
                      facecolor='none',
                      edgecolor='none')
    axes.add_patch(polygon)
    # Preventing bounding box related issues as per
    # https://github.com/matplotlib/matplotlib/issues/10529
    image = axes.imshow(RGB,
                        interpolation='bilinear',
                        extent=(0, 1, 0, 1),
                        clip_path=None,
                        alpha=diagram_opacity)
    image.set_clip_path(polygon)

    settings = {'axes': axes}
    settings.update(kwargs)

    return render(**kwargs)
Beispiel #6
0
def plot_chromaticity_diagram_colours(
    samples: Integer = 256,
    diagram_colours: Optional[Union[ArrayLike, str]] = None,
    diagram_opacity: Floating = 1,
    diagram_clipping_path: Optional[ArrayLike] = None,
    cmfs: Union[MultiSpectralDistributions, str, Sequence[Union[
        MultiSpectralDistributions,
        str]], ] = "CIE 1931 2 Degree Standard Observer",
    method: Union[Literal["CIE 1931", "CIE 1960 UCS", "CIE 1976 UCS"],
                  str] = "CIE 1931",
    **kwargs: Any,
) -> Tuple[plt.Figure, plt.Axes]:
    """
    Plot the *Chromaticity Diagram* colours according to given method.

    Parameters
    ----------
    samples
        Samples count on one axis when computing the *Chromaticity Diagram*
        colours.
    diagram_colours
        Colours of the *Chromaticity Diagram*, if ``diagram_colours`` is set
        to *RGB*, the colours will be computed according to the corresponding
        coordinates.
    diagram_opacity
        Opacity of the *Chromaticity Diagram*.
    diagram_clipping_path
        Path of points used to clip the *Chromaticity Diagram* colours.
    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.filter_cmfs` definition.
    method
        *Chromaticity Diagram* 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_chromaticity_diagram_colours(diagram_colours='RGB')
    ... # doctest: +ELLIPSIS
    (<Figure size ... with 1 Axes>, <...AxesSubplot...>)

    .. image:: ../_static/Plotting_Plot_Chromaticity_Diagram_Colours.png
        :align: center
        :alt: plot_chromaticity_diagram_colours
    """

    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)

    diagram_colours = cast(
        ArrayLike,
        optional(diagram_colours,
                 HEX_to_RGB(CONSTANTS_COLOUR_STYLE.colour.average)),
    )

    cmfs = cast(MultiSpectralDistributions,
                first_item(filter_cmfs(cmfs).values()))

    illuminant = CONSTANTS_COLOUR_STYLE.colour.colourspace.whitepoint

    if method == "cie 1931":
        spectral_locus = XYZ_to_xy(cmfs.values, illuminant)
    elif method == "cie 1960 ucs":
        spectral_locus = UCS_to_uv(XYZ_to_UCS(cmfs.values))
    elif method == "cie 1976 ucs":
        spectral_locus = Luv_to_uv(XYZ_to_Luv(cmfs.values, illuminant),
                                   illuminant)

    use_RGB_diagram_colours = str(diagram_colours).upper() == "RGB"
    if use_RGB_diagram_colours:
        ii, jj = np.meshgrid(np.linspace(0, 1, samples),
                             np.linspace(1, 0, samples))
        ij = tstack([ii, jj])

        # NOTE: Various values in the grid have potential to generate
        # zero-divisions, they could be avoided by perturbing the grid, e.g.
        # adding a small epsilon. It was decided instead to disable warnings.
        with suppress_warnings(python_warnings=True):
            if method == "cie 1931":
                XYZ = xy_to_XYZ(ij)
            elif method == "cie 1960 ucs":
                XYZ = xy_to_XYZ(UCS_uv_to_xy(ij))
            elif method == "cie 1976 ucs":
                XYZ = xy_to_XYZ(Luv_uv_to_xy(ij))

        diagram_colours = normalise_maximum(XYZ_to_plotting_colourspace(
            XYZ, illuminant),
                                            axis=-1)

    polygon = Polygon(
        spectral_locus
        if diagram_clipping_path is None else diagram_clipping_path,
        facecolor="none" if use_RGB_diagram_colours else np.hstack(
            [diagram_colours, diagram_opacity]),
        edgecolor="none" if use_RGB_diagram_colours else np.hstack(
            [diagram_colours, diagram_opacity]),
        zorder=CONSTANTS_COLOUR_STYLE.zorder.background_polygon,
    )
    axes.add_patch(polygon)

    if use_RGB_diagram_colours:
        # Preventing bounding box related issues as per
        # https://github.com/matplotlib/matplotlib/issues/10529
        image = axes.imshow(
            diagram_colours,
            interpolation="bilinear",
            extent=(0, 1, 0, 1),
            clip_path=None,
            alpha=diagram_opacity,
            zorder=CONSTANTS_COLOUR_STYLE.zorder.background_polygon,
        )
        image.set_clip_path(polygon)

    settings = {"axes": axes}
    settings.update(kwargs)

    return render(**kwargs)
Beispiel #7
0
def generate_illuminants_rawtoaces_v1() -> CaseInsensitiveMapping:
    """
    Generate a series of illuminants according to *RAW to ACES* v1:

    -   *CIE Illuminant D Series* in range [4000, 25000] kelvin degrees.
    -   *Blackbodies* in range [1000, 3500] kelvin degrees.
    -   A.M.P.A.S. variant of *ISO 7589 Studio Tungsten*.

    Returns
    -------
    :class:`colour.utilities.CaseInsensitiveMapping`
        Series of illuminants.

    Notes
    -----
    -   This definition introduces a few differences compared to
        *RAW to ACES* v1: *CIE Illuminant D Series* are computed in range
        [4002.15, 7003.77] kelvin degrees and the :math:`C_2` change is not
        used in *RAW to ACES* v1.

    References
    ----------
    :cite:`Dyer2017`

    Examples
    --------
    >>> list(sorted(generate_illuminants_rawtoaces_v1().keys()))
    ['1000K Blackbody', '1500K Blackbody', '2000K Blackbody', \
'2500K Blackbody', '3000K Blackbody', '3500K Blackbody', 'D100', 'D105', \
'D110', 'D115', 'D120', 'D125', 'D130', 'D135', 'D140', 'D145', 'D150', \
'D155', 'D160', 'D165', 'D170', 'D175', 'D180', 'D185', 'D190', 'D195', \
'D200', 'D205', 'D210', 'D215', 'D220', 'D225', 'D230', 'D235', 'D240', \
'D245', 'D250', 'D40', 'D45', 'D50', 'D55', 'D60', 'D65', 'D70', 'D75', \
'D80', 'D85', 'D90', 'D95', 'iso7589']
    """

    global _ILLUMINANTS_RAWTOACES_V1

    if _ILLUMINANTS_RAWTOACES_V1 is not None:
        illuminants = _ILLUMINANTS_RAWTOACES_V1
    else:
        illuminants = CaseInsensitiveMapping()

        # CIE Illuminants D Series from 4000K to 25000K.
        for i in np.arange(4000, 25000 + 500, 500):
            CCT = i * 1.4388 / 1.4380
            xy = CCT_to_xy_CIE_D(CCT)
            sd = sd_CIE_illuminant_D_series(xy)
            sd.name = f"D{int(CCT / 100):d}"
            illuminants[sd.name] = sd.align(SPECTRAL_SHAPE_RAWTOACES)

        # TODO: Remove when removing the "colour.sd_blackbody" definition
        # warning.
        with suppress_warnings(colour_usage_warnings=True):
            # Blackbody from 1000K to 4000K.
            for i in np.arange(1000, 4000, 500):
                sd = sd_blackbody(i, SPECTRAL_SHAPE_RAWTOACES)
                illuminants[sd.name] = sd

        # A.M.P.A.S. variant of ISO 7589 Studio Tungsten.
        sd = read_sds_from_csv_file(
            os.path.join(RESOURCES_DIRECTORY_RAWTOACES,
                         "AMPAS_ISO_7589_Tungsten.csv"))["iso7589"]
        illuminants.update({sd.name: sd})

        _ILLUMINANTS_RAWTOACES_V1 = illuminants

    return illuminants
Beispiel #8
0
def blackbody_colours_plot(shape=SpectralShape(150, 12500, 50),
                           cmfs='CIE 1931 2 Degree Standard Observer',
                           **kwargs):
    """
    Plots blackbody colours.

    Parameters
    ----------
    shape : SpectralShape, optional
        Spectral shape to use as plot boundaries.
    cmfs : unicode, optional
        Standard observer colour matching functions.

    Other Parameters
    ----------------
    \**kwargs : dict, optional
        {:func:`colour.plotting.render`},
        Please refer to the documentation of the previously listed definition.

    Returns
    -------
    Figure
        Current figure or None.

    Examples
    --------
    >>> blackbody_colours_plot()  # doctest: +SKIP
    """

    axes = canvas(**kwargs).gca()

    cmfs = get_cmfs(cmfs)

    colours = []
    temperatures = []

    with suppress_warnings():
        for temperature in shape:
            spd = blackbody_spd(temperature, cmfs.shape)

            XYZ = spectral_to_XYZ(spd, cmfs)
            RGB = normalise_maximum(XYZ_to_sRGB(XYZ / 100))

            colours.append(RGB)
            temperatures.append(temperature)

    x_min, x_max = min(temperatures), max(temperatures)
    y_min, y_max = 0, 1

    axes.bar(x=temperatures,
             height=1,
             width=shape.interval,
             color=colours,
             align='edge')

    settings = {
        'title': 'Blackbody Colours',
        'x_label': 'Temperature K',
        'y_label': None,
        'limits': (x_min, x_max, y_min, y_max),
        'x_tighten': True,
        'y_tighten': True,
        'y_ticker': False
    }
    settings.update(kwargs)

    return render(**settings)
Beispiel #9
0
def exponent_function_monitor_curve(x,
                                    exponent=1,
                                    offset=0,
                                    style='monCurveFwd'):
    """
    Defines the *Monitor Curve* exponent transfer function.

    Parameters
    ----------
    x : numeric or array_like
        Data to undergo the monitor curve exponential conversion.
    exponent : numeric or array_like, optional
        Exponent value used for the conversion.
    offset: numeric or array_like, optional
        Offset value used for the conversion.
    style : unicode, optional
        **{'monCurveFwd', 'monCurveRev', 'monCurveMirrorFwd',
        'monCurveMirrorRev'}**,
        Defines the behaviour for the transfer function to operate:

        -   *monCurveFwd*: *Monitor Curve Forward* exponential behaviour
            where the definition applies a power law function with a linear
            segment near the origin.
        -   *monCurveRev*: *Monitor Curve Reverse* exponential behaviour
            where the definition applies a power law function with a linear
            segment near the origin.
        -   *monCurveMirrorFwd*: *Monitor Curve Mirror Forward* exponential
            behaviour where the definition applies a power law function with a
            linear segment near the origin and mirrors the function for values
            less than zero (i.e. rotationally symmetric around the origin).
        -   *monCurveMirrorRev*: *Monitor Curve Mirror Reverse* exponential
            behaviour where the definition applies a power law function with a
            linear segment near the origin and mirrors the function for values
            less than zero (i.e. rotationally symmetric around the origin).

    Returns
    -------
    numeric or ndarray
        Exponentially converted data.

    Raises
    ------
    ValueError
        If the *style* is not defined.

    Examples
    --------
    >>> exponent_function_monitor_curve(  # doctest: +ELLIPSIS
    ...     0.18, 2.2, 0.001)
    0.0232240...
    >>> exponent_function_monitor_curve(  # doctest: +ELLIPSIS
    ...     -0.18, 2.2, 0.001)
    -0.0002054...
    >>> exponent_function_monitor_curve(  # doctest: +ELLIPSIS
    ...     0.18, 2.2, 0.001, 'monCurveRev')
    0.4581151...
    >>> exponent_function_monitor_curve(  # doctest: +ELLIPSIS
    ...     -0.18, 2.2, 0.001, 'monCurveRev')
    -157.7302795...
    >>> exponent_function_monitor_curve(  # doctest: +ELLIPSIS
    ...     0.18, 2.2, 2, 'monCurveMirrorFwd')
    0.1679399...
    >>> exponent_function_monitor_curve(  # doctest: +ELLIPSIS
    ...     -0.18, 2.2, 0.001, 'monCurveMirrorFwd')
    -0.0232240...
    >>> exponent_function_monitor_curve(  # doctest: +ELLIPSIS
    ...     0.18, 2.2, 0.001, 'monCurveMirrorRev')
    0.4581151...
    >>> exponent_function_monitor_curve(  # doctest: +ELLIPSIS
    ...     -0.18, 2.2, 0.001, 'monCurveMirrorRev')
    -0.4581151...
    """

    x = as_float_array(x)
    exponent = as_float_array(exponent)
    offset = as_float_array(offset)

    with suppress_warnings(python_warnings=True):
        s = as_float_array(
            ((exponent - 1) / offset) *
            ((exponent * offset) / ((exponent - 1) * (offset + 1)))**exponent)

        s[np.isnan(s)] = 1

    def monitor_curve_forward(x):
        """
        Defines the *Monitor Curve Forward* function.
        """

        x_break = offset / (exponent - 1)

        return np.where(
            x >= x_break,
            ((x + offset) / (1 + offset))**exponent,
            x * s,
        )

    def monitor_curve_reverse(y):
        """
        Defines the *Monitor Curve Reverse* function.
        """

        y_break = ((exponent * offset) / ((exponent - 1) *
                                          (1 + offset)))**exponent

        return np.where(
            y >= y_break,
            ((1 + offset) * (y**(1 / exponent))) - offset,
            y / s,
        )

    style = style.lower()
    if style == 'moncurvefwd':
        return as_float(monitor_curve_forward(x))
    elif style == 'moncurverev':
        return as_float(monitor_curve_reverse(x))
    elif style == 'moncurvemirrorfwd':
        return as_float(
            np.where(
                x >= 0,
                monitor_curve_forward(x),
                -monitor_curve_forward(-x),
            ))
    elif style == 'moncurvemirrorrev':
        return as_float(
            np.where(
                x >= 0,
                monitor_curve_reverse(x),
                -monitor_curve_reverse(-x),
            ))
    else:
        raise ValueError(
            'Undefined style used: "{0}", must be one of the following: '
            '"{1}".'.format(
                style, ', '.join([
                    'monCurveFwd', 'monCurveRev', 'monCurveMirrorFwd',
                    'monCurveMirrorRev'
                ])))