def test_convert_direct_keyword_argument_passing(self): """ Test :func:`colour.graph.conversion.convert` definition behaviour when direct keyword arguments are passed. """ a = np.array([0.20654008, 0.12197225, 0.05136952]) illuminant = CCS_ILLUMINANTS["CIE 1931 2 Degree Standard Observer"][ "D50" ] np.testing.assert_almost_equal( convert( a, "CIE XYZ", "CIE xyY", XYZ_to_xyY={"illuminant": illuminant} ), convert(a, "CIE XYZ", "CIE xyY", illuminant=illuminant), decimal=7, ) # Illuminant "ndarray" is converted to tuple here so that it can # be hashed by the "sd_to_XYZ" definition, this should never occur # in practical application. self.assertRaises( AttributeError, lambda: convert( SDS_COLOURCHECKERS["ColorChecker N Ohta"]["dark skin"], "Spectral Distribution", "sRGB", illuminant=tuple(illuminant), ), )
def test_convert_direct_keyword_argument_passing(self): """ Tests :func:`colour.graph.conversion.convert` definition behaviour when direct keyword arguments are passed. """ a = np.array([0.20654008, 0.12197225, 0.05136952]) illuminant = CCS_ILLUMINANTS['CIE 1931 2 Degree Standard Observer'][ 'D50'] np.testing.assert_almost_equal(convert( a, 'CIE XYZ', 'CIE xyY', XYZ_to_xyY={'illuminant': illuminant}), convert(a, 'CIE XYZ', 'CIE xyY', illuminant=illuminant), decimal=7) if six.PY3: # pragma: no cover # Illuminant "ndarray" is converted to tuple here so that it can # be hashed by the "sd_to_XYZ" definition, this should never occur # in practical application. self.assertRaises( AttributeError, lambda: convert(SDS_COLOURCHECKERS[ 'ColorChecker N Ohta']['dark skin'], 'Spectral Distribution', 'sRGB', illuminant=tuple(illuminant)))
def test_convert_direct_keyword_argument_passing(self): """ Tests :func:`colour.graph.conversion.convert` definition behaviour when direct keyword arguments are passed. """ a = np.array([0.20654008, 0.12197225, 0.05136952]) illuminant = ILLUMINANTS['CIE 1931 2 Degree Standard Observer']['D50'] np.testing.assert_almost_equal( convert( a, 'CIE XYZ', 'CIE xyY', XYZ_to_xyY={'illuminant': illuminant}), convert(a, 'CIE XYZ', 'CIE xyY', illuminant=illuminant), decimal=7) if six.PY3: # pragma: no cover self.assertRaises(AttributeError, lambda: convert( COLOURCHECKERS_SDS['ColorChecker N Ohta']['dark skin'], 'Spectral Distribution', 'sRGB', illuminant=illuminant))
def test_convert(self): """ Tests :func:`colour.graph.conversion.convert` definition. """ RGB_a = convert(SDS_COLOURCHECKERS['ColorChecker N Ohta']['dark skin'], 'Spectral Distribution', 'sRGB') np.testing.assert_almost_equal( RGB_a, np.array([0.45675795, 0.30986982, 0.24861924]), decimal=7) Jpapbp = convert(RGB_a, 'Output-Referred RGB', 'CAM16UCS') np.testing.assert_almost_equal( Jpapbp, np.array([0.39994810, 0.09206557, 0.08127526]), decimal=7) RGB_b = convert(Jpapbp, 'CAM16UCS', 'sRGB', verbose={'mode': 'Extended'}) # NOTE: The "CIE XYZ" tristimulus values to "sRGB" matrix is given # rounded at 4 decimals as per "IEC 61966-2-1:1999" and thus preventing # exact roundtrip. np.testing.assert_allclose(RGB_a, RGB_b, rtol=1e-5, atol=1e-5) np.testing.assert_almost_equal( convert('#808080', 'Hexadecimal', 'Scene-Referred RGB'), np.array([0.21586050, 0.21586050, 0.21586050]), decimal=7) self.assertAlmostEqual(convert('#808080', 'Hexadecimal', 'RGB Luminance'), 0.21586050, places=7) np.testing.assert_almost_equal( convert( convert(np.array([0.5, 0.5, 0.5]), 'Output-Referred RGB', 'Scene-Referred RGB'), 'RGB', 'YCbCr'), np.array([0.49215686, 0.50196078, 0.50196078]), decimal=7) np.testing.assert_almost_equal( convert( RGB_a, 'RGB', 'Scene-Referred RGB', RGB_to_RGB={'output_colourspace': RGB_COLOURSPACE_ACES2065_1}), np.array([0.36364180, 0.31715308, 0.25888531]), decimal=7)
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)
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)
def plot_RGB_scatter( RGB: ArrayLike, colourspace: Union[RGB_Colourspace, str, Sequence[Union[RGB_Colourspace, str]]], reference_colourspace: 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", colourspaces: Optional[Union[RGB_Colourspace, str, Sequence[Union[RGB_Colourspace, str]]]] = None, segments: Integer = 8, show_grid: Boolean = True, grid_segments: Integer = 10, show_spectral_locus: Boolean = False, spectral_locus_colour: Optional[Union[ArrayLike, str]] = None, points_size: Floating = 12, cmfs: Union[MultiSpectralDistributions, str, Sequence[Union[ MultiSpectralDistributions, str]], ] = "CIE 1931 2 Degree Standard Observer", chromatically_adapt: Boolean = False, convert_kwargs: Optional[Dict] = None, **kwargs: Any, ) -> Tuple[plt.Figure, plt.Axes]: """ Plot given *RGB* colourspace array in a scatter plot. 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.filter_RGB_colourspaces` definition. reference_colourspace Reference colourspace model to plot the gamuts into, see :attr:`colour.COLOURSPACE_MODELS` attribute for the list of supported colourspace models. colourspaces *RGB* colourspaces to plot the gamuts. ``colourspaces`` elements can be of any type or form supported by the :func:`colour.plotting.filter_RGB_colourspaces` definition. segments Edge segments count for each *RGB* colourspace cubes. show_grid Whether to show a grid at the bottom of the *RGB* colourspace cubes. grid_segments Edge segments count for the grid. show_spectral_locus Whether to show the spectral locus. spectral_locus_colour Spectral locus colour. points_size Scatter points size. 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. chromatically_adapt Whether to chromatically adapt the *RGB* colourspaces given in ``colourspaces`` to the whitepoint of the default plotting colourspace. convert_kwargs Keyword arguments for the :func:`colour.convert` definition. Other Parameters ---------------- kwargs {:func:`colour.plotting.artist`, :func:`colour.plotting.plot_RGB_colourspaces_gamuts`}, 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_scatter(RGB, 'ITU-R BT.709') # doctest: +ELLIPSIS (<Figure size ... with 1 Axes>, <...Axes3DSubplot...>) .. image:: ../_static/Plotting_Plot_RGB_Scatter.png :align: center :alt: plot_RGB_scatter """ colourspace = cast( RGB_Colourspace, first_item(filter_RGB_colourspaces(colourspace).values()), ) colourspaces = cast(List[str], optional(colourspaces, [colourspace.name])) convert_kwargs = optional(convert_kwargs, {}) count_c = len(colourspaces) settings = Structure( **{ "face_colours": [None] * count_c, "edge_colours": [(0.25, 0.25, 0.25)] * count_c, "face_alpha": [0.0] * count_c, "edge_alpha": [0.1] * count_c, }) settings.update(kwargs) settings["standalone"] = False plot_RGB_colourspaces_gamuts( colourspaces=colourspaces, reference_colourspace=reference_colourspace, segments=segments, show_grid=show_grid, grid_segments=grid_segments, show_spectral_locus=show_spectral_locus, spectral_locus_colour=spectral_locus_colour, cmfs=cmfs, chromatically_adapt=chromatically_adapt, **settings, ) XYZ = RGB_to_XYZ( RGB, colourspace.whitepoint, colourspace.whitepoint, colourspace.matrix_RGB_to_XYZ, ) convert_settings = {"illuminant": colourspace.whitepoint} convert_settings.update(convert_kwargs) points = colourspace_model_axis_reorder( convert(XYZ, "CIE XYZ", reference_colourspace, **convert_settings), reference_colourspace, ) axes = plt.gca() axes.scatter( points[..., 0], points[..., 1], points[..., 2], color=np.reshape(RGB, (-1, 3)), s=points_size, zorder=CONSTANTS_COLOUR_STYLE.zorder.midground_scatter, ) settings.update({"axes": axes, "standalone": True}) settings.update(kwargs) return render(**settings)
def plot_RGB_colourspaces_gamuts( colourspaces: Union[RGB_Colourspace, str, Sequence[Union[RGB_Colourspace, str]]], reference_colourspace: 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", segments: Integer = 8, show_grid: Boolean = True, grid_segments: Integer = 10, show_spectral_locus: Boolean = False, spectral_locus_colour: Optional[Union[ArrayLike, str]] = None, cmfs: Union[MultiSpectralDistributions, str, Sequence[Union[ MultiSpectralDistributions, str]], ] = "CIE 1931 2 Degree Standard Observer", chromatically_adapt: Boolean = False, convert_kwargs: Optional[Dict] = None, **kwargs: Any, ) -> Tuple[plt.Figure, plt.Axes]: """ Plot given *RGB* colourspaces gamuts in given reference colourspace. Parameters ---------- colourspaces *RGB* colourspaces to plot the gamuts. ``colourspaces`` elements can be of any type or form supported by the :func:`colour.plotting.filter_RGB_colourspaces` definition. reference_colourspace Reference colourspace model to plot the gamuts into, see :attr:`colour.COLOURSPACE_MODELS` attribute for the list of supported colourspace models. segments Edge segments count for each *RGB* colourspace cubes. show_grid Whether to show a grid at the bottom of the *RGB* colourspace cubes. grid_segments Edge segments count for the grid. show_spectral_locus Whether to show the spectral locus. spectral_locus_colour Spectral locus colour. 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. chromatically_adapt Whether to chromatically adapt the *RGB* colourspaces given in ``colourspaces`` to the whitepoint of the default plotting colourspace. convert_kwargs Keyword arguments for the :func:`colour.convert` definition. Other Parameters ---------------- edge_colours Edge colours array such as `edge_colours = (None, (0.5, 0.5, 1.0))`. edge_alpha Edge opacity value such as `edge_alpha = (0.0, 1.0)`. face_alpha Face opacity value such as `face_alpha = (0.5, 1.0)`. face_colours Face colours array such as `face_colours = (None, (0.5, 0.5, 1.0))`. kwargs {:func:`colour.plotting.artist`, :func:`colour.plotting.volume.nadir_grid`}, See the documentation of the previously listed definitions. Returns ------- :class:`tuple` Current figure and axes. Examples -------- >>> plot_RGB_colourspaces_gamuts(['ITU-R BT.709', 'ACEScg', 'S-Gamut']) ... # doctest: +ELLIPSIS (<Figure size ... with 1 Axes>, <...Axes3DSubplot...>) .. image:: ../_static/Plotting_Plot_RGB_Colourspaces_Gamuts.png :align: center :alt: plot_RGB_colourspaces_gamuts """ colourspaces = cast( List[RGB_Colourspace], list(filter_RGB_colourspaces(colourspaces).values()), ) convert_kwargs = optional(convert_kwargs, {}) count_c = len(colourspaces) title = ( f"{', '.join([colourspace.name for colourspace in colourspaces])} " f"- {reference_colourspace} Reference Colourspace") illuminant = CONSTANTS_COLOUR_STYLE.colour.colourspace.whitepoint convert_settings = {"illuminant": illuminant} convert_settings.update(convert_kwargs) settings = Structure( **{ "face_colours": [None] * count_c, "edge_colours": [None] * count_c, "face_alpha": [1] * count_c, "edge_alpha": [1] * count_c, "title": title, }) settings.update(kwargs) figure = plt.figure() axes = figure.add_subplot(111, projection="3d") points = zeros((4, 3)) if show_spectral_locus: cmfs = cast(MultiSpectralDistributions, first_item(filter_cmfs(cmfs).values())) XYZ = cmfs.values points = colourspace_model_axis_reorder( convert(XYZ, "CIE XYZ", reference_colourspace, **convert_settings), reference_colourspace, ) points[np.isnan(points)] = 0 c = ((0.0, 0.0, 0.0, 0.5) if spectral_locus_colour is None else spectral_locus_colour) axes.plot( points[..., 0], points[..., 1], points[..., 2], color=c, zorder=CONSTANTS_COLOUR_STYLE.zorder.midground_line, ) axes.plot( (points[-1][0], points[0][0]), (points[-1][1], points[0][1]), (points[-1][2], points[0][2]), color=c, zorder=CONSTANTS_COLOUR_STYLE.zorder.midground_line, ) plotting_colourspace = CONSTANTS_COLOUR_STYLE.colour.colourspace quads_c: List = [] RGB_cf: List = [] RGB_ce: List = [] for i, colourspace in enumerate(colourspaces): if chromatically_adapt and not np.array_equal( colourspace.whitepoint, plotting_colourspace.whitepoint): colourspace = colourspace.chromatically_adapt( plotting_colourspace.whitepoint, plotting_colourspace.whitepoint_name, ) quads_cb, RGB = RGB_identity_cube( width_segments=segments, height_segments=segments, depth_segments=segments, ) XYZ = RGB_to_XYZ( quads_cb, colourspace.whitepoint, colourspace.whitepoint, colourspace.matrix_RGB_to_XYZ, ) convert_settings = {"illuminant": colourspace.whitepoint} convert_settings.update(convert_kwargs) quads_c.extend( colourspace_model_axis_reorder( convert(XYZ, "CIE XYZ", reference_colourspace, **convert_settings), reference_colourspace, )) if settings.face_colours[i] is not None: RGB = ones(RGB.shape) * settings.face_colours[i] RGB_cf.extend( np.hstack([RGB, full((RGB.shape[0], 1), settings.face_alpha[i])])) if settings.edge_colours[i] is not None: RGB = ones(RGB.shape) * settings.edge_colours[i] RGB_ce.extend( np.hstack([RGB, full((RGB.shape[0], 1), settings.edge_alpha[i])])) quads = as_float_array(quads_c) RGB_f = as_float_array(RGB_cf) RGB_e = as_float_array(RGB_ce) quads[np.isnan(quads)] = 0 if quads.size != 0: for i, axis in enumerate("xyz"): min_a = np.minimum(np.min(quads[..., i]), np.min(points[..., i])) max_a = np.maximum(np.max(quads[..., i]), np.max(points[..., i])) getattr(axes, f"set_{axis}lim")((min_a, max_a)) labels = np.array( COLOURSPACE_MODELS_AXIS_LABELS[reference_colourspace])[as_int_array( colourspace_model_axis_reorder([0, 1, 2], reference_colourspace))] for i, axis in enumerate("xyz"): getattr(axes, f"set_{axis}label")(labels[i]) if show_grid: limits = np.array([[-1.5, 1.5], [-1.5, 1.5]]) quads_g, RGB_gf, RGB_ge = nadir_grid(limits, grid_segments, labels, axes, **settings) quads = np.vstack([quads_g, quads]) RGB_f = np.vstack([RGB_gf, RGB_f]) RGB_e = np.vstack([RGB_ge, RGB_e]) collection = Poly3DCollection(quads) collection.set_facecolors(RGB_f) collection.set_edgecolors(RGB_e) axes.add_collection3d(collection) settings.update({ "axes": axes, "axes_visible": False, "camera_aspect": "equal" }) settings.update(kwargs) return render(**settings)
def test_convert(self): """Test :func:`colour.graph.conversion.convert` definition.""" RGB_a = convert( SDS_COLOURCHECKERS["ColorChecker N Ohta"]["dark skin"], "Spectral Distribution", "sRGB", ) np.testing.assert_almost_equal( RGB_a, np.array([0.45675795, 0.30986982, 0.24861924]), decimal=7 ) Jpapbp = convert(RGB_a, "Output-Referred RGB", "CAM16UCS") np.testing.assert_almost_equal( Jpapbp, np.array([0.39994810, 0.09206557, 0.08127526]), decimal=7 ) RGB_b = convert( Jpapbp, "CAM16UCS", "sRGB", verbose={"mode": "Extended"} ) # NOTE: The "CIE XYZ" tristimulus values to "sRGB" matrix is given # rounded at 4 decimals as per "IEC 61966-2-1:1999" and thus preventing # exact roundtrip. np.testing.assert_allclose(RGB_a, RGB_b, rtol=1e-5, atol=1e-5) np.testing.assert_almost_equal( convert("#808080", "Hexadecimal", "Scene-Referred RGB"), np.array([0.21586050, 0.21586050, 0.21586050]), decimal=7, ) self.assertAlmostEqual( convert("#808080", "Hexadecimal", "RGB Luminance"), 0.21586050, places=7, ) np.testing.assert_almost_equal( convert( convert( np.array([0.5, 0.5, 0.5]), "Output-Referred RGB", "Scene-Referred RGB", ), "RGB", "YCbCr", ), np.array([0.49215686, 0.50196078, 0.50196078]), decimal=7, ) np.testing.assert_almost_equal( convert( RGB_a, "RGB", "Scene-Referred RGB", RGB_to_RGB={"output_colourspace": RGB_COLOURSPACE_ACES2065_1}, ), np.array([0.36364180, 0.31715308, 0.25888531]), decimal=7, ) # Consistency check to verify that all the colour models are properly # named in the graph: for model in COLOURSPACE_MODELS: convert( np.array([0.20654008, 0.12197225, 0.05136952]), "CIE XYZ", model, )