def camera(**kwargs): """ Sets the camera settings. Parameters ---------- \**kwargs : dict, optional **{'camera_aspect', 'elevation', 'azimuth'}** Keywords arguments such as ``{'camera_aspect': unicode (Matplotlib axes aspect), 'elevation' : numeric, 'azimuth' : numeric}`` Returns ------- Axes Current axes. """ settings = Structure( **{'camera_aspect': 'equal', 'elevation': None, 'azimuth': None}) settings.update(kwargs) axes = matplotlib.pyplot.gca() if settings.camera_aspect == 'equal': equal_axes3d(axes) axes.view_init(elev=settings.elevation, azim=settings.azimuth) return axes
def canvas(**kwargs): """ Sets the figure size. Parameters ---------- \**kwargs : dict, optional **{'figure_size', }** Keywords arguments such as ``{'figure_size': array_like (width, height), }`` Returns ------- Figure Current figure. """ settings = Structure( **{'figure_size': DEFAULT_FIGURE_SIZE}) settings.update(kwargs) figure = matplotlib.pyplot.gcf() if figure is None: figure = matplotlib.pyplot.figure(figsize=settings.figure_size) else: figure.set_size_inches(settings.figure_size) return figure
def camera(**kwargs): """ Sets the camera settings. Other Parameters ---------------- azimuth : numeric, optional Camera azimuth. camera_aspect : unicode, optional Matplotlib axes aspect. Default is *equal*. elevation : numeric, optional Camera elevation. Returns ------- Axes Current axes. """ axes = kwargs.get('axes', plt.gca()) settings = Structure(**{ 'camera_aspect': 'equal', 'elevation': None, 'azimuth': None }) settings.update(kwargs) if settings.camera_aspect == 'equal': uniform_axes3d(axes) axes.view_init(elev=settings.elevation, azim=settings.azimuth) return axes
def display(**kwargs): """ Sets the figure display. Parameters ---------- \**kwargs : dict, optional **{'standalone', 'filename'}** Keywords arguments such as ``{'standalone': bool (figure is shown), 'filename': unicode (figure is saved as `filename`)}`` Returns ------- bool Definition success. """ settings = Structure( **{'standalone': True, 'filename': None}) settings.update(kwargs) if settings.standalone: if settings.filename is not None: pylab.savefig(**kwargs) else: pylab.show() pylab.close() return True
def colour_cycle(**kwargs: Any) -> itertools.cycle: """ Return a colour cycle iterator using given colour map. Other Parameters ---------------- colour_cycle_map Matplotlib colourmap name. colour_cycle_count Colours count to pick in the colourmap. Returns ------- :class:`itertools.cycle` Colour cycle iterator. """ settings = Structure( **{ "colour_cycle_map": CONSTANTS_COLOUR_STYLE.colour.map, "colour_cycle_count": len(CONSTANTS_COLOUR_STYLE.colour.cycle), }) settings.update(kwargs) samples = np.linspace(0, 1, settings.colour_cycle_count) if isinstance(settings.colour_cycle_map, LinearSegmentedColormap): cycle = settings.colour_cycle_map(samples) else: cycle = getattr(plt.cm, settings.colour_cycle_map)(samples) return itertools.cycle(cycle)
def test_Structure(self): """Test :class:`colour.utilities.data_structures.Structure` class.""" structure = Structure(John="Doe", Jane="Doe") self.assertIn("John", structure) self.assertTrue(hasattr(structure, "John")) setattr(structure, "John", "Nemo") self.assertEqual(structure["John"], "Nemo") structure["John"] = "Vador" self.assertEqual(structure["John"], "Vador") del structure["John"] self.assertNotIn("John", structure) self.assertFalse(hasattr(structure, "John")) structure.John = "Doe" self.assertIn("John", structure) self.assertTrue(hasattr(structure, "John")) del structure.John self.assertNotIn("John", structure) self.assertFalse(hasattr(structure, "John")) structure = Structure(John=None, Jane=None) self.assertIsNone(structure.John) self.assertIsNone(structure["John"]) structure.update(**{"John": "Doe", "Jane": "Doe"}) self.assertEqual(structure.John, "Doe") self.assertEqual(structure["John"], "Doe")
def camera(**kwargs: Union[KwargsCamera, Any]) -> Tuple[plt.Figure, plt.Axes]: """ Set the camera settings. Other Parameters ---------------- kwargs {:func:`colour.plotting.common.KwargsCamera`}, See the documentation of the previously listed class. Returns ------- :class:`tuple` Current figure and axes. """ figure = cast(plt.Figure, kwargs.get("figure", plt.gcf())) axes = cast(plt.Axes, kwargs.get("axes", plt.gca())) settings = Structure(**{ "camera_aspect": "equal", "elevation": None, "azimuth": None }) settings.update(kwargs) if settings.camera_aspect == "equal": uniform_axes3d(axes=axes) axes.view_init(elev=settings.elevation, azim=settings.azimuth) return figure, axes
def display(**kwargs): """ Sets the figure display. Parameters ---------- \**kwargs : dict, optional **{'standalone', 'filename'}** Keywords arguments such as ``{'standalone': bool (figure is shown), 'filename': unicode (figure is saved as `filename`)}`` Returns ------- Figure Current figure or None. """ settings = Structure( **{'standalone': True, 'filename': None}) settings.update(kwargs) figure = matplotlib.pyplot.gcf() if settings.standalone: if settings.filename is not None: pylab.savefig(**kwargs) else: pylab.show() pylab.close() return None else: return figure
def colour_cycle(**kwargs): """ Returns a colour cycle iterator using given colour map. Parameters ---------- \**kwargs : dict, optional **{'colour_cycle_map', 'colour_cycle_count'}** Keywords arguments such as ``{'colour_cycle_map': unicode (Matplotlib colormap name), 'colour_cycle_count': int}`` Returns ------- cycle Colour cycle iterator. """ settings = Structure( **{'colour_cycle_map': 'hsv', 'colour_cycle_count': len(DEFAULT_COLOUR_CYCLE)}) settings.update(kwargs) if settings.colour_cycle_map is None: cycle = DEFAULT_COLOUR_CYCLE else: cycle = getattr(matplotlib.pyplot.cm, settings.colour_cycle_map)( np.linspace(0, 1, settings.colour_cycle_count)) return itertools.cycle(cycle)
def test_Structure(self): """ Tests :class:`colour.utilities.data_structures.Structure` class. """ structure = Structure(John='Doe', Jane='Doe') self.assertIn('John', structure) self.assertTrue(hasattr(structure, 'John')) setattr(structure, 'John', 'Nemo') self.assertEqual(structure['John'], 'Nemo') structure['John'] = 'Vador' self.assertEqual(structure['John'], 'Vador') del structure['John'] self.assertNotIn('John', structure) self.assertFalse(hasattr(structure, 'John')) structure.John = 'Doe' self.assertIn('John', structure) self.assertTrue(hasattr(structure, 'John')) del structure.John self.assertNotIn('John', structure) self.assertFalse(hasattr(structure, 'John')) structure = Structure(John=None, Jane=None) self.assertIsNone(structure.John) self.assertIsNone(structure['John']) structure.update(**{'John': 'Doe', 'Jane': 'Doe'}) self.assertEqual(structure.John, 'Doe') self.assertEqual(structure['John'], 'Doe')
def colour_cycle(**kwargs): """ Returns a colour cycle iterator using given colour map. Parameters ---------- \*\*kwargs : \*\* Keywords arguments. Returns ------- cycle Colour cycle iterator. """ settings = Structure( **{'colour_cycle_map': 'hsv', 'colour_cycle_count': len(DEFAULT_COLOUR_CYCLE)}) settings.update(kwargs) if settings.colour_cycle_map is None: cycle = DEFAULT_COLOUR_CYCLE else: cycle = getattr(matplotlib.pyplot.cm, settings.colour_cycle_map)( np.linspace(0, 1, settings.colour_cycle_count)) return itertools.cycle(cycle)
def display(**kwargs): """ Sets the figure display. Parameters ---------- \*\*kwargs : \*\* Keywords arguments. Returns ------- bool Definition success. """ settings = Structure( **{'standalone': True, 'filename': None}) settings.update(kwargs) if settings.standalone: if settings.filename is not None: pylab.savefig(**kwargs) else: pylab.show() pylab.close() return True
def colour_cycle(**kwargs): """ Returns a colour cycle iterator using given colour map. Other Parameters ---------------- colour_cycle_map : unicode or LinearSegmentedColormap, optional Matplotlib colourmap name. colour_cycle_count : int, optional Colours count to pick in the colourmap. Returns ------- cycle Colour cycle iterator. """ settings = Structure( **{ 'colour_cycle_map': COLOUR_STYLE_CONSTANTS.colour.map, 'colour_cycle_count': len(COLOUR_STYLE_CONSTANTS.colour.cycle) }) settings.update(kwargs) samples = np.linspace(0, 1, settings.colour_cycle_count) if isinstance(settings.colour_cycle_map, LinearSegmentedColormap): cycle = settings.colour_cycle_map(samples) else: cycle = getattr(plt.cm, settings.colour_cycle_map)(samples) return itertools.cycle(cycle)
def display(**kwargs): """ Sets the figure display. Other Parameters ---------------- standalone : bool, optional Whether to show the figure. filename : unicode, optional Figure will be saved using given `filename` argument. Returns ------- Figure Current figure or None. """ settings = Structure(**{'standalone': True, 'filename': None}) settings.update(kwargs) figure = matplotlib.pyplot.gcf() if settings.standalone: if settings.filename is not None: pylab.savefig(**kwargs) else: pylab.show() pylab.close() return None else: return figure
def display(**kwargs): """ Sets the figure display. Parameters ---------- \**kwargs : dict, optional **{'standalone', 'filename'}** Keywords arguments such as ``{'standalone': bool (figure is shown), 'filename': unicode (figure is saved as `filename`)}`` Returns ------- bool Definition success. """ settings = Structure(**{'standalone': True, 'filename': None}) settings.update(kwargs) if settings.standalone: if settings.filename is not None: pylab.savefig(**kwargs) else: pylab.show() pylab.close() return True
def canvas(**kwargs): """ Sets the figure size and aspect. Parameters ---------- \*\*kwargs : \*\* Keywords arguments. Returns ------- Figure Current figure. """ settings = Structure( **{'figure_size': DEFAULT_FIGURE_SIZE}) settings.update(kwargs) figure = matplotlib.pyplot.gcf() if figure is None: figure = matplotlib.pyplot.figure(figsize=settings.figure_size) else: figure.set_size_inches(settings.figure_size) return figure
def canvas(**kwargs): """ Sets the figure size. Parameters ---------- \**kwargs : dict, optional **{'figure_size', }** Keywords arguments such as ``{'figure_size': array_like (width, height), }`` Returns ------- Figure Current figure. """ settings = Structure(**{'figure_size': DEFAULT_FIGURE_SIZE}) settings.update(kwargs) figure = matplotlib.pyplot.gcf() if figure is None: figure = matplotlib.pyplot.figure(figsize=settings.figure_size) else: figure.set_size_inches(settings.figure_size) return figure
def camera(**kwargs): """ Sets the camera settings. Parameters ---------- \**kwargs : dict, optional **{'camera_aspect', 'elevation', 'azimuth'}** Keywords arguments such as ``{'camera_aspect': unicode (Matplotlib axes aspect), 'elevation' : numeric, 'azimuth' : numeric}`` Returns ------- bool Definition success. """ settings = Structure(**{ 'camera_aspect': 'equal', 'elevation': None, 'azimuth': None }) settings.update(kwargs) axes = matplotlib.pyplot.gca() if settings.camera_aspect == 'equal': equal_axes3d(axes) axes.view_init(elev=settings.elevation, azim=settings.azimuth) return True
def colour_cycle(**kwargs): """ Returns a colour cycle iterator using given colour map. Parameters ---------- \**kwargs : dict, optional **{'colour_cycle_map', 'colour_cycle_count'}** Keywords arguments such as ``{'colour_cycle_map': unicode (Matplotlib colormap name), 'colour_cycle_count': int}`` Returns ------- cycle Colour cycle iterator. """ settings = Structure(**{ 'colour_cycle_map': 'hsv', 'colour_cycle_count': len(DEFAULT_COLOUR_CYCLE) }) settings.update(kwargs) if settings.colour_cycle_map is None: cycle = DEFAULT_COLOUR_CYCLE else: cycle = getattr(matplotlib.pyplot.cm, settings.colour_cycle_map)(np.linspace( 0, 1, settings.colour_cycle_count)) return itertools.cycle(cycle)
def display(**kwargs): """ Sets the figure display. Parameters ---------- \**kwargs : dict, optional **{'standalone', 'filename'}** Keywords arguments such as ``{'standalone': bool (figure is shown), 'filename': unicode (figure is saved as `filename`)}`` Returns ------- Figure Current figure or None. """ settings = Structure(**{'standalone': True, 'filename': None}) settings.update(kwargs) figure = matplotlib.pyplot.gcf() if settings.standalone: if settings.filename is not None: pylab.savefig(**kwargs) else: pylab.show() pylab.close() return None else: return figure
def canvas(**kwargs): """ Sets the figure size. Other Parameters ---------------- figure_size : array_like, optional Array defining figure `width` and `height` such as `figure_size = (width, height)`. Returns ------- Figure Current figure. """ settings = Structure(**{'figure_size': DEFAULT_FIGURE_SIZE}) settings.update(kwargs) figure = matplotlib.pyplot.gcf() if figure is None: figure = matplotlib.pyplot.figure(figsize=settings.figure_size) else: figure.set_size_inches(settings.figure_size) return figure
def colour_cycle(**kwargs): """ Returns a colour cycle iterator using given colour map. Other Parameters ---------------- colour_cycle_map : unicode, optional Matplotlib colourmap name. colour_cycle_count : int, optional Colours count to pick in the colourmap. Returns ------- cycle Colour cycle iterator. """ settings = Structure(**{ 'colour_cycle_map': 'hsv', 'colour_cycle_count': len(DEFAULT_COLOUR_CYCLE) }) settings.update(kwargs) if settings.colour_cycle_map is None: cycle = DEFAULT_COLOUR_CYCLE else: cycle = getattr(matplotlib.pyplot.cm, settings.colour_cycle_map)(np.linspace( 0, 1, settings.colour_cycle_count)) return itertools.cycle(cycle)
def boundaries(**kwargs): """ Sets the plot boundaries. Other Parameters ---------------- bounding_box : array_like, optional Array defining current axes limits such `bounding_box = (x min, x max, y min, y max)`. x_tighten : bool, optional Whether to tighten the *X* axis limit. Default is `False`. y_tighten : bool, optional Whether to tighten the *Y* axis limit. Default is `False`. limits : array_like, optional Array defining current axes limits such as `limits = (x limit min, x limit max, y limit min, y limit max)`. `limits` argument values are added to the `margins` argument values to define the final bounding box for the current axes. margins : array_like, optional Array defining current axes margins such as `margins = (x margin min, x margin max, y margin min, y margin max)`. `margins` argument values are added to the `limits` argument values to define the final bounding box for the current axes. Returns ------- Axes Current axes. """ settings = Structure( **{ 'bounding_box': None, 'x_tighten': False, 'y_tighten': False, 'limits': (0, 1, 0, 1), 'margins': (0, 0, 0, 0) }) settings.update(kwargs) axes = matplotlib.pyplot.gca() if settings.bounding_box is None: x_limit_min, x_limit_max, y_limit_min, y_limit_max = (settings.limits) x_margin_min, x_margin_max, y_margin_min, y_margin_max = ( settings.margins) if settings.x_tighten: pylab.xlim(x_limit_min + x_margin_min, x_limit_max + x_margin_max) if settings.y_tighten: pylab.ylim(y_limit_min + y_margin_min, y_limit_max + y_margin_max) else: pylab.xlim(settings.bounding_box[0], settings.bounding_box[1]) pylab.ylim(settings.bounding_box[2], settings.bounding_box[3]) return axes
def boundaries(**kwargs): """ Sets the plot boundaries. Parameters ---------- \**kwargs : dict, optional **{'bounding_box', 'x_tighten', 'y_tighten', 'limits', 'margins'}** Keywords arguments such as ``{'bounding_box': array_like (x min, x max, y min, y max), 'x_tighten': bool, 'y_tighten': bool, 'limits': array_like (x min, x max, y min, y max), 'limits': array_like (x min, x max, y min, y max)}`` Returns ------- Axes Current axes. """ settings = Structure( **{ 'bounding_box': None, 'x_tighten': False, 'y_tighten': False, 'limits': (0, 1, 0, 1), 'margins': (0, 0, 0, 0) }) settings.update(kwargs) axes = matplotlib.pyplot.gca() if settings.bounding_box is None: x_limit_min, x_limit_max, y_limit_min, y_limit_max = (settings.limits) x_margin_min, x_margin_max, y_margin_min, y_margin_max = ( settings.margins) if settings.x_tighten: pylab.xlim(x_limit_min + x_margin_min, x_limit_max + x_margin_max) if settings.y_tighten: pylab.ylim(y_limit_min + y_margin_min, y_limit_max + y_margin_max) else: pylab.xlim(settings.bounding_box[0], settings.bounding_box[1]) pylab.ylim(settings.bounding_box[2], settings.bounding_box[3]) return axes
def boundaries(**kwargs): """ Sets the plot boundaries. Parameters ---------- \**kwargs : dict, optional **{'bounding_box', 'x_tighten', 'y_tighten', 'limits', 'margins'}** Keywords arguments such as ``{'bounding_box': array_like (x min, x max, y min, y max), 'x_tighten': bool, 'y_tighten': bool, 'limits': array_like (x min, x max, y min, y max), 'limits': array_like (x min, x max, y min, y max)}`` Returns ------- Axes Current axes. """ settings = Structure( **{'bounding_box': None, 'x_tighten': False, 'y_tighten': False, 'limits': (0, 1, 0, 1), 'margins': (0, 0, 0, 0)}) settings.update(kwargs) axes = matplotlib.pyplot.gca() if settings.bounding_box is None: x_limit_min, x_limit_max, y_limit_min, y_limit_max = ( settings.limits) x_margin_min, x_margin_max, y_margin_min, y_margin_max = ( settings.margins) if settings.x_tighten: pylab.xlim(x_limit_min + x_margin_min, x_limit_max + x_margin_max) if settings.y_tighten: pylab.ylim(y_limit_min + y_margin_min, y_limit_max + y_margin_max) else: pylab.xlim(settings.bounding_box[0], settings.bounding_box[1]) pylab.ylim(settings.bounding_box[2], settings.bounding_box[3]) return axes
def camera(**kwargs): """ Sets the camera settings. Other Parameters ---------------- figure : Figure, optional Figure to apply the render elements onto. axes : Axes, optional Axes to apply the render elements onto. azimuth : numeric, optional Camera azimuth. camera_aspect : unicode, optional Matplotlib axes aspect. Default is *equal*. elevation : numeric, optional Camera elevation. Returns ------- tuple Current figure and axes. """ figure = kwargs.get('figure', plt.gcf()) axes = kwargs.get('axes', plt.gca()) settings = Structure(**{ 'camera_aspect': 'equal', 'elevation': None, 'azimuth': None }) settings.update(kwargs) if settings.camera_aspect == 'equal': uniform_axes3d(axes=axes) axes.view_init(elev=settings.elevation, azim=settings.azimuth) return figure, axes
def boundaries(**kwargs): """ Sets the plot boundaries. Parameters ---------- \*\*kwargs : \*\* Keywords arguments. Returns ------- bool Definition success. """ settings = Structure( **{'bounding_box': None, 'x_tighten': False, 'y_tighten': False, 'limits': [0, 1, 0, 1], 'margins': [0, 0, 0, 0]}) settings.update(kwargs) if settings.bounding_box is None: x_limit_min, x_limit_max, y_limit_min, y_limit_max = ( settings.limits) x_margin_min, x_margin_max, y_margin_min, y_margin_max = ( settings.margins) if settings.x_tighten: pylab.xlim(x_limit_min + x_margin_min, x_limit_max + x_margin_max) if settings.y_tighten: pylab.ylim(y_limit_min + y_margin_min, y_limit_max + y_margin_max) else: pylab.xlim(settings.bounding_box[0], settings.bounding_box[1]) pylab.ylim(settings.bounding_box[2], settings.bounding_box[3]) return True
__license__ = 'New BSD License - http://opensource.org/licenses/BSD-3-Clause' __maintainer__ = 'Colour Developers' __email__ = '*****@*****.**' __status__ = 'Production' __all__ = [ 'JZAZBZ_CONSTANTS', 'JZAZBZ_XYZ_TO_LMS_MATRIX', 'JZAZBZ_LMS_TO_XYZ_MATRIX', 'JZAZBZ_LMS_P_TO_IZAZBZ_MATRIX', 'JZAZBZ_IZAZBZ_TO_LMS_P_MATRIX', 'XYZ_to_JzAzBz', 'JzAzBz_to_XYZ' ] JZAZBZ_CONSTANTS = Structure(b=1.15, g=0.66, d=-0.56, d_0=1.6295499532821566 * 10**-11) JZAZBZ_CONSTANTS.update(ST2084_CONSTANTS) JZAZBZ_CONSTANTS.m_2 = 1.7 * 2523 / 2**5 """ Constants for :math:`J_zA_zB_z` colourspace and its variant of the perceptual quantizer (PQ) from Dolby Laboratories. Notes ----- - The :math:`m2` constant, i.e. the power factor has been re-optimized during the development of the :math:`J_zA_zB_z` colourspace. JZAZBZ_CONSTANTS : Structure """ JZAZBZ_XYZ_TO_LMS_MATRIX = np.array([ [0.41478972, 0.579999, 0.0146480],
def render(**kwargs): """ Renders the current figure while adjusting various settings such as the bounding box, the title or background transparency. Other Parameters ---------------- figure : Figure, optional Figure to apply the render elements onto. axes : Axes, optional Axes to apply the render elements onto. filename : unicode, optional Figure will be saved using given ``filename`` argument. standalone : bool, optional Whether to show the figure and call :func:`plt.show` definition. aspect : unicode, optional Matplotlib axes aspect. axes_visible : bool, optional Whether the axes are visible. Default is *True*. bounding_box : array_like, optional Array defining current axes limits such `bounding_box = (x min, x max, y min, y max)`. tight_layout : bool, optional Whether to invoke the :func:`plt.tight_layout` definition. legend : bool, optional Whether to display the legend. Default is *False*. legend_columns : int, optional Number of columns in the legend. Default is *1*. transparent_background : bool, optional Whether to turn off the background patch. Default is *False*. title : unicode, optional Figure title. wrap_title : unicode, optional Whether to wrap the figure title, the default is to wrap at a number of characters equal to the width of the figure multiplied by 10. x_label : unicode, optional *X* axis label. y_label : unicode, optional *Y* axis label. x_ticker : bool, optional Whether to display the *X* axis ticker. Default is *True*. y_ticker : bool, optional Whether to display the *Y* axis ticker. Default is *True*. Returns ------- tuple Current figure and axes. """ figure = kwargs.get('figure') if figure is None: figure = plt.gcf() axes = kwargs.get('axes') if axes is None: axes = plt.gca() settings = Structure( **{ 'filename': None, 'standalone': True, 'aspect': None, 'axes_visible': True, 'bounding_box': None, 'tight_layout': True, 'legend': False, 'legend_columns': 1, 'transparent_background': True, 'title': None, 'wrap_title': True, 'x_label': None, 'y_label': None, 'x_ticker': True, 'y_ticker': True, }) settings.update(kwargs) if settings.aspect: axes.set_aspect(settings.aspect) if not settings.axes_visible: axes.set_axis_off() if settings.bounding_box: axes.set_xlim(settings.bounding_box[0], settings.bounding_box[1]) axes.set_ylim(settings.bounding_box[2], settings.bounding_box[3]) if settings.title: title = settings.title if settings.wrap_title: title = wrap_label(settings.title, int(plt.rcParams['figure.figsize'][0] * 10)) axes.set_title(title) if settings.x_label: axes.set_xlabel(settings.x_label) if settings.y_label: axes.set_ylabel(settings.y_label) if not settings.x_ticker: axes.set_xticks([]) if not settings.y_ticker: axes.set_yticks([]) if settings.legend: axes.legend(ncol=settings.legend_columns) if settings.tight_layout: figure.tight_layout() if settings.transparent_background: figure.patch.set_alpha(0) if settings.standalone: if settings.filename is not None: figure.savefig(settings.filename) else: plt.show() return figure, axes
def RGB_scatter_plot(RGB, colourspace, reference_colourspace='CIE xyY', colourspaces=None, segments=8, display_grid=True, grid_segments=10, spectral_locus=False, spectral_locus_colour=None, points_size=12, cmfs='CIE 1931 2 Degree Standard Observer', **kwargs): """ Plots given *RGB* colourspace array in a scatter plot. Parameters ---------- RGB : array_like *RGB* colourspace array. colourspace : RGB_Colourspace *RGB* colourspace of the *RGB* array. reference_colourspace : unicode, optional **{'CIE XYZ', 'CIE xyY', 'CIE Lab', 'CIE Luv', 'CIE UCS', 'CIE UVW', 'IPT'}**, Reference colourspace for colour conversion. colourspaces : array_like, optional *RGB* colourspaces to plot the gamuts. segments : int, optional Edge segments count for each *RGB* colourspace cubes. display_grid : bool, optional Display a grid at the bottom of the *RGB* colourspace cubes. grid_segments : bool, optional Edge segments count for the grid. spectral_locus : bool, optional Is spectral locus line plotted. spectral_locus_colour : array_like, optional Spectral locus line colour. points_size : numeric, optional Scatter points size. cmfs : unicode, optional Standard observer colour matching functions used for spectral locus. \**kwargs : dict, optional **{'face_colours', 'edge_colours', 'edge_alpha', 'face_alpha'}**, Arguments for each given colourspace where each key has an array_like value such as: ``{ 'face_colours': (None, (0.5, 0.5, 1.0)), 'edge_colours': (None, (0.5, 0.5, 1.0)), 'edge_alpha': (0.5, 1.0), 'face_alpha': (0.0, 1.0)}`` **{'grid_face_colours', 'grid_edge_colours', 'grid_face_alpha', 'grid_edge_alpha', 'x_axis_colour', 'y_axis_colour', 'x_ticks_colour', 'y_ticks_colour', 'x_label_colour', 'y_label_colour', 'ticks_and_label_location'}**, Arguments for the nadir grid such as ``{'grid_face_colours': (0.25, 0.25, 0.25), 'grid_edge_colours': (0.50, 0.50, 0.50), 'grid_face_alpha': 0.1, 'grid_edge_alpha': 0.5, 'x_axis_colour': (0.0, 0.0, 0.0, 1.0), 'y_axis_colour': (0.0, 0.0, 0.0, 1.0), 'x_ticks_colour': (0.0, 0.0, 0.0, 0.85), 'y_ticks_colour': (0.0, 0.0, 0.0, 0.85), 'x_label_colour': (0.0, 0.0, 0.0, 0.85), 'y_label_colour': (0.0, 0.0, 0.0, 0.85), 'ticks_and_label_location': ('-x', '-y')}`` Returns ------- bool Definition success. Examples -------- >>> c = 'Rec. 709' >>> RGB_scatter_plot(c) # doctest: +SKIP True """ colourspace = get_RGB_colourspace(colourspace) if colourspaces is None: colourspaces = (colourspace.name,) 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, 'standalone': False}) settings.update(kwargs) RGB_colourspaces_gamuts_plot( colourspaces=colourspaces, reference_colourspace=reference_colourspace, segments=segments, display_grid=display_grid, grid_segments=grid_segments, spectral_locus=spectral_locus, spectral_locus_colour=spectral_locus_colour, cmfs=cmfs, **settings) XYZ = RGB_to_XYZ( RGB, colourspace.whitepoint, colourspace.whitepoint, colourspace.RGB_to_XYZ_matrix) points = XYZ_to_reference_colourspace(XYZ, colourspace.whitepoint, reference_colourspace) axes = matplotlib.pyplot.gca() axes.scatter(points[..., 0], points[..., 1], points[..., 2], color=np.reshape(RGB, (-1, 3)), s=points_size) settings.update({'standalone': True}) settings.update(kwargs) camera(**settings) decorate(**settings) return display(**settings)
def nadir_grid(limits=None, segments=10, labels=None, axes=None, **kwargs): """ Returns a grid on *xy* plane made of quad geometric elements and its associated faces and edges colours. Ticks and labels are added to the given axes accordingly to the extended grid settings. Parameters ---------- limits : array_like, optional Extended grid limits. segments : int, optional Edge segments count for the extended grid. labels : array_like, optional Axis labels. axes : matplotlib.axes.Axes, optional Axes to add the grid. \**kwargs : dict, optional **{'grid_face_colours', 'grid_edge_colours', 'grid_face_alpha', 'grid_edge_alpha', 'x_axis_colour', 'y_axis_colour', 'x_ticks_colour', 'y_ticks_colour', 'x_label_colour', 'y_label_colour', 'ticks_and_label_location'}**, Arguments for the nadir grid such as ``{'grid_face_colours': (0.25, 0.25, 0.25), 'grid_edge_colours': (0.50, 0.50, 0.50), 'grid_face_alpha': 0.1, 'grid_edge_alpha': 0.5, 'x_axis_colour': (0.0, 0.0, 0.0, 1.0), 'y_axis_colour': (0.0, 0.0, 0.0, 1.0), 'x_ticks_colour': (0.0, 0.0, 0.0, 0.85), 'y_ticks_colour': (0.0, 0.0, 0.0, 0.85), 'x_label_colour': (0.0, 0.0, 0.0, 0.85), 'y_label_colour': (0.0, 0.0, 0.0, 0.85), 'ticks_and_label_location': ('-x', '-y')}`` Returns ------- tuple Grid quads, faces colours, edges colours. Examples -------- >>> c = 'Rec. 709' >>> RGB_scatter_plot(c) # doctest: +SKIP True """ if limits is None: limits = np.array([[-1, 1], [-1, 1]]) if labels is None: labels = ('x', 'y') extent = np.max(np.abs(limits[..., 1] - limits[..., 0])) settings = Structure( **{'grid_face_colours': (0.25, 0.25, 0.25), 'grid_edge_colours': (0.50, 0.50, 0.50), 'grid_face_alpha': 0.1, 'grid_edge_alpha': 0.5, 'x_axis_colour': (0.0, 0.0, 0.0, 1.0), 'y_axis_colour': (0.0, 0.0, 0.0, 1.0), 'x_ticks_colour': (0.0, 0.0, 0.0, 0.85), 'y_ticks_colour': (0.0, 0.0, 0.0, 0.85), 'x_label_colour': (0.0, 0.0, 0.0, 0.85), 'y_label_colour': (0.0, 0.0, 0.0, 0.85), 'ticks_and_label_location': ('-x', '-y')}) settings.update(**kwargs) # Outer grid. quads_g = grid(origin=(-extent / 2, -extent / 2), width=extent, height=extent, height_segments=segments, width_segments=segments) RGB_g = np.ones((quads_g.shape[0], quads_g.shape[-1])) RGB_gf = RGB_g * settings.grid_face_colours RGB_gf = np.hstack((RGB_gf, np.full((RGB_gf.shape[0], 1), settings.grid_face_alpha, np.float_))) RGB_ge = RGB_g * settings.grid_edge_colours RGB_ge = np.hstack((RGB_ge, np.full((RGB_ge.shape[0], 1), settings.grid_edge_alpha, np.float_))) # Inner grid. quads_gs = grid(origin=(-extent / 2, -extent / 2), width=extent, height=extent, height_segments=segments * 2, width_segments=segments * 2) RGB_gs = np.ones((quads_gs.shape[0], quads_gs.shape[-1])) RGB_gsf = RGB_gs * 0 RGB_gsf = np.hstack((RGB_gsf, np.full((RGB_gsf.shape[0], 1, np.float_), 0))) RGB_gse = np.clip(RGB_gs * settings.grid_edge_colours * 1.5, 0, 1) RGB_gse = np.hstack((RGB_gse, np.full((RGB_gse.shape[0], 1), settings.grid_edge_alpha / 2, np.float_))) # Axis. thickness = extent / 1000 quad_x = grid(origin=(limits[0, 0], -thickness / 2), width=extent, height=thickness) RGB_x = np.ones((quad_x.shape[0], quad_x.shape[-1] + 1)) RGB_x = RGB_x * settings.x_axis_colour quad_y = grid(origin=(-thickness / 2, limits[1, 0]), width=thickness, height=extent) RGB_y = np.ones((quad_y.shape[0], quad_y.shape[-1] + 1)) RGB_y = RGB_y * settings.y_axis_colour # Ticks. x_s = 1 if '+x' in settings.ticks_and_label_location else -1 y_s = 1 if '+y' in settings.ticks_and_label_location else -1 for i, axis in enumerate('xy'): h_a = 'center' if axis == 'x' else 'left' if x_s == 1 else 'right' v_a = 'center' ticks = list(sorted(set(quads_g[..., 0, i]))) ticks += [ticks[-1] + ticks[-1] - ticks[-2]] for tick in ticks: x = (limits[1, 1 if x_s == 1 else 0] + (x_s * extent / 25) if i else tick) y = (tick if i else limits[0, 1 if y_s == 1 else 0] + (y_s * extent / 25)) tick = int(tick) if float(tick).is_integer() else tick c = settings['{0}_ticks_colour'.format(axis)] axes.text(x, y, 0, tick, 'x', horizontalalignment=h_a, verticalalignment=v_a, color=c, clip_on=True) # Labels. for i, axis in enumerate('xy'): h_a = 'center' if axis == 'x' else 'left' if x_s == 1 else 'right' v_a = 'center' x = (limits[1, 1 if x_s == 1 else 0] + (x_s * extent / 10) if i else 0) y = (0 if i else limits[0, 1 if y_s == 1 else 0] + (y_s * extent / 10)) c = settings['{0}_label_colour'.format(axis)] axes.text(x, y, 0, labels[i], 'x', horizontalalignment=h_a, verticalalignment=v_a, color=c, size=20, clip_on=True) quads = np.vstack((quads_g, quads_gs, quad_x, quad_y)) RGB_f = np.vstack((RGB_gf, RGB_gsf, RGB_x, RGB_y)) RGB_e = np.vstack((RGB_ge, RGB_gse, RGB_x, RGB_y)) return quads, RGB_f, RGB_e
def nadir_grid( limits: Optional[ArrayLike] = None, segments: Integer = 10, labels: Optional[Sequence[str]] = None, axes: Optional[plt.Axes] = None, **kwargs: Any, ) -> Tuple[NDArray, NDArray, NDArray]: """ Return a grid on *CIE xy* plane made of quad geometric elements and its associated faces and edges colours. Ticks and labels are added to the given axes according to the extended grid settings. Parameters ---------- limits Extended grid limits. segments Edge segments count for the extended grid. labels Axis labels. axes Axes to add the grid. Other Parameters ---------------- grid_edge_alpha Grid edge opacity value such as `grid_edge_alpha = 0.5`. grid_edge_colours Grid edge colours array such as `grid_edge_colours = (0.25, 0.25, 0.25)`. grid_face_alpha Grid face opacity value such as `grid_face_alpha = 0.1`. grid_face_colours Grid face colours array such as `grid_face_colours = (0.25, 0.25, 0.25)`. ticks_and_label_location Location of the *X* and *Y* axis ticks and labels such as `ticks_and_label_location = ('-x', '-y')`. x_axis_colour *X* axis colour array such as `x_axis_colour = (0.0, 0.0, 0.0, 1.0)`. x_label_colour *X* axis label colour array such as `x_label_colour = (0.0, 0.0, 0.0, 0.85)`. x_ticks_colour *X* axis ticks colour array such as `x_ticks_colour = (0.0, 0.0, 0.0, 0.85)`. y_axis_colour *Y* axis colour array such as `y_axis_colour = (0.0, 0.0, 0.0, 1.0)`. y_label_colour *Y* axis label colour array such as `y_label_colour = (0.0, 0.0, 0.0, 0.85)`. y_ticks_colour *Y* axis ticks colour array such as `y_ticks_colour = (0.0, 0.0, 0.0, 0.85)`. Returns ------- :class:`tuple` Grid quads, faces colours, edges colours. Examples -------- >>> nadir_grid(segments=1) (array([[[-1. , -1. , 0. ], [ 1. , -1. , 0. ], [ 1. , 1. , 0. ], [-1. , 1. , 0. ]], <BLANKLINE> [[-1. , -1. , 0. ], [ 0. , -1. , 0. ], [ 0. , 0. , 0. ], [-1. , 0. , 0. ]], <BLANKLINE> [[-1. , 0. , 0. ], [ 0. , 0. , 0. ], [ 0. , 1. , 0. ], [-1. , 1. , 0. ]], <BLANKLINE> [[ 0. , -1. , 0. ], [ 1. , -1. , 0. ], [ 1. , 0. , 0. ], [ 0. , 0. , 0. ]], <BLANKLINE> [[ 0. , 0. , 0. ], [ 1. , 0. , 0. ], [ 1. , 1. , 0. ], [ 0. , 1. , 0. ]], <BLANKLINE> [[-1. , -0.001, 0. ], [ 1. , -0.001, 0. ], [ 1. , 0.001, 0. ], [-1. , 0.001, 0. ]], <BLANKLINE> [[-0.001, -1. , 0. ], [ 0.001, -1. , 0. ], [ 0.001, 1. , 0. ], [-0.001, 1. , 0. ]]]), array([[ 0.25, 0.25, 0.25, 0.1 ], [ 0. , 0. , 0. , 0. ], [ 0. , 0. , 0. , 0. ], [ 0. , 0. , 0. , 0. ], [ 0. , 0. , 0. , 0. ], [ 0. , 0. , 0. , 1. ], [ 0. , 0. , 0. , 1. ]]), array([[ 0.5 , 0.5 , 0.5 , 0.5 ], [ 0.75, 0.75, 0.75, 0.25], [ 0.75, 0.75, 0.75, 0.25], [ 0.75, 0.75, 0.75, 0.25], [ 0.75, 0.75, 0.75, 0.25], [ 0. , 0. , 0. , 1. ], [ 0. , 0. , 0. , 1. ]])) """ limits = as_float_array( cast(ArrayLike, optional(limits, np.array([[-1, 1], [-1, 1]])))) labels = cast(Sequence, optional(labels, ("x", "y"))) extent = np.max(np.abs(limits[..., 1] - limits[..., 0])) settings = Structure( **{ "grid_face_colours": (0.25, 0.25, 0.25), "grid_edge_colours": (0.50, 0.50, 0.50), "grid_face_alpha": 0.1, "grid_edge_alpha": 0.5, "x_axis_colour": (0.0, 0.0, 0.0, 1.0), "y_axis_colour": (0.0, 0.0, 0.0, 1.0), "x_ticks_colour": (0.0, 0.0, 0.0, 0.85), "y_ticks_colour": (0.0, 0.0, 0.0, 0.85), "x_label_colour": (0.0, 0.0, 0.0, 0.85), "y_label_colour": (0.0, 0.0, 0.0, 0.85), "ticks_and_label_location": ("-x", "-y"), }) settings.update(**kwargs) # Outer grid. quads_g = primitive_vertices_grid_mpl( origin=(-extent / 2, -extent / 2), width=extent, height=extent, height_segments=segments, width_segments=segments, ) RGB_g = ones((quads_g.shape[0], quads_g.shape[-1])) RGB_gf = RGB_g * settings.grid_face_colours RGB_gf = np.hstack( [RGB_gf, full((RGB_gf.shape[0], 1), settings.grid_face_alpha)]) RGB_ge = RGB_g * settings.grid_edge_colours RGB_ge = np.hstack( [RGB_ge, full((RGB_ge.shape[0], 1), settings.grid_edge_alpha)]) # Inner grid. quads_gs = primitive_vertices_grid_mpl( origin=(-extent / 2, -extent / 2), width=extent, height=extent, height_segments=segments * 2, width_segments=segments * 2, ) RGB_gs = ones((quads_gs.shape[0], quads_gs.shape[-1])) RGB_gsf = RGB_gs * 0 RGB_gsf = np.hstack([RGB_gsf, full((RGB_gsf.shape[0], 1), 0)]) RGB_gse = np.clip(RGB_gs * settings.grid_edge_colours * 1.5, 0, 1) RGB_gse = np.hstack( (RGB_gse, full((RGB_gse.shape[0], 1), settings.grid_edge_alpha / 2))) # Axis. thickness = extent / 1000 quad_x = primitive_vertices_grid_mpl(origin=(limits[0, 0], -thickness / 2), width=extent, height=thickness) RGB_x = ones((quad_x.shape[0], quad_x.shape[-1] + 1)) RGB_x = RGB_x * settings.x_axis_colour quad_y = primitive_vertices_grid_mpl(origin=(-thickness / 2, limits[1, 0]), width=thickness, height=extent) RGB_y = ones((quad_y.shape[0], quad_y.shape[-1] + 1)) RGB_y = RGB_y * settings.y_axis_colour if axes is not None: # Ticks. x_s = 1 if "+x" in settings.ticks_and_label_location else -1 y_s = 1 if "+y" in settings.ticks_and_label_location else -1 for i, axis in enumerate("xy"): h_a = "center" if axis == "x" else "left" if x_s == 1 else "right" v_a = "center" ticks = list(sorted(set(quads_g[..., 0, i]))) ticks += [ticks[-1] + ticks[-1] - ticks[-2]] for tick in ticks: x = (limits[1, 1 if x_s == 1 else 0] + (x_s * extent / 25) if i else tick) y = (tick if i else limits[0, 1 if y_s == 1 else 0] + (y_s * extent / 25)) tick = as_int_scalar(tick) if is_integer(tick) else tick c = settings[f"{axis}_ticks_colour"] axes.text( x, y, 0, tick, "x", horizontalalignment=h_a, verticalalignment=v_a, color=c, clip_on=True, ) # Labels. for i, axis in enumerate("xy"): h_a = "center" if axis == "x" else "left" if x_s == 1 else "right" v_a = "center" x = (limits[1, 1 if x_s == 1 else 0] + (x_s * extent / 10) if i else 0) y = (0 if i else limits[0, 1 if y_s == 1 else 0] + (y_s * extent / 10)) c = settings[f"{axis}_label_colour"] axes.text( x, y, 0, labels[i], "x", horizontalalignment=h_a, verticalalignment=v_a, color=c, size=20, clip_on=True, ) quads = np.vstack([quads_g, quads_gs, quad_x, quad_y]) RGB_f = np.vstack([RGB_gf, RGB_gsf, RGB_x, RGB_y]) RGB_e = np.vstack([RGB_ge, RGB_gse, RGB_x, RGB_y]) return quads, RGB_f, RGB_e
def RGB_colourspaces_gamuts_plot(colourspaces=None, reference_colourspace='CIE xyY', segments=8, display_grid=True, grid_segments=10, spectral_locus=False, spectral_locus_colour=None, cmfs='CIE 1931 2 Degree Standard Observer', **kwargs): """ Plots given *RGB* colourspaces gamuts in given reference colourspace. Parameters ---------- colourspaces : array_like, optional *RGB* colourspaces to plot the gamuts. reference_colourspace : unicode, optional **{'CIE XYZ', 'CIE xyY', 'CIE Lab', 'CIE Luv', 'CIE UCS', 'CIE UVW', 'IPT'}**, Reference colourspace to plot the gamuts into. segments : int, optional Edge segments count for each *RGB* colourspace cubes. display_grid : bool, optional Display a grid at the bottom of the *RGB* colourspace cubes. grid_segments : bool, optional Edge segments count for the grid. spectral_locus : bool, optional Is spectral locus line plotted. spectral_locus_colour : array_like, optional Spectral locus line colour. cmfs : unicode, optional Standard observer colour matching functions used for spectral locus. \**kwargs : dict, optional **{'face_colours', 'edge_colours', 'edge_alpha', 'face_alpha'}**, Arguments for each given colourspace where each key has an array_like value such as: ``{ 'face_colours': (None, (0.5, 0.5, 1.0)), 'edge_colours': (None, (0.5, 0.5, 1.0)), 'edge_alpha': (0.5, 1.0), 'face_alpha': (0.0, 1.0)}`` **{'grid_face_colours', 'grid_edge_colours', 'grid_face_alpha', 'grid_edge_alpha', 'x_axis_colour', 'y_axis_colour', 'x_ticks_colour', 'y_ticks_colour', 'x_label_colour', 'y_label_colour', 'ticks_and_label_location'}**, Arguments for the nadir grid such as ``{'grid_face_colours': (0.25, 0.25, 0.25), 'grid_edge_colours': (0.50, 0.50, 0.50), 'grid_face_alpha': 0.1, 'grid_edge_alpha': 0.5, 'x_axis_colour': (0.0, 0.0, 0.0, 1.0), 'y_axis_colour': (0.0, 0.0, 0.0, 1.0), 'x_ticks_colour': (0.0, 0.0, 0.0, 0.85), 'y_ticks_colour': (0.0, 0.0, 0.0, 0.85), 'x_label_colour': (0.0, 0.0, 0.0, 0.85), 'y_label_colour': (0.0, 0.0, 0.0, 0.85), 'ticks_and_label_location': ('-x', '-y')}`` Returns ------- bool Definition success. Examples -------- >>> c = ['Rec. 709', 'ACEScg', 'S-Gamut'] >>> RGB_colourspaces_gamuts_plot(c) # doctest: +SKIP True """ if colourspaces is None: colourspaces = ('Rec. 709', 'ACEScg') count_c = len(colourspaces) settings = Structure( **{'face_colours': [None] * count_c, 'edge_colours': [None] * count_c, 'face_alpha': [1] * count_c, 'edge_alpha': [1] * count_c, 'title': '{0} - {1} Reference Colourspace'.format( ', '.join(colourspaces), reference_colourspace)}) settings.update(kwargs) figure = matplotlib.pyplot.figure() axes = figure.add_subplot(111, projection='3d') illuminant = DEFAULT_PLOTTING_ILLUMINANT points = np.zeros((4, 3)) if spectral_locus: cmfs = get_cmfs(cmfs) XYZ = cmfs.values points = XYZ_to_reference_colourspace(XYZ, illuminant, 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) pylab.plot(points[..., 0], points[..., 1], points[..., 2], color=c, linewidth=2, zorder=1) pylab.plot((points[-1][0], points[0][0]), (points[-1][1], points[0][1]), (points[-1][2], points[0][2]), color=c, linewidth=2, zorder=1) quads, RGB_f, RGB_e = [], [], [] for i, colourspace in enumerate(colourspaces): colourspace = get_RGB_colourspace(colourspace) quads_c, RGB = RGB_identity_cube(width_segments=segments, height_segments=segments, depth_segments=segments) XYZ = RGB_to_XYZ( quads_c, colourspace.whitepoint, colourspace.whitepoint, colourspace.RGB_to_XYZ_matrix) quads.extend(XYZ_to_reference_colourspace(XYZ, colourspace.whitepoint, reference_colourspace)) if settings.face_colours[i] is not None: RGB = np.ones(RGB.shape) * settings.face_colours[i] RGB_f.extend(np.hstack( (RGB, np.full((RGB.shape[0], 1, np.float_), settings.face_alpha[i])))) if settings.edge_colours[i] is not None: RGB = np.ones(RGB.shape) * settings.edge_colours[i] RGB_e.extend(np.hstack( (RGB, np.full((RGB.shape[0], 1, np.float_), settings.edge_alpha[i])))) quads = np.asarray(quads) quads[np.isnan(quads)] = 0 if quads.size != 0: for i, axis in enumerate('xyz'): min_a = np.min(np.vstack((quads[..., i], points[..., i]))) max_a = np.max(np.vstack((quads[..., i], points[..., i]))) getattr(axes, 'set_{}lim'.format(axis))((min_a, max_a)) labels = REFERENCE_COLOURSPACES_TO_LABELS[reference_colourspace] for i, axis in enumerate('xyz'): getattr(axes, 'set_{}label'.format(axis))(labels[i]) if display_grid: if reference_colourspace == 'CIE Lab': limits = np.array([[-450, 450], [-450, 450]]) elif reference_colourspace == 'CIE Luv': limits = np.array([[-650, 650], [-650, 650]]) elif reference_colourspace == 'CIE UVW': limits = np.array([[-850, 850], [-850, 850]]) else: 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({ 'camera_aspect': 'equal', 'no_axes3d': True}) settings.update(kwargs) camera(**settings) decorate(**settings) return display(**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 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=None, reference_colourspace='CIE xyY', segments=8, show_grid=True, grid_segments=10, show_spectral_locus=False, spectral_locus_colour=None, cmfs='CIE 1931 2 Degree Standard Observer', **kwargs): """ Plots given *RGB* colourspaces gamuts in given reference colourspace. Parameters ---------- colourspaces : array_like, optional *RGB* colourspaces to plot the gamuts. reference_colourspace : unicode, optional **{'CIE XYZ', 'CIE xyY', 'CIE xy', 'CIE Lab', 'CIE LCHab', 'CIE Luv', 'CIE Luv uv', 'CIE LCHuv', 'CIE UCS', 'CIE UCS uv', 'CIE UVW', 'DIN 99', 'Hunter Lab', 'Hunter Rdab', 'IPT', 'JzAzBz', 'OSA UCS', 'hdr-CIELAB', 'hdr-IPT'}**, Reference colourspace to plot the gamuts into. segments : int, optional Edge segments count for each *RGB* colourspace cubes. show_grid : bool, optional Whether to show a grid at the bottom of the *RGB* colourspace cubes. grid_segments : bool, optional Edge segments count for the grid. show_spectral_locus : bool, optional Whether to show the spectral locus. spectral_locus_colour : array_like, optional Spectral locus colour. cmfs : unicode, optional Standard observer colour matching functions used for spectral locus. Other Parameters ---------------- \\**kwargs : dict, optional {:func:`colour.plotting.artist`, :func:`colour.plotting.volume.nadir_grid`}, Please refer to the documentation of the previously listed definitions. face_colours : array_like, optional Face colours array such as `face_colours = (None, (0.5, 0.5, 1.0))`. edge_colours : array_like, optional Edge colours array such as `edge_colours = (None, (0.5, 0.5, 1.0))`. face_alpha : numeric, optional Face opacity value such as `face_alpha = (0.5, 1.0)`. edge_alpha : numeric, optional Edge opacity value such as `edge_alpha = (0.0, 1.0)`. Returns ------- tuple Current figure and axes. Examples -------- >>> plot_RGB_colourspaces_gamuts(['ITU-R BT.709', 'ACEScg', 'S-Gamut']) ... # doctest: +SKIP .. image:: ../_static/Plotting_Plot_RGB_Colourspaces_Gamuts.png :align: center :alt: plot_RGB_colourspaces_gamuts """ if colourspaces is None: colourspaces = ('ITU-R BT.709', 'ACEScg') colourspaces = filter_RGB_colourspaces(colourspaces).values() count_c = len(colourspaces) title = '{0} - {1} Reference Colourspace'.format( ', '.join([colourspace.name for colourspace in colourspaces]), reference_colourspace, ) 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') illuminant = COLOUR_STYLE_CONSTANTS.colour.colourspace.whitepoint points = np.zeros((4, 3)) if show_spectral_locus: cmfs = first_item(filter_cmfs(cmfs).values()) XYZ = cmfs.values points = common_colourspace_model_axis_reorder( XYZ_to_colourspace_model(XYZ, illuminant, reference_colourspace), 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=1) axes.plot( (points[-1][0], points[0][0]), (points[-1][1], points[0][1]), (points[-1][2], points[0][2]), color=c, zorder=1) quads, RGB_f, RGB_e = [], [], [] for i, colourspace in enumerate(colourspaces): quads_c, RGB = RGB_identity_cube( width_segments=segments, height_segments=segments, depth_segments=segments) XYZ = RGB_to_XYZ( quads_c, colourspace.whitepoint, colourspace.whitepoint, colourspace.RGB_to_XYZ_matrix, ) quads.extend( common_colourspace_model_axis_reorder( XYZ_to_colourspace_model( XYZ, colourspace.whitepoint, reference_colourspace, ), reference_colourspace)) if settings.face_colours[i] is not None: RGB = np.ones(RGB.shape) * settings.face_colours[i] RGB_f.extend( np.hstack([ RGB, np.full((RGB.shape[0], 1), settings.face_alpha[i], DEFAULT_FLOAT_DTYPE) ])) if settings.edge_colours[i] is not None: RGB = np.ones(RGB.shape) * settings.edge_colours[i] RGB_e.extend( np.hstack([ RGB, np.full((RGB.shape[0], 1), settings.edge_alpha[i], DEFAULT_FLOAT_DTYPE) ])) quads = as_float_array(quads) quads[np.isnan(quads)] = 0 if quads.size != 0: for i, axis in enumerate('xyz'): min_a = min(np.min(quads[..., i]), np.min(points[..., i])) max_a = max(np.max(quads[..., i]), np.max(points[..., i])) getattr(axes, 'set_{}lim'.format(axis))((min_a, max_a)) labels = COLOURSPACE_MODELS_LABELS[reference_colourspace] for i, axis in enumerate('xyz'): getattr(axes, 'set_{}label'.format(axis))(labels[i]) if show_grid: if reference_colourspace == 'CIE Lab': limits = np.array([[-450, 450], [-450, 450]]) elif reference_colourspace == 'CIE Luv': limits = np.array([[-650, 650], [-650, 650]]) elif reference_colourspace == 'CIE UVW': limits = np.array([[-850, 850], [-850, 850]]) elif reference_colourspace in ('Hunter Lab', 'Hunter Rdab'): limits = np.array([[-250, 250], [-250, 250]]) else: 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 colour_checkers_coordinates_segmentation( image: ArrayLike, additional_data: Boolean = False, **kwargs: Any ) -> Union[ColourCheckersDetectionData, Tuple[NDArray, ...]]: """ Detect the colour checkers coordinates in given image :math:`image` using segmentation. This is the core detection definition. The process is a follows: - Input image :math:`image` is converted to a grayscale image :math:`image_g`. - Image :math:`image_g` is denoised. - Image :math:`image_g` is thresholded/segmented to image :math:`image_s`. - Image :math:`image_s` is eroded and dilated to cleanup remaining noise. - Contours are detected on image :math:`image_s`. - Contours are filtered to only keep squares/swatches above and below defined surface area. - Squares/swatches are clustered to isolate region-of-interest that are potentially colour checkers: Contours are scaled by a third so that colour checkers swatches are expected to be joined, creating a large rectangular cluster. Rectangles are fitted to the clusters. - Clusters with an aspect ratio different to the expected one are rejected, a side-effect is that the complementary pane of the *X-Rite* *ColorChecker Passport* is omitted. - Clusters with a number of swatches close to the expected one are kept. Parameters ---------- image Image to detect the colour checkers in. additional_data Whether to output additional data. Other Parameters ---------------- aspect_ratio Colour checker aspect ratio, e.g. 1.5. aspect_ratio_minimum Minimum colour checker aspect ratio for detection: projective geometry might reduce the colour checker aspect ratio. aspect_ratio_maximum Maximum colour checker aspect ratio for detection: projective geometry might increase the colour checker aspect ratio. swatches Colour checker swatches total count. swatches_horizontal Colour checker swatches horizontal columns count. swatches_vertical Colour checker swatches vertical row count. swatches_count_minimum Minimum swatches count to be considered for the detection. swatches_count_maximum Maximum swatches count to be considered for the detection. swatches_chromatic_slice A `slice` instance defining chromatic swatches used to detect if the colour checker is upside down. swatches_achromatic_slice A `slice` instance defining achromatic swatches used to detect if the colour checker is upside down. swatch_minimum_area_factor Swatch minimum area factor :math:`f` with the minimum area :math:`m_a` expressed as follows: :math:`m_a = image_w * image_h / s_c / f` where :math:`image_w`, :math:`image_h` and :math:`s_c` are respectively the image width, height and the swatches count. swatch_contour_scale As the image is filtered, the swatches area will tend to shrink, the generated contours can thus be scaled. cluster_contour_scale As the swatches are clustered, it might be necessary to adjust the cluster scale so that the masks are centred better on the swatches. working_width Size the input image is resized to for detection. fast_non_local_means_denoising_kwargs Keyword arguments for :func:`cv2.fastNlMeansDenoising` definition. adaptive_threshold_kwargs Keyword arguments for :func:`cv2.adaptiveThreshold` definition. interpolation_method Interpolation method used when resizing the images, `cv2.INTER_CUBIC` and `cv2.INTER_LINEAR` methods are recommended. Returns ------- :class:`colour_checker_detection.detection.segmentation.\ ColourCheckersDetectionData` or :class:`tuple` Tuple of colour checkers coordinates or :class:`ColourCheckersDetectionData` class instance with additional data. Notes ----- - Multiple colour checkers can be detected if presented in ``image``. Examples -------- >>> import os >>> from colour import read_image >>> from colour_checker_detection import TESTS_RESOURCES_DIRECTORY >>> path = os.path.join(TESTS_RESOURCES_DIRECTORY, ... 'colour_checker_detection', 'detection', ... 'IMG_1967.png') >>> image = read_image(path) >>> colour_checkers_coordinates_segmentation(image) # doctest: +ELLIPSIS (array([[ 369, 688], [ 382, 226], [1078, 246], [1065, 707]]...) """ image = as_float_array(image, FLOAT_DTYPE_DEFAULT)[..., :3] settings = Structure(**SETTINGS_SEGMENTATION_COLORCHECKER_CLASSIC) settings.update(**kwargs) image = as_8_bit_BGR_image( adjust_image(image, settings.working_width, settings.interpolation_method)) width, height = image.shape[1], image.shape[0] maximum_area = width * height / settings.swatches minimum_area = (width * height / settings.swatches / settings.swatch_minimum_area_factor) # Thresholding/Segmentation. image_g = cv2.cvtColor(image, cv2.COLOR_RGB2GRAY) image_g = cv2.fastNlMeansDenoising( image_g, None, **settings.fast_non_local_means_denoising_kwargs) image_s = cv2.adaptiveThreshold(image_g, **settings.adaptive_threshold_kwargs) # Cleanup. kernel = np.ones([3, 3], np.uint8) image_c = cv2.erode(image_s, kernel, iterations=1) image_c = cv2.dilate(image_c, kernel, iterations=1) # Detecting contours. contours, _hierarchy = cv2.findContours(image_c, cv2.RETR_TREE, cv2.CHAIN_APPROX_NONE) # Filtering squares/swatches contours. swatches = [] for contour in contours: curve = cv2.approxPolyDP(contour, 0.01 * cv2.arcLength(contour, True), True) if minimum_area < cv2.contourArea(curve) < maximum_area and is_square( curve): swatches.append(as_int_array(cv2.boxPoints( cv2.minAreaRect(curve)))) # Clustering squares/swatches. contours = np.zeros(image.shape, dtype=np.uint8) for swatch in [ as_int_array(scale_contour(swatch, settings.swatch_contour_scale)) for swatch in swatches ]: cv2.drawContours(contours, [swatch], -1, [255] * 3, -1) contours = cv2.cvtColor(contours, cv2.COLOR_RGB2GRAY) contours, _hierarchy = cv2.findContours(contours, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE) clusters = [ as_int_array( scale_contour( cv2.boxPoints(cv2.minAreaRect(cluster)), settings.cluster_contour_scale, )) for cluster in contours ] # Filtering clusters using their aspect ratio. filtered_clusters = [] for cluster in clusters[:]: rectangle = cv2.minAreaRect(cluster) width = max(rectangle[1][0], rectangle[1][1]) height = min(rectangle[1][0], rectangle[1][1]) ratio = width / height if (settings.aspect_ratio_minimum < ratio < settings.aspect_ratio_maximum): filtered_clusters.append(as_int_array(cluster)) clusters = filtered_clusters # Filtering swatches within cluster. counts = [] for cluster in clusters: count = 0 for swatch in swatches: if (cv2.pointPolygonTest(cluster, contour_centroid(swatch), False) == 1): count += 1 counts.append(count) indexes = np.where( np.logical_and( as_int_array(counts) >= settings.swatches_count_minimum, as_int_array(counts) <= settings.swatches_count_maximum, ))[0] colour_checkers = tuple(clusters[i] for i in indexes) if additional_data: return ColourCheckersDetectionData(tuple(colour_checkers), tuple(clusters), tuple(swatches), image_c) else: return colour_checkers
def decorate(**kwargs): """ Sets the figure decorations. Parameters ---------- \*\*kwargs : \*\* Keywords arguments. Returns ------- bool Definition success. """ settings = Structure( **{'title': None, 'x_label': None, 'y_label': None, 'legend': False, 'legend_location': 'upper right', 'x_ticker': False, 'y_ticker': False, 'x_ticker_locator': matplotlib.ticker.AutoMinorLocator(2), 'y_ticker_locator': matplotlib.ticker.AutoMinorLocator(2), 'no_ticks': False, 'no_x_ticks': False, 'no_y_ticks': False, 'grid': False, 'grid_which': 'both', 'grid_axis': 'both', 'x_axis_line': False, 'y_axis_line': False, 'aspect': None}) settings.update(kwargs) if settings.title: pylab.title(settings.title) if settings.x_label: pylab.xlabel(settings.x_label) if settings.y_label: pylab.ylabel(settings.y_label) if settings.legend: pylab.legend(loc=settings.legend_location) if settings.x_ticker: matplotlib.pyplot.gca().xaxis.set_minor_locator( settings.x_ticker_locator) if settings.y_ticker: matplotlib.pyplot.gca().yaxis.set_minor_locator( settings.y_ticker_locator) if settings.no_ticks: matplotlib.pyplot.gca().set_xticks([]) matplotlib.pyplot.gca().set_yticks([]) if settings.no_x_ticks: matplotlib.pyplot.gca().set_xticks([]) if settings.no_y_ticks: matplotlib.pyplot.gca().set_yticks([]) if settings.grid: pylab.grid(which=settings.grid_which, axis=settings.grid_axis) if settings.x_axis_line: pylab.axvline(color='black', linestyle='--') if settings.y_axis_line: pylab.axhline(color='black', linestyle='--') if settings.aspect: matplotlib.pyplot.axes().set_aspect(settings.aspect) return True
def RGB_scatter_plot(RGB, colourspace, reference_colourspace='CIE xyY', colourspaces=None, segments=8, display_grid=True, grid_segments=10, spectral_locus=False, spectral_locus_colour=None, points_size=12, cmfs='CIE 1931 2 Degree Standard Observer', **kwargs): """ Plots given *RGB* colourspace array in a scatter plot. Parameters ---------- RGB : array_like *RGB* colourspace array. colourspace : RGB_Colourspace *RGB* colourspace of the *RGB* array. reference_colourspace : unicode, optional **{'CIE XYZ', 'CIE xyY', 'CIE Lab', 'CIE Luv', 'CIE UCS', 'CIE UVW', 'IPT', 'Hunter Lab', 'Hunter Rdab'}**, Reference colourspace for colour conversion. colourspaces : array_like, optional *RGB* colourspaces to plot the gamuts. segments : int, optional Edge segments count for each *RGB* colourspace cubes. display_grid : bool, optional Display a grid at the bottom of the *RGB* colourspace cubes. grid_segments : bool, optional Edge segments count for the grid. spectral_locus : bool, optional Is spectral locus line plotted. spectral_locus_colour : array_like, optional Spectral locus line colour. points_size : numeric, optional Scatter points size. cmfs : unicode, optional Standard observer colour matching functions used for spectral locus. Other Parameters ---------------- \**kwargs : dict, optional {:func:`RGB_colourspaces_gamuts_plot`}, Please refer to the documentation of the previously listed definitions. Returns ------- Figure Current figure or None. Examples -------- >>> c = 'Rec. 709' >>> RGB_scatter_plot(c) # doctest: +SKIP """ colourspace = get_RGB_colourspace(colourspace) if colourspaces is None: colourspaces = (colourspace.name, ) 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, 'standalone': False }) settings.update(kwargs) RGB_colourspaces_gamuts_plot(colourspaces=colourspaces, reference_colourspace=reference_colourspace, segments=segments, display_grid=display_grid, grid_segments=grid_segments, spectral_locus=spectral_locus, spectral_locus_colour=spectral_locus_colour, cmfs=cmfs, **settings) XYZ = RGB_to_XYZ(RGB, colourspace.whitepoint, colourspace.whitepoint, colourspace.RGB_to_XYZ_matrix) points = common_colourspace_model_axis_reorder( XYZ_to_colourspace_model(XYZ, colourspace.whitepoint, reference_colourspace), reference_colourspace) axes = matplotlib.pyplot.gca() axes.scatter(points[..., 0], points[..., 1], points[..., 2], color=np.reshape(RGB, (-1, 3)), s=points_size) settings.update({'standalone': True}) settings.update(kwargs) camera(**settings) decorate(**settings) return display(**settings)
def RGB_colourspaces_gamuts_plot(colourspaces=None, reference_colourspace='CIE xyY', segments=8, display_grid=True, grid_segments=10, spectral_locus=False, spectral_locus_colour=None, cmfs='CIE 1931 2 Degree Standard Observer', **kwargs): """ Plots given *RGB* colourspaces gamuts in given reference colourspace. Parameters ---------- colourspaces : array_like, optional *RGB* colourspaces to plot the gamuts. reference_colourspace : unicode, optional **{'CIE XYZ', 'CIE xyY', 'CIE Lab', 'CIE Luv', 'CIE UCS', 'CIE UVW', 'IPT', 'Hunter Lab', 'Hunter Rdab'}**, Reference colourspace to plot the gamuts into. segments : int, optional Edge segments count for each *RGB* colourspace cubes. display_grid : bool, optional Display a grid at the bottom of the *RGB* colourspace cubes. grid_segments : bool, optional Edge segments count for the grid. spectral_locus : bool, optional Is spectral locus line plotted. spectral_locus_colour : array_like, optional Spectral locus line colour. cmfs : unicode, optional Standard observer colour matching functions used for spectral locus. Other Parameters ---------------- \**kwargs : dict, optional {:func:`nadir_grid`}, Please refer to the documentation of the previously listed definitions. face_colours : array_like, optional Face colours array such as `face_colours = (None, (0.5, 0.5, 1.0))`. edge_colours : array_like, optional Edge colours array such as `edge_colours = (None, (0.5, 0.5, 1.0))`. face_alpha : numeric, optional Face opacity value such as `face_alpha = (0.5, 1.0)`. edge_alpha : numeric, optional Edge opacity value such as `edge_alpha = (0.0, 1.0)`. Returns ------- Figure Current figure or None. Examples -------- >>> c = ['Rec. 709', 'ACEScg', 'S-Gamut'] >>> RGB_colourspaces_gamuts_plot(c) # doctest: +SKIP """ if colourspaces is None: colourspaces = ('Rec. 709', 'ACEScg') count_c = len(colourspaces) settings = Structure( **{ 'face_colours': [None] * count_c, 'edge_colours': [None] * count_c, 'face_alpha': [1] * count_c, 'edge_alpha': [1] * count_c, 'title': '{0} - {1} Reference Colourspace'.format(', '.join(colourspaces), reference_colourspace) }) settings.update(kwargs) figure = matplotlib.pyplot.figure() axes = figure.add_subplot(111, projection='3d') illuminant = DEFAULT_PLOTTING_ILLUMINANT points = np.zeros((4, 3)) if spectral_locus: cmfs = get_cmfs(cmfs) XYZ = cmfs.values points = common_colourspace_model_axis_reorder( XYZ_to_colourspace_model(XYZ, illuminant, reference_colourspace), 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) pylab.plot(points[..., 0], points[..., 1], points[..., 2], color=c, linewidth=2, zorder=1) pylab.plot((points[-1][0], points[0][0]), (points[-1][1], points[0][1]), (points[-1][2], points[0][2]), color=c, linewidth=2, zorder=1) quads, RGB_f, RGB_e = [], [], [] for i, colourspace in enumerate(colourspaces): colourspace = get_RGB_colourspace(colourspace) quads_c, RGB = RGB_identity_cube(width_segments=segments, height_segments=segments, depth_segments=segments) XYZ = RGB_to_XYZ(quads_c, colourspace.whitepoint, colourspace.whitepoint, colourspace.RGB_to_XYZ_matrix) quads.extend( common_colourspace_model_axis_reorder( XYZ_to_colourspace_model(XYZ, colourspace.whitepoint, reference_colourspace), reference_colourspace)) if settings.face_colours[i] is not None: RGB = np.ones(RGB.shape) * settings.face_colours[i] RGB_f.extend( np.hstack((RGB, np.full((RGB.shape[0], 1), settings.face_alpha[i], np.float_)))) if settings.edge_colours[i] is not None: RGB = np.ones(RGB.shape) * settings.edge_colours[i] RGB_e.extend( np.hstack((RGB, np.full((RGB.shape[0], 1), settings.edge_alpha[i], np.float_)))) quads = np.asarray(quads) quads[np.isnan(quads)] = 0 if quads.size != 0: for i, axis in enumerate('xyz'): min_a = np.min(np.vstack((quads[..., i], points[..., i]))) max_a = np.max(np.vstack((quads[..., i], points[..., i]))) getattr(axes, 'set_{}lim'.format(axis))((min_a, max_a)) labels = COLOURSPACE_MODELS_LABELS[reference_colourspace] for i, axis in enumerate('xyz'): getattr(axes, 'set_{}label'.format(axis))(labels[i]) if display_grid: if reference_colourspace == 'CIE Lab': limits = np.array([[-450, 450], [-450, 450]]) elif reference_colourspace == 'CIE Luv': limits = np.array([[-650, 650], [-650, 650]]) elif reference_colourspace == 'CIE UVW': limits = np.array([[-850, 850], [-850, 850]]) elif reference_colourspace in ('Hunter Lab', 'Hunter Rdab'): limits = np.array([[-250, 250], [-250, 250]]) else: 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({'camera_aspect': 'equal', 'no_axes': True}) settings.update(kwargs) camera(**settings) decorate(**settings) return display(**settings)
def detect_colour_checkers_segmentation( image: ArrayLike, samples: Integer = 16, additional_data: Boolean = False, **kwargs: Any, ) -> Union[Tuple[ColourCheckerSwatchesData, ...], Tuple[NDArray, ...]]: """ Detect the colour checkers swatches in given image using segmentation. Parameters ---------- image : array_like Image to detect the colour checkers swatches in. samples : int Samples count to use to compute the swatches colours. The effective samples count is :math:`samples^2`. additional_data : bool, optional Whether to output additional data. Other Parameters ---------------- aspect_ratio Colour checker aspect ratio, e.g. 1.5. aspect_ratio_minimum Minimum colour checker aspect ratio for detection: projective geometry might reduce the colour checker aspect ratio. aspect_ratio_maximum Maximum colour checker aspect ratio for detection: projective geometry might increase the colour checker aspect ratio. swatches Colour checker swatches total count. swatches_horizontal Colour checker swatches horizontal columns count. swatches_vertical Colour checker swatches vertical row count. swatches_count_minimum Minimum swatches count to be considered for the detection. swatches_count_maximum Maximum swatches count to be considered for the detection. swatches_chromatic_slice A `slice` instance defining chromatic swatches used to detect if the colour checker is upside down. swatches_achromatic_slice A `slice` instance defining achromatic swatches used to detect if the colour checker is upside down. swatch_minimum_area_factor Swatch minimum area factor :math:`f` with the minimum area :math:`m_a` expressed as follows: :math:`m_a = image_w * image_h / s_c / f` where :math:`image_w`, :math:`image_h` and :math:`s_c` are respectively the image width, height and the swatches count. swatch_contour_scale As the image is filtered, the swatches area will tend to shrink, the generated contours can thus be scaled. cluster_contour_scale As the swatches are clustered, it might be necessary to adjust the cluster scale so that the masks are centred better on the swatches. working_width Size the input image is resized to for detection. fast_non_local_means_denoising_kwargs Keyword arguments for :func:`cv2.fastNlMeansDenoising` definition. adaptive_threshold_kwargs Keyword arguments for :func:`cv2.adaptiveThreshold` definition. interpolation_method Interpolation method used when resizing the images, `cv2.INTER_CUBIC` and `cv2.INTER_LINEAR` methods are recommended. Returns ------- :class`tuple` Tuple of :class:`ColourCheckerSwatchesData` class instances or colour checkers swatches. Examples -------- >>> import os >>> from colour import read_image >>> from colour_checker_detection import TESTS_RESOURCES_DIRECTORY >>> path = os.path.join(TESTS_RESOURCES_DIRECTORY, ... 'colour_checker_detection', 'detection', ... 'IMG_1967.png') >>> image = read_image(path) >>> detect_colour_checkers_segmentation(image) # doctest: +SKIP (array([[ 0.361626... , 0.2241066..., 0.1187837...], [ 0.6280594..., 0.3950883..., 0.2434766...], [ 0.3326232..., 0.3156182..., 0.2891038...], [ 0.3048414..., 0.2738973..., 0.1069985...], [ 0.4174869..., 0.3199669..., 0.3081552...], [ 0.347873 ..., 0.4413193..., 0.2931614...], [ 0.6816301..., 0.3539050..., 0.0753397...], [ 0.2731050..., 0.2528467..., 0.3312920...], [ 0.6192335..., 0.2703833..., 0.1866387...], [ 0.3068567..., 0.1803366..., 0.1919807...], [ 0.4866354..., 0.4594004..., 0.0374186...], [ 0.6518523..., 0.4010608..., 0.0171886...], [ 0.1941571..., 0.1855801..., 0.2750632...], [ 0.2799946..., 0.3854609..., 0.1241038...], [ 0.5537481..., 0.2139004..., 0.1267332...], [ 0.7208045..., 0.5152904..., 0.0061946...], [ 0.5778360..., 0.2578533..., 0.2687992...], [ 0.1809450..., 0.3174742..., 0.2959902...], [ 0.7427522..., 0.6107554..., 0.4398439...], [ 0.6296108..., 0.5177606..., 0.3728032...], [ 0.5139589..., 0.4216307..., 0.2992694...], [ 0.3704401..., 0.3033927..., 0.2093089...], [ 0.2641854..., 0.2154007..., 0.1441267...], [ 0.1650098..., 0.1345239..., 0.0817437...]], dtype=float32),) """ image = as_float_array(image, FLOAT_DTYPE_DEFAULT)[..., :3] settings = Structure(**SETTINGS_SEGMENTATION_COLORCHECKER_CLASSIC) settings.update(**kwargs) image = adjust_image(image, settings.working_width, settings.interpolation_method) swatches_h, swatches_v = ( settings.swatches_horizontal, settings.swatches_vertical, ) colour_checkers_colours = [] colour_checkers_data = [] for colour_checker in extract_colour_checkers_segmentation( image, **settings): width, height = colour_checker.shape[1], colour_checker.shape[0] masks = swatch_masks(width, height, swatches_h, swatches_v, samples) swatch_colours = [] for mask in masks: swatch_colours.append( np.mean( colour_checker[mask[0]:mask[1], mask[2]:mask[3], ...], axis=(0, 1), )) # The colour checker might be flipped: The mean standard deviation # of some expected normalised chromatic and achromatic neutral # swatches is computed. If the chromatic mean is lesser than the # achromatic mean, it means that the colour checker is flipped. std_means = [] for slice_ in [ settings.swatches_chromatic_slice, settings.swatches_achromatic_slice, ]: swatch_std_mean = as_float_array(swatch_colours[slice_]) swatch_std_mean /= swatch_std_mean[..., 1][..., np.newaxis] std_means.append(np.mean(np.std(swatch_std_mean, 0))) if std_means[0] < std_means[1]: usage_warning("Colour checker was seemingly flipped," " reversing the samples!") swatch_colours = swatch_colours[::-1] colour_checkers_colours.append(np.asarray(swatch_colours)) colour_checkers_data.append((colour_checker, masks)) if additional_data: return tuple( ColourCheckerSwatchesData(tuple(colour_checkers_colours[i]), * colour_checkers_data[i]) for i, colour_checker_colours in enumerate(colour_checkers_colours)) else: return tuple(colour_checkers_colours)
def extract_colour_checkers_segmentation(image: ArrayLike, **kwargs: Any) -> Tuple[NDArray, ...]: """ Extract the colour checkers sub-images in given image using segmentation. Parameters ---------- image Image to extract the colours checkers sub-images from. Other Parameters ---------------- aspect_ratio Colour checker aspect ratio, e.g. 1.5. aspect_ratio_minimum Minimum colour checker aspect ratio for detection: projective geometry might reduce the colour checker aspect ratio. aspect_ratio_maximum Maximum colour checker aspect ratio for detection: projective geometry might increase the colour checker aspect ratio. swatches Colour checker swatches total count. swatches_horizontal Colour checker swatches horizontal columns count. swatches_vertical Colour checker swatches vertical row count. swatches_count_minimum Minimum swatches count to be considered for the detection. swatches_count_maximum Maximum swatches count to be considered for the detection. swatches_chromatic_slice A `slice` instance defining chromatic swatches used to detect if the colour checker is upside down. swatches_achromatic_slice A `slice` instance defining achromatic swatches used to detect if the colour checker is upside down. swatch_minimum_area_factor Swatch minimum area factor :math:`f` with the minimum area :math:`m_a` expressed as follows: :math:`m_a = image_w * image_h / s_c / f` where :math:`image_w`, :math:`image_h` and :math:`s_c` are respectively the image width, height and the swatches count. swatch_contour_scale As the image is filtered, the swatches area will tend to shrink, the generated contours can thus be scaled. cluster_contour_scale As the swatches are clustered, it might be necessary to adjust the cluster scale so that the masks are centred better on the swatches. working_width Size the input image is resized to for detection. fast_non_local_means_denoising_kwargs Keyword arguments for :func:`cv2.fastNlMeansDenoising` definition. adaptive_threshold_kwargs Keyword arguments for :func:`cv2.adaptiveThreshold` definition. interpolation_method Interpolation method used when resizing the images, `cv2.INTER_CUBIC` and `cv2.INTER_LINEAR` methods are recommended. Returns ------- :class:`tuple` Tuple of colour checkers sub-images. Examples -------- >>> import os >>> from colour import read_image >>> from colour_checker_detection import TESTS_RESOURCES_DIRECTORY >>> path = os.path.join(TESTS_RESOURCES_DIRECTORY, ... 'colour_checker_detection', 'detection', ... 'IMG_1967.png') >>> image = read_image(path) >>> extract_colour_checkers_segmentation(image) ... # doctest: +SKIP (array([[[ 0.17908671, 0.14010708, 0.09243158], [ 0.17805016, 0.13058874, 0.09513047], [ 0.17175764, 0.13128328, 0.08811688], ..., [ 0.15934898, 0.13436384, 0.07479276], [ 0.17178158, 0.13138185, 0.07703256], [ 0.15082785, 0.11866678, 0.07680314]], <BLANKLINE> [[ 0.16597673, 0.13563241, 0.08780421], [ 0.16490564, 0.13110894, 0.08601525], [ 0.16939694, 0.12963502, 0.08783565], ..., [ 0.14708202, 0.12856133, 0.0814603 ], [ 0.16883563, 0.12862256, 0.08452422], [ 0.16781917, 0.12363558, 0.07361614]], <BLANKLINE> [[ 0.16326806, 0.13720085, 0.08925959], [ 0.16014062, 0.13585283, 0.08104862], [ 0.16657823, 0.12889633, 0.08870038], ..., [ 0.14619341, 0.13086307, 0.07367594], [ 0.16302426, 0.13062705, 0.07938427], [ 0.16618022, 0.1266259 , 0.07200021]], <BLANKLINE> ..., [[ 0.1928642 , 0.14578913, 0.11224515], [ 0.18931177, 0.14416392, 0.10288388], [ 0.17707473, 0.1436448 , 0.09188452], ..., [ 0.16879168, 0.12867133, 0.09001681], [ 0.1699731 , 0.1287041 , 0.07616285], [ 0.17137891, 0.129711 , 0.07517841]], <BLANKLINE> [[ 0.19514292, 0.1532704 , 0.10375113], [ 0.18217109, 0.14982903, 0.10452617], [ 0.18830594, 0.1469499 , 0.10896181], ..., [ 0.18234864, 0.12642328, 0.08047272], [ 0.17617388, 0.13000189, 0.06874527], [ 0.17108543, 0.13264084, 0.06309374]], <BLANKLINE> [[ 0.16243187, 0.14983535, 0.08954653], [ 0.155507 , 0.14899652, 0.10273992], [ 0.17993385, 0.1498394 , 0.1099571 ], ..., [ 0.18079454, 0.1253967 , 0.07739887], [ 0.17239226, 0.13181566, 0.07806754], [ 0.17422497, 0.13277327, 0.07513551]]], dtype=float32),) """ image = as_float_array(image, FLOAT_DTYPE_DEFAULT)[..., :3] settings = Structure(**SETTINGS_SEGMENTATION_COLORCHECKER_CLASSIC) settings.update(**kwargs) image = adjust_image(image, settings.working_width, settings.interpolation_method) colour_checkers = [] for rectangle in cast( List[NDArray], colour_checkers_coordinates_segmentation(image, **settings), ): colour_checker = crop_and_level_image_with_rectangle( image, cv2.minAreaRect(rectangle), settings.interpolation_method) width, height = (colour_checker.shape[1], colour_checker.shape[0]) if width < height: colour_checker = cv2.rotate(colour_checker, cv2.ROTATE_90_CLOCKWISE) colour_checkers.append(colour_checker) return tuple(colour_checkers)
def nadir_grid(limits=None, segments=10, labels=None, axes=None, **kwargs): """ Returns a grid on *xy* plane made of quad geometric elements and its associated faces and edges colours. Ticks and labels are added to the given axes according to the extended grid settings. Parameters ---------- limits : array_like, optional Extended grid limits. segments : int, optional Edge segments count for the extended grid. labels : array_like, optional Axis labels. axes : matplotlib.axes.Axes, optional Axes to add the grid. Other Parameters ---------------- grid_face_colours : array_like, optional Grid face colours array such as `grid_face_colours = (0.25, 0.25, 0.25)`. grid_edge_colours : array_like, optional Grid edge colours array such as `grid_edge_colours = (0.25, 0.25, 0.25)`. grid_face_alpha : numeric, optional Grid face opacity value such as `grid_face_alpha = 0.1`. grid_edge_alpha : numeric, optional Grid edge opacity value such as `grid_edge_alpha = 0.5`. x_axis_colour : array_like, optional *X* axis colour array such as `x_axis_colour = (0.0, 0.0, 0.0, 1.0)`. y_axis_colour : array_like, optional *Y* axis colour array such as `y_axis_colour = (0.0, 0.0, 0.0, 1.0)`. x_ticks_colour : array_like, optional *X* axis ticks colour array such as `x_ticks_colour = (0.0, 0.0, 0.0, 0.85)`. y_ticks_colour : array_like, optional *Y* axis ticks colour array such as `y_ticks_colour = (0.0, 0.0, 0.0, 0.85)`. x_label_colour : array_like, optional *X* axis label colour array such as `x_label_colour = (0.0, 0.0, 0.0, 0.85)`. y_label_colour : array_like, optional *Y* axis label colour array such as `y_label_colour = (0.0, 0.0, 0.0, 0.85)`. ticks_and_label_location : array_like, optional Location of the *X* and *Y* axis ticks and labels such as `ticks_and_label_location = ('-x', '-y')`. Returns ------- tuple Grid quads, faces colours, edges colours. Examples -------- >>> nadir_grid(segments=1) (array([[[-1. , -1. , 0. ], [ 1. , -1. , 0. ], [ 1. , 1. , 0. ], [-1. , 1. , 0. ]], <BLANKLINE> [[-1. , -1. , 0. ], [ 0. , -1. , 0. ], [ 0. , 0. , 0. ], [-1. , 0. , 0. ]], <BLANKLINE> [[-1. , 0. , 0. ], [ 0. , 0. , 0. ], [ 0. , 1. , 0. ], [-1. , 1. , 0. ]], <BLANKLINE> [[ 0. , -1. , 0. ], [ 1. , -1. , 0. ], [ 1. , 0. , 0. ], [ 0. , 0. , 0. ]], <BLANKLINE> [[ 0. , 0. , 0. ], [ 1. , 0. , 0. ], [ 1. , 1. , 0. ], [ 0. , 1. , 0. ]], <BLANKLINE> [[-1. , -0.001, 0. ], [ 1. , -0.001, 0. ], [ 1. , 0.001, 0. ], [-1. , 0.001, 0. ]], <BLANKLINE> [[-0.001, -1. , 0. ], [ 0.001, -1. , 0. ], [ 0.001, 1. , 0. ], [-0.001, 1. , 0. ]]]), array([[ 0.25, 0.25, 0.25, 0.1 ], [ 0. , 0. , 0. , 0. ], [ 0. , 0. , 0. , 0. ], [ 0. , 0. , 0. , 0. ], [ 0. , 0. , 0. , 0. ], [ 0. , 0. , 0. , 1. ], [ 0. , 0. , 0. , 1. ]]), array([[ 0.5 , 0.5 , 0.5 , 0.5 ], [ 0.75, 0.75, 0.75, 0.25], [ 0.75, 0.75, 0.75, 0.25], [ 0.75, 0.75, 0.75, 0.25], [ 0.75, 0.75, 0.75, 0.25], [ 0. , 0. , 0. , 1. ], [ 0. , 0. , 0. , 1. ]])) """ if limits is None: limits = np.array([[-1, 1], [-1, 1]]) if labels is None: labels = ('x', 'y') extent = np.max(np.abs(limits[..., 1] - limits[..., 0])) settings = Structure( **{ 'grid_face_colours': (0.25, 0.25, 0.25), 'grid_edge_colours': (0.50, 0.50, 0.50), 'grid_face_alpha': 0.1, 'grid_edge_alpha': 0.5, 'x_axis_colour': (0.0, 0.0, 0.0, 1.0), 'y_axis_colour': (0.0, 0.0, 0.0, 1.0), 'x_ticks_colour': (0.0, 0.0, 0.0, 0.85), 'y_ticks_colour': (0.0, 0.0, 0.0, 0.85), 'x_label_colour': (0.0, 0.0, 0.0, 0.85), 'y_label_colour': (0.0, 0.0, 0.0, 0.85), 'ticks_and_label_location': ('-x', '-y') }) settings.update(**kwargs) # Outer grid. quads_g = grid( origin=(-extent / 2, -extent / 2), width=extent, height=extent, height_segments=segments, width_segments=segments) RGB_g = np.ones((quads_g.shape[0], quads_g.shape[-1])) RGB_gf = RGB_g * settings.grid_face_colours RGB_gf = np.hstack([ RGB_gf, np.full((RGB_gf.shape[0], 1), settings.grid_face_alpha, DEFAULT_FLOAT_DTYPE) ]) RGB_ge = RGB_g * settings.grid_edge_colours RGB_ge = np.hstack([ RGB_ge, np.full((RGB_ge.shape[0], 1), settings.grid_edge_alpha, DEFAULT_FLOAT_DTYPE) ]) # Inner grid. quads_gs = grid( origin=(-extent / 2, -extent / 2), width=extent, height=extent, height_segments=segments * 2, width_segments=segments * 2) RGB_gs = np.ones((quads_gs.shape[0], quads_gs.shape[-1])) RGB_gsf = RGB_gs * 0 RGB_gsf = np.hstack( [RGB_gsf, np.full((RGB_gsf.shape[0], 1), 0, DEFAULT_FLOAT_DTYPE)]) RGB_gse = np.clip(RGB_gs * settings.grid_edge_colours * 1.5, 0, 1) RGB_gse = np.hstack( (RGB_gse, np.full((RGB_gse.shape[0], 1), settings.grid_edge_alpha / 2, DEFAULT_FLOAT_DTYPE))) # Axis. thickness = extent / 1000 quad_x = grid( origin=(limits[0, 0], -thickness / 2), width=extent, height=thickness) RGB_x = np.ones((quad_x.shape[0], quad_x.shape[-1] + 1)) RGB_x = RGB_x * settings.x_axis_colour quad_y = grid( origin=(-thickness / 2, limits[1, 0]), width=thickness, height=extent) RGB_y = np.ones((quad_y.shape[0], quad_y.shape[-1] + 1)) RGB_y = RGB_y * settings.y_axis_colour if axes is not None: # Ticks. x_s = 1 if '+x' in settings.ticks_and_label_location else -1 y_s = 1 if '+y' in settings.ticks_and_label_location else -1 for i, axis in enumerate('xy'): h_a = 'center' if axis == 'x' else 'left' if x_s == 1 else 'right' v_a = 'center' ticks = list(sorted(set(quads_g[..., 0, i]))) ticks += [ticks[-1] + ticks[-1] - ticks[-2]] for tick in ticks: x = (limits[1, 1 if x_s == 1 else 0] + (x_s * extent / 25) if i else tick) y = (tick if i else limits[0, 1 if y_s == 1 else 0] + (y_s * extent / 25)) tick = DEFAULT_INT_DTYPE(tick) if DEFAULT_FLOAT_DTYPE( tick).is_integer() else tick c = settings['{0}_ticks_colour'.format(axis)] axes.text( x, y, 0, tick, 'x', horizontalalignment=h_a, verticalalignment=v_a, color=c, clip_on=True) # Labels. for i, axis in enumerate('xy'): h_a = 'center' if axis == 'x' else 'left' if x_s == 1 else 'right' v_a = 'center' x = (limits[1, 1 if x_s == 1 else 0] + (x_s * extent / 10) if i else 0) y = (0 if i else limits[0, 1 if y_s == 1 else 0] + (y_s * extent / 10)) c = settings['{0}_label_colour'.format(axis)] axes.text( x, y, 0, labels[i], 'x', horizontalalignment=h_a, verticalalignment=v_a, color=c, size=20, clip_on=True) quads = np.vstack([quads_g, quads_gs, quad_x, quad_y]) RGB_f = np.vstack([RGB_gf, RGB_gsf, RGB_x, RGB_y]) RGB_e = np.vstack([RGB_ge, RGB_gse, RGB_x, RGB_y]) return quads, RGB_f, RGB_e
def decorate(**kwargs): """ Sets the figure decorations. Parameters ---------- \**kwargs : dict, optional **{'title', 'x_label', 'y_label', 'legend', 'legend_columns', 'legend_location', 'x_ticker', 'y_ticker', 'x_ticker_locator', 'y_ticker_locator', 'grid', 'grid_which', 'grid_axis', 'x_axis_line', 'y_axis_line', 'aspect', 'no_axes'}** Keywords arguments such as ``{'title': unicode (figure title), 'x_label': unicode (X axis label), 'y_label': unicode (Y axis label), 'legend': bool, 'legend_columns': int, 'legend_location': unicode (Matplotlib legend location), 'x_ticker': bool, 'y_ticker': bool, 'x_ticker_locator': Locator, 'y_ticker_locator': Locator, 'grid': bool, 'grid_which': unicode, 'grid_axis': unicode, 'x_axis_line': bool, 'y_axis_line': bool, 'aspect': unicode (Matplotlib axes aspect), 'no_axes': bool}`` Returns ------- Axes Current axes. """ settings = Structure( **{'title': None, 'x_label': None, 'y_label': None, 'legend': False, 'legend_columns': 1, 'legend_location': 'upper right', 'x_ticker': True, 'y_ticker': True, 'x_ticker_locator': matplotlib.ticker.AutoMinorLocator(2), 'y_ticker_locator': matplotlib.ticker.AutoMinorLocator(2), 'grid': False, 'grid_which': 'both', 'grid_axis': 'both', 'x_axis_line': False, 'y_axis_line': False, 'aspect': None, 'no_axes': False}) settings.update(kwargs) axes = matplotlib.pyplot.gca() if settings.title: pylab.title(settings.title) if settings.x_label: pylab.xlabel(settings.x_label) if settings.y_label: pylab.ylabel(settings.y_label) if settings.legend: pylab.legend(loc=settings.legend_location, ncol=settings.legend_columns) if settings.x_ticker: axes.xaxis.set_minor_locator( settings.x_ticker_locator) else: axes.set_xticks([]) if settings.y_ticker: axes.yaxis.set_minor_locator( settings.y_ticker_locator) else: axes.set_yticks([]) if settings.grid: pylab.grid(which=settings.grid_which, axis=settings.grid_axis) if settings.x_axis_line: pylab.axvline(color='black', linestyle='--') if settings.y_axis_line: pylab.axhline(color='black', linestyle='--') if settings.aspect: matplotlib.pyplot.axes().set_aspect(settings.aspect) if settings.no_axes: axes.set_axis_off() return axes
def plot_RGB_scatter(RGB, colourspace, reference_colourspace='CIE xyY', colourspaces=None, segments=8, show_grid=True, grid_segments=10, show_spectral_locus=False, spectral_locus_colour=None, points_size=12, cmfs='CIE 1931 2 Degree Standard Observer', **kwargs): """ Plots given *RGB* colourspace array in a scatter plot. Parameters ---------- RGB : array_like *RGB* colourspace array. colourspace : RGB_Colourspace *RGB* colourspace of the *RGB* array. reference_colourspace : unicode, optional **{'CIE XYZ', 'CIE xyY', 'CIE xy', 'CIE Lab', 'CIE LCHab', 'CIE Luv', 'CIE Luv uv', 'CIE LCHuv', 'CIE UCS', 'CIE UCS uv', 'CIE UVW', 'DIN 99', 'Hunter Lab', 'Hunter Rdab', 'IPT', 'JzAzBz', 'OSA UCS', 'hdr-CIELAB', 'hdr-IPT'}**, Reference colourspace for colour conversion. colourspaces : array_like, optional *RGB* colourspaces to plot the gamuts. segments : int, optional Edge segments count for each *RGB* colourspace cubes. show_grid : bool, optional Whether to show a grid at the bottom of the *RGB* colourspace cubes. grid_segments : bool, optional Edge segments count for the grid. show_spectral_locus : bool, optional Whether to show the spectral locus. spectral_locus_colour : array_like, optional Spectral locus colour. points_size : numeric, optional Scatter points size. cmfs : unicode, optional Standard observer colour matching functions used for spectral locus. Other Parameters ---------------- \\**kwargs : dict, optional {:func:`colour.plotting.artist`, :func:`colour.plotting.plot_RGB_colourspaces_gamuts`}, Please refer to the documentation of the previously listed definitions. Returns ------- tuple Current figure and axes. Examples -------- >>> RGB = np.random.random((128, 128, 3)) >>> plot_RGB_scatter(RGB, 'ITU-R BT.709') # doctest: +SKIP .. image:: ../_static/Plotting_Plot_RGB_Scatter.png :align: center :alt: plot_RGB_scatter """ colourspace = first_item(filter_RGB_colourspaces(colourspace).values()) if colourspaces is None: colourspaces = (colourspace.name, ) 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, **settings) XYZ = RGB_to_XYZ(RGB, colourspace.whitepoint, colourspace.whitepoint, colourspace.RGB_to_XYZ_matrix) points = common_colourspace_model_axis_reorder( XYZ_to_colourspace_model(XYZ, colourspace.whitepoint, reference_colourspace), reference_colourspace) axes = plt.gca() axes.scatter( points[..., 0], points[..., 1], points[..., 2], color=np.reshape(RGB, (-1, 3)), s=points_size) settings.update({'axes': axes, 'standalone': True}) settings.update(kwargs) return render(**settings)