def multi_transfer_function_plot(colourspaces=None, inverse=False, **kwargs): """ Plots given colourspaces transfer functions. Parameters ---------- colourspaces : list, optional Colourspaces transfer functions to plot. inverse : bool Plot inverse transfer functions. \*\*kwargs : \*\* Keywords arguments. Returns ------- bool Definition success. Examples -------- >>> multi_transfer_function_plot(['sRGB', 'Rec. 709']) # doctest: +SKIP True """ if colourspaces is None: colourspaces = ['sRGB', 'Rec. 709'] samples = np.linspace(0, 1, 1000) for i, colourspace in enumerate(colourspaces): colourspace, name = get_RGB_colourspace(colourspace), colourspace RGBs = np.array([colourspace.inverse_transfer_function(x) if inverse else colourspace.transfer_function(x) for x in samples]) pylab.plot(samples, RGBs, label=u'{0}'.format(colourspace.name), linewidth=2) settings = { 'title': '{0} - Transfer Functions'.format( ', '.join(colourspaces)), 'x_tighten': True, 'legend': True, 'legend_location': 'upper left', 'x_ticker': True, 'y_ticker': True, 'grid': True, 'limits': [0, 1, 0, 1]} settings.update(kwargs) bounding_box(**settings) aspect(**settings) return display(**settings)
def multi_munsell_value_function_plot( functions=None, **kwargs): """ Plots given *Munsell* value functions. Parameters ---------- functions : array_like, optional *Munsell* value functions to plot. \*\*kwargs : \*\* Keywords arguments. Returns ------- bool Definition success. Raises ------ KeyError If one of the given *Munsell* value function is not found in the factory *Munsell* value functions. Examples -------- >>> fs = ('ASTM D1535-08', 'McCamy 1987') >>> multi_munsell_value_function_plot(fs) # doctest: +SKIP True """ if functions is None: functions = ('ASTM D1535-08', 'McCamy 1987') samples = np.linspace(0, 100, 1000) for i, function in enumerate(functions): function, name = MUNSELL_VALUE_METHODS.get(function), function if function is None: raise KeyError( ('"{0}" "Munsell" value function not found in ' 'factory "Munsell" value functions: "{1}".').format( name, sorted(MUNSELL_VALUE_METHODS.keys()))) pylab.plot(samples, [function(x) for x in samples], label=u'{0}'.format(name), linewidth=2) settings = { 'title': '{0} - Munsell Functions'.format(', '.join(functions)), 'x_label': 'Luminance Y', 'y_label': 'Munsell Value V', 'x_tighten': True, 'legend': True, 'legend_location': 'upper left', 'x_ticker': True, 'y_ticker': True, 'grid': True, 'limits': [0, 100, 0, 100]} settings.update(kwargs) bounding_box(**settings) aspect(**settings) return display(**settings)
def blackbody_spectral_radiance_plot( temperature=3500, cmfs='CIE 1931 2 Degree Standard Observer', blackbody='VY Canis Major', **kwargs): """ Plots given blackbody spectral radiance. Parameters ---------- temperature : numeric, optional Blackbody temperature. cmfs : unicode, optional Standard observer colour matching functions. blackbody : unicode, optional Blackbody name. \*\*kwargs : \*\* Keywords arguments. Returns ------- bool Definition success. Examples -------- >>> blackbody_spectral_radiance_plot() # doctest: +SKIP True """ cmfs, name = get_cmfs(cmfs), cmfs matplotlib.pyplot.subplots_adjust(hspace=0.4) spd = blackbody_spd(temperature, cmfs.shape) matplotlib.pyplot.figure(1) matplotlib.pyplot.subplot(211) settings = {'title': '{0} - Spectral Radiance'.format(blackbody), 'y_label': u'W / (sr m²) / m', 'standalone': False} settings.update(kwargs) single_spd_plot(spd, name, **settings) XYZ = spectral_to_XYZ(spd, cmfs) RGB = normalise(XYZ_to_sRGB(XYZ / 100)) matplotlib.pyplot.subplot(212) settings = {'title': '{0} - Colour'.format(blackbody), 'x_label': '{0}K'.format(temperature), 'y_label': '', 'aspect': None, 'standalone': False} single_colour_plot(colour_parameter(name='', RGB=RGB), **settings) settings = {'standalone': True} settings.update(kwargs) bounding_box(**settings) aspect(**settings) return display(**settings)
def multi_lightness_function_plot(functions=None, **kwargs): """ Plots given *Lightness* functions. Parameters ---------- functions : array_like, optional *Lightness* functions to plot. \*\*kwargs : \*\* Keywords arguments. Returns ------- bool Definition success. Raises ------ KeyError If one of the given *Lightness* function is not found in the factory *Lightness* functions. Examples -------- >>> fs = ('CIE 1976', 'Wyszecki 1964') >>> multi_lightness_function_plot(fs) # doctest: +SKIP True """ if functions is None: functions = ('CIE 1976', 'Wyszecki 1964') samples = np.linspace(0, 100, 1000) for i, function in enumerate(functions): function, name = LIGHTNESS_METHODS.get(function), function if function is None: raise KeyError( ('"{0}" "Lightness" function not found in factory ' '"Lightness" functions: "{1}".').format( name, sorted(LIGHTNESS_METHODS.keys()))) pylab.plot(samples, [function(x) for x in samples], label=u'{0}'.format(name), linewidth=2) settings = { 'title': '{0} - Lightness Functions'.format(', '.join(functions)), 'x_label': 'Luminance Y', 'y_label': 'Lightness L*', 'x_tighten': True, 'legend': True, 'legend_location': 'upper left', 'x_ticker': True, 'y_ticker': True, 'grid': True, 'limits': [0, 100, 0, 100]} settings.update(kwargs) bounding_box(**settings) aspect(**settings) return display(**settings)
def colour_rendering_index_bars_plot(illuminant, **kwargs): """ Plots the *colour rendering index* of given illuminant. Parameters ---------- illuminant : SpectralPowerDistribution Illuminant to plot the *colour rendering index*. \*\*kwargs : \*\* Keywords arguments. Returns ------- bool Definition success. Examples -------- >>> from colour import ILLUMINANTS_RELATIVE_SPDS >>> illuminant = ILLUMINANTS_RELATIVE_SPDS.get('F2') >>> colour_rendering_index_bars_plot(illuminant) # doctest: +SKIP True """ figure, axis = matplotlib.pyplot.subplots() cri, colour_rendering_indexes, additional_data = \ colour_rendering_index(illuminant, additional_data=True) colours = ( [[1] * 3] + [normalise(XYZ_to_sRGB(x.XYZ / 100)) for x in additional_data[0]]) x, y = tuple( zip(*sorted(colour_rendering_indexes.items(), key=lambda x: x[0]))) x, y = np.array([0] + list(x)), np.array([cri] + list(y)) positive = True if np.sign(min(y)) in (0, 1) else False width = 0.5 bars = pylab.bar(x, y, color=colours, width=width) y_ticks_steps = 10 pylab.yticks( range(0 if positive else -100, 100 + y_ticks_steps, y_ticks_steps)) pylab.xticks(x + width / 2, ['Ra'] + ['R{0}'.format(index) for index in x[1:]]) def label_bars(bars): """ Add labels above given bars. """ for bar in bars: y = bar.get_y() height = bar.get_height() value = height if np.sign(y) in (0, 1) else -height axis.text(bar.get_x() + bar.get_width() / 2, 0.025 * height + height + y, '{0:.1f}'.format(value), ha='center', va='bottom') label_bars(bars) settings = { 'title': 'Colour Rendering Index - {0}'.format(illuminant.name), 'grid': True, 'x_tighten': True, 'y_tighten': True, 'limits': [-width, 14 + width * 2, -10 if positive else -110, 110] } settings.update(kwargs) bounding_box(**settings) aspect(**settings) return display(**settings)
def colourspaces_CIE_1931_chromaticity_diagram_plot( colourspaces=None, cmfs='CIE 1931 2 Degree Standard Observer', **kwargs): """ Plots given colourspaces in *CIE 1931 Chromaticity Diagram*. Parameters ---------- colourspaces : list, optional Colourspaces to plot. cmfs : unicode, optional Standard observer colour matching functions used for diagram bounds. \*\*kwargs : \*\* Keywords arguments. Returns ------- bool Definition success. Examples -------- >>> csps = ['sRGB', 'ACES RGB'] >>> colourspaces_CIE_1931_chromaticity_diagram_plot(csps) # doctest: +SKIP True """ if colourspaces is None: colourspaces = ('sRGB', 'ACES RGB', 'Pointer Gamut') cmfs, name = get_cmfs(cmfs), cmfs settings = {'title': '{0} - {1}'.format(', '.join(colourspaces), name), 'standalone': False} settings.update(kwargs) if not CIE_1931_chromaticity_diagram_plot(**settings): return x_limit_min, x_limit_max = [-0.1], [0.9] y_limit_min, y_limit_max = [-0.1], [0.9] for colourspace in colourspaces: if colourspace == 'Pointer Gamut': x, y = tuple(zip(*POINTER_GAMUT_DATA)) pylab.plot(x, y, label='Pointer Gamut', color='0.95', linewidth=2) pylab.plot([x[-1], x[0]], [y[-1], y[0]], color='0.95', linewidth=2) else: colourspace, name = get_RGB_colourspace( colourspace), colourspace random_colour = lambda: float(random.randint(64, 224)) / 255 r, g, b = random_colour(), random_colour(), random_colour() primaries = colourspace.primaries whitepoint = colourspace.whitepoint pylab.plot([whitepoint[0], whitepoint[0]], [whitepoint[1], whitepoint[1]], color=(r, g, b), label=colourspace.name, linewidth=2) pylab.plot([whitepoint[0], whitepoint[0]], [whitepoint[1], whitepoint[1]], 'o', color=(r, g, b), linewidth=2) pylab.plot([primaries[0, 0], primaries[1, 0]], [primaries[0, 1], primaries[1, 1]], 'o-', color=(r, g, b), linewidth=2) pylab.plot([primaries[1, 0], primaries[2, 0]], [primaries[1, 1], primaries[2, 1]], 'o-', color=(r, g, b), linewidth=2) pylab.plot([primaries[2, 0], primaries[0, 0]], [primaries[2, 1], primaries[0, 1]], 'o-', color=(r, g, b), linewidth=2) x_limit_min.append(np.amin(primaries[:, 0])) y_limit_min.append(np.amin(primaries[:, 1])) x_limit_max.append(np.amax(primaries[:, 0])) y_limit_max.append(np.amax(primaries[:, 1])) settings.update({'legend': True, 'legend_location': 'upper right', 'x_tighten': True, 'y_tighten': True, 'limits': [min(x_limit_min), max(x_limit_max), min(y_limit_min), max(y_limit_max)], 'margins': [-0.05, 0.05, -0.05, 0.05], 'standalone': True}) bounding_box(**settings) aspect(**settings) return display(**settings)
def CIE_1931_chromaticity_diagram_colours_plot( surface=1.25, spacing=0.00075, cmfs='CIE 1931 2 Degree Standard Observer', **kwargs): """ Plots the *CIE 1931 Chromaticity Diagram* colours. Parameters ---------- surface : numeric, optional Generated markers surface. spacing : numeric, optional Spacing between markers. cmfs : unicode, optional Standard observer colour matching functions used for diagram bounds. \*\*kwargs : \*\* Keywords arguments. Returns ------- bool Definition success. Examples -------- >>> CIE_1931_chromaticity_diagram_colours_plot() # doctest: +SKIP True """ cmfs, name = get_cmfs(cmfs), cmfs illuminant = ILLUMINANTS.get( 'CIE 1931 2 Degree Standard Observer').get('E') XYZs = [value for key, value in cmfs] x, y = tuple(zip(*([XYZ_to_xy(x) for x in XYZs]))) path = matplotlib.path.Path(tuple(zip(x, y))) x_dot, y_dot, colours = [], [], [] for i in np.arange(0, 1, spacing): for j in np.arange(0, 1, spacing): if path.contains_path(matplotlib.path.Path([[i, j], [i, j]])): x_dot.append(i) y_dot.append(j) XYZ = xy_to_XYZ((i, j)) RGB = normalise(XYZ_to_sRGB(XYZ, illuminant)) colours.append(RGB) pylab.scatter(x_dot, y_dot, color=colours, s=surface) settings = {'no_ticks': True, 'bounding_box': [0, 1, 0, 1], 'bbox_inches': 'tight', 'pad_inches': 0} settings.update(kwargs) bounding_box(**settings) aspect(**settings) return display(**settings)
def CIE_1931_chromaticity_diagram_plot( cmfs='CIE 1931 2 Degree Standard Observer', **kwargs): """ Plots the *CIE 1931 Chromaticity Diagram*. Parameters ---------- cmfs : unicode, optional Standard observer colour matching functions used for diagram bounds. \*\*kwargs : \*\* Keywords arguments. Returns ------- bool Definition success. Examples -------- >>> CIE_1931_chromaticity_diagram_plot() # doctest: +SKIP True """ cmfs, name = get_cmfs(cmfs), cmfs image = matplotlib.image.imread( os.path.join(PLOTTING_RESOURCES_DIRECTORY, 'CIE_1931_Chromaticity_Diagram_{0}_Small.png'.format( cmfs.name.replace(' ', '_')))) pylab.imshow(image, interpolation='nearest', extent=(0, 1, 0, 1)) labels = ( [390, 460, 470, 480, 490, 500, 510, 520, 540, 560, 580, 600, 620, 700]) wavelengths = cmfs.wavelengths equal_energy = np.array([1 / 3] * 2) XYZs = [value for key, value in cmfs] x, y = tuple(zip(*([XYZ_to_xy(x) for x in XYZs]))) wavelengths_chromaticity_coordinates = dict( tuple(zip(wavelengths, tuple(zip(x, y))))) pylab.plot(x, y, color='black', linewidth=2) pylab.plot((x[-1], x[0]), (y[-1], y[0]), color='black', linewidth=2) for label in labels: x, y = wavelengths_chromaticity_coordinates.get(label) pylab.plot(x, y, 'o', color='black', linewidth=2) index = bisect.bisect(wavelengths, label) left = wavelengths[index - 1] if index >= 0 else wavelengths[index] right = (wavelengths[index] if index < len(wavelengths) else wavelengths[-1]) dx = (wavelengths_chromaticity_coordinates.get(right)[0] - wavelengths_chromaticity_coordinates.get(left)[0]) dy = (wavelengths_chromaticity_coordinates.get(right)[1] - wavelengths_chromaticity_coordinates.get(left)[1]) norme = lambda x: x / np.linalg.norm(x) xy = np.array([x, y]) direction = np.array((-dy, dx)) normal = (np.array((-dy, dx)) if np.dot(norme(xy - equal_energy), norme(direction)) > 0 else np.array((dy, -dx))) normal = norme(normal) normal /= 25 pylab.plot([x, x + normal[0] * 0.75], [y, y + normal[1] * 0.75], color='black', linewidth=1.5) pylab.text(x + normal[0], y + normal[1], label, clip_on=True, ha='left' if normal[0] >= 0 else 'right', va='center', fontdict={'size': 'small'}) settings = { 'title': 'CIE 1931 Chromaticity Diagram - {0}'.format(name), 'x_label': 'CIE x', 'y_label': 'CIE y', 'x_ticker': True, 'y_ticker': True, 'grid': True, 'bounding_box': [-0.1, 0.9, -0.1, 0.9], 'bbox_inches': 'tight', 'pad_inches': 0} settings.update(kwargs) bounding_box(**settings) aspect(**settings) return display(**settings)
def multi_cmfs_plot(cmfss=None, **kwargs): """ Plots given colour matching functions. Parameters ---------- cmfss : array_like, optional Colour matching functions to plot. \*\*kwargs : \*\* Keywords arguments. Returns ------- bool Definition success. Examples -------- >>> cmfss = [ ... 'CIE 1931 2 Degree Standard Observer', ... 'CIE 1964 10 Degree Standard Observer'] >>> multi_cmfs_plot(cmfss) # doctest: +SKIP True """ if cmfss is None: cmfss = ('CIE 1931 2 Degree Standard Observer', 'CIE 1964 10 Degree Standard Observer') x_limit_min, x_limit_max, y_limit_min, y_limit_max = [], [], [], [] for axis, rgb in (('x', [1, 0, 0]), ('y', [0, 1, 0]), ('z', [0, 0, 1])): for i, cmfs in enumerate(cmfss): cmfs, name = get_cmfs(cmfs), cmfs rgb = [reduce(lambda y, _: y * 0.5, range(i), x) for x in rgb] wavelengths, values = tuple( zip(*[(key, value) for key, value in getattr(cmfs, axis)])) shape = cmfs.shape x_limit_min.append(shape.start) x_limit_max.append(shape.end) y_limit_min.append(min(values)) y_limit_max.append(max(values)) pylab.plot(wavelengths, values, color=rgb, label=u'{0} - {1}'.format(cmfs.labels.get(axis), cmfs.name), linewidth=2) settings = { 'title': '{0} - Colour Matching Functions'.format(', '.join(cmfss)), 'x_label': u'Wavelength λ (nm)', 'y_label': 'Tristimulus Values', 'x_tighten': True, 'legend': True, 'legend_location': 'upper right', 'x_ticker': True, 'y_ticker': True, 'grid': True, 'y_axis_line': True, 'limits': [ min(x_limit_min), max(x_limit_max), min(y_limit_min), max(y_limit_max) ] } settings.update(kwargs) bounding_box(**settings) aspect(**settings) return display(**settings)
def multi_spd_plot(spds, cmfs='CIE 1931 2 Degree Standard Observer', use_spds_colours=False, normalise_spds_colours=False, **kwargs): """ Plots given spectral power distributions. Parameters ---------- spds : list, optional Spectral power distributions to plot. cmfs : unicode, optional Standard observer colour matching functions used for spectrum creation. use_spds_colours : bool, optional Use spectral power distributions colours. normalise_spds_colours : bool Should spectral power distributions colours normalised. \*\*kwargs : \*\* Keywords arguments. Returns ------- bool Definition success. Examples -------- >>> from colour import SpectralPowerDistribution >>> data1 = {400: 0.0641, 420: 0.0645, 440: 0.0562} >>> data2 = {400: 0.134, 420: 0.789, 440: 1.289} >>> spd1 = SpectralPowerDistribution('Custom1', data1) >>> spd2 = SpectralPowerDistribution('Custom2', data2) >>> multi_spd_plot([spd1, spd2]) # doctest: +SKIP True """ cmfs, name = get_cmfs(cmfs), cmfs if use_spds_colours: illuminant = ILLUMINANTS_RELATIVE_SPDS.get('D65') x_limit_min, x_limit_max, y_limit_min, y_limit_max = [], [], [], [] for spd in spds: wavelengths, values = tuple(zip(*[(key, value) for key, value in spd])) shape = spd.shape x_limit_min.append(shape.start) x_limit_max.append(shape.end) y_limit_min.append(min(values)) y_limit_max.append(max(values)) matplotlib.pyplot.rc("axes", color_cycle=["r", "g", "b", "y"]) if use_spds_colours: XYZ = spectral_to_XYZ(spd, cmfs, illuminant) / 100 if normalise_spds_colours: XYZ = normalise(XYZ, clip=False) RGB = np.clip(XYZ_to_sRGB(XYZ), 0, 1) pylab.plot(wavelengths, values, color=RGB, label=spd.name, linewidth=2) else: pylab.plot(wavelengths, values, label=spd.name, linewidth=2) settings = { 'x_label': u'Wavelength λ (nm)', 'y_label': 'Spectral Power Distribution', 'x_tighten': True, 'legend': True, 'legend_location': 'upper left', 'x_ticker': True, 'y_ticker': True, 'limits': [ min(x_limit_min), max(x_limit_max), min(y_limit_min), max(y_limit_max) ] } settings.update(kwargs) bounding_box(**settings) aspect(**settings) return display(**settings)
def CIE_1931_chromaticity_diagram_colours_plot( surface=1.25, spacing=0.00075, cmfs='CIE 1931 2 Degree Standard Observer', **kwargs): """ Plots the *CIE 1931 Chromaticity Diagram* colours. Parameters ---------- surface : numeric, optional Generated markers surface. spacing : numeric, optional Spacing between markers. cmfs : unicode, optional Standard observer colour matching functions used for diagram bounds. \*\*kwargs : \*\* Keywords arguments. Returns ------- bool Definition success. Examples -------- >>> CIE_1931_chromaticity_diagram_colours_plot() # doctest: +SKIP True """ cmfs, name = get_cmfs(cmfs), cmfs illuminant = ILLUMINANTS.get('CIE 1931 2 Degree Standard Observer').get( 'E') XYZs = [value for key, value in cmfs] x, y = tuple(zip(*([XYZ_to_xy(x) for x in XYZs]))) path = matplotlib.path.Path(tuple(zip(x, y))) x_dot, y_dot, colours = [], [], [] for i in np.arange(0, 1, spacing): for j in np.arange(0, 1, spacing): if path.contains_path(matplotlib.path.Path([[i, j], [i, j]])): x_dot.append(i) y_dot.append(j) XYZ = xy_to_XYZ((i, j)) RGB = normalise(XYZ_to_sRGB(XYZ, illuminant)) colours.append(RGB) pylab.scatter(x_dot, y_dot, color=colours, s=surface) settings = { 'no_ticks': True, 'bounding_box': [0, 1, 0, 1], 'bbox_inches': 'tight', 'pad_inches': 0 } settings.update(kwargs) bounding_box(**settings) aspect(**settings) return display(**settings)
def CIE_1976_UCS_chromaticity_diagram_plot( cmfs='CIE 1931 2 Degree Standard Observer', **kwargs): """ Plots the *CIE 1976 UCS Chromaticity Diagram*. Parameters ---------- cmfs : unicode, optional Standard observer colour matching functions used for diagram bounds. \*\*kwargs : \*\* Keywords arguments. Returns ------- bool Definition success. Examples -------- >>> CIE_1976_UCS_chromaticity_diagram_plot() # doctest: +SKIP True """ cmfs, name = get_cmfs(cmfs), cmfs image = matplotlib.image.imread( os.path.join( PLOTTING_RESOURCES_DIRECTORY, 'CIE_1976_UCS_Chromaticity_Diagram_{0}_Small.png'.format( cmfs.name.replace(' ', '_')))) pylab.imshow(image, interpolation='nearest', extent=(0, 1, 0, 1)) labels = [ 420, 430, 440, 450, 460, 470, 480, 490, 500, 510, 520, 530, 540, 550, 560, 570, 580, 590, 600, 610, 620, 630, 640, 680 ] wavelengths = cmfs.wavelengths equal_energy = np.array([1 / 3] * 2) illuminant = ILLUMINANTS.get('CIE 1931 2 Degree Standard Observer').get( 'D50') Luvs = [XYZ_to_Luv(value, illuminant) for key, value in cmfs] u, v = tuple(zip(*([Luv_to_uv(x) for x in Luvs]))) wavelengths_chromaticity_coordinates = dict( zip(wavelengths, tuple(zip(u, v)))) pylab.plot(u, v, color='black', linewidth=2) pylab.plot((u[-1], u[0]), (v[-1], v[0]), color='black', linewidth=2) for label in labels: u, v = wavelengths_chromaticity_coordinates.get(label) pylab.plot(u, v, 'o', color='black', linewidth=2) index = bisect.bisect(wavelengths, label) left = wavelengths[index - 1] if index >= 0 else wavelengths[index] right = (wavelengths[index] if index < len(wavelengths) else wavelengths[-1]) dx = (wavelengths_chromaticity_coordinates.get(right)[0] - wavelengths_chromaticity_coordinates.get(left)[0]) dy = (wavelengths_chromaticity_coordinates.get(right)[1] - wavelengths_chromaticity_coordinates.get(left)[1]) norme = lambda x: x / np.linalg.norm(x) uv = np.array([u, v]) direction = np.array((-dy, dx)) normal = (np.array( (-dy, dx)) if np.dot(norme(uv - equal_energy), norme(direction)) > 0 else np.array((dy, -dx))) normal = norme(normal) normal /= 25 pylab.plot([u, u + normal[0] * 0.75], [v, v + normal[1] * 0.75], color='black', linewidth=1.5) pylab.text(u + normal[0], v + normal[1], label, clip_on=True, ha='left' if normal[0] >= 0 else 'right', va='center', fontdict={'size': 'small'}) settings = { 'title': 'CIE 1976 UCS Chromaticity Diagram - {0}'.format(name), 'x_label': 'CIE u"', 'y_label': 'CIE v"', 'x_ticker': True, 'y_ticker': True, 'grid': True, 'bounding_box': [-0.1, .7, -.1, .7], 'bbox_inches': 'tight', 'pad_inches': 0 } settings.update(kwargs) bounding_box(**settings) aspect(**settings) return display(**settings)
def CIE_1931_chromaticity_diagram_plot( cmfs='CIE 1931 2 Degree Standard Observer', **kwargs): """ Plots the *CIE 1931 Chromaticity Diagram*. Parameters ---------- cmfs : unicode, optional Standard observer colour matching functions used for diagram bounds. \*\*kwargs : \*\* Keywords arguments. Returns ------- bool Definition success. Examples -------- >>> CIE_1931_chromaticity_diagram_plot() # doctest: +SKIP True """ cmfs, name = get_cmfs(cmfs), cmfs image = matplotlib.image.imread( os.path.join( PLOTTING_RESOURCES_DIRECTORY, 'CIE_1931_Chromaticity_Diagram_{0}_Small.png'.format( cmfs.name.replace(' ', '_')))) pylab.imshow(image, interpolation='nearest', extent=(0, 1, 0, 1)) labels = ([ 390, 460, 470, 480, 490, 500, 510, 520, 540, 560, 580, 600, 620, 700 ]) wavelengths = cmfs.wavelengths equal_energy = np.array([1 / 3] * 2) XYZs = [value for key, value in cmfs] x, y = tuple(zip(*([XYZ_to_xy(x) for x in XYZs]))) wavelengths_chromaticity_coordinates = dict( tuple(zip(wavelengths, tuple(zip(x, y))))) pylab.plot(x, y, color='black', linewidth=2) pylab.plot((x[-1], x[0]), (y[-1], y[0]), color='black', linewidth=2) for label in labels: x, y = wavelengths_chromaticity_coordinates.get(label) pylab.plot(x, y, 'o', color='black', linewidth=2) index = bisect.bisect(wavelengths, label) left = wavelengths[index - 1] if index >= 0 else wavelengths[index] right = (wavelengths[index] if index < len(wavelengths) else wavelengths[-1]) dx = (wavelengths_chromaticity_coordinates.get(right)[0] - wavelengths_chromaticity_coordinates.get(left)[0]) dy = (wavelengths_chromaticity_coordinates.get(right)[1] - wavelengths_chromaticity_coordinates.get(left)[1]) norme = lambda x: x / np.linalg.norm(x) xy = np.array([x, y]) direction = np.array((-dy, dx)) normal = (np.array( (-dy, dx)) if np.dot(norme(xy - equal_energy), norme(direction)) > 0 else np.array((dy, -dx))) normal = norme(normal) normal /= 25 pylab.plot([x, x + normal[0] * 0.75], [y, y + normal[1] * 0.75], color='black', linewidth=1.5) pylab.text(x + normal[0], y + normal[1], label, clip_on=True, ha='left' if normal[0] >= 0 else 'right', va='center', fontdict={'size': 'small'}) settings = { 'title': 'CIE 1931 Chromaticity Diagram - {0}'.format(name), 'x_label': 'CIE x', 'y_label': 'CIE y', 'x_ticker': True, 'y_ticker': True, 'grid': True, 'bounding_box': [-0.1, 0.9, -0.1, 0.9], 'bbox_inches': 'tight', 'pad_inches': 0 } settings.update(kwargs) bounding_box(**settings) aspect(**settings) return display(**settings)
def colour_checker_plot(colour_checker='ColorChecker 2005', **kwargs): """ Plots given colour checker. Parameters ---------- colour_checker : unicode, optional Color checker name. \*\*kwargs : \*\* Keywords arguments. Returns ------- bool Definition success. Raises ------ KeyError If the given colour rendition chart is not found in the factory colour rendition charts. Examples -------- >>> colour_checker_plot() # doctest: +SKIP True """ colour_checker, name = COLOURCHECKERS.get(colour_checker), colour_checker if colour_checker is None: raise KeyError(('Colour checker "{0}" not found in ' 'factory colour checkers: "{1}".').format( name, sorted(COLOURCHECKERS.keys()))) _, data, illuminant = colour_checker colour_parameters = [] for index, label, x, y, Y in data: XYZ = xyY_to_XYZ((x, y, Y)) RGB = XYZ_to_sRGB(XYZ, illuminant) colour_parameters.append( colour_parameter(label.title(), np.clip(np.ravel(RGB), 0, 1))) background_colour = '0.1' matplotlib.pyplot.gca().patch.set_facecolor(background_colour) width = height = 1.0 spacing = 0.25 across = 6 settings = { 'standalone': False, 'width': width, 'height': height, 'spacing': spacing, 'across': across, 'text_size': 8, 'margins': [-0.125, 0.125, -0.5, 0.125] } settings.update(kwargs) multi_colour_plot(colour_parameters, **settings) text_x = width * (across / 2) + (across * (spacing / 2)) - spacing / 2 text_y = -(len(colour_parameters) / across + spacing / 2) pylab.text(text_x, text_y, '{0} - {1} - Colour Rendition Chart'.format( name, RGB_COLOURSPACES.get('sRGB').name), color='0.95', clip_on=True, ha='center') settings.update({ 'title': name, 'facecolor': background_colour, 'edgecolor': None, 'standalone': True }) bounding_box(**settings) aspect(**settings) return display(**settings)
def the_blue_sky_plot(cmfs='CIE 1931 2 Degree Standard Observer', **kwargs): """ Plots the blue sky. Parameters ---------- cmfs : unicode, optional Standard observer colour matching functions. \*\*kwargs : \*\* Keywords arguments. Returns ------- bool Definition success. Examples -------- >>> the_blue_sky_plot() # doctest: +SKIP True """ cmfs, name = get_cmfs(cmfs), cmfs ASTM_G_173_spd = ASTM_G_173_ETR.clone() rayleigh_spd = rayleigh_scattering_spd() ASTM_G_173_spd.align(rayleigh_spd.shape) spd = rayleigh_spd * ASTM_G_173_spd matplotlib.pyplot.subplots_adjust(hspace=0.4) matplotlib.pyplot.figure(1) matplotlib.pyplot.subplot(211) settings = { 'title': 'The Blue Sky - Synthetic Spectral Power Distribution', 'y_label': u'W / m-2 / nm-1', 'standalone': False } settings.update(kwargs) single_spd_plot(spd, name, **settings) matplotlib.pyplot.subplot(212) settings = { 'title': 'The Blue Sky - Colour', 'x_label': ('The sky is blue because molecules in the atmosphere ' 'scatter shorter wavelengths more than longer ones.\n' 'The synthetic spectral power distribution is computed as ' 'follows: ' '(ASTM G-173 ETR * Standard Air Rayleigh Scattering).'), 'y_label': '', 'aspect': None, 'standalone': False } blue_sky_color = XYZ_to_sRGB(spectral_to_XYZ(spd)) single_colour_plot(colour_parameter('', normalise(blue_sky_color)), **settings) settings = {'standalone': True} settings.update(kwargs) bounding_box(**settings) aspect(**settings) return display(**settings)
def colour_checker_plot(colour_checker='ColorChecker 2005', **kwargs): """ Plots given colour checker. Parameters ---------- colour_checker : unicode, optional Color checker name. \*\*kwargs : \*\* Keywords arguments. Returns ------- bool Definition success. Raises ------ KeyError If the given colour rendition chart is not found in the factory colour rendition charts. Examples -------- >>> colour_checker_plot() # doctest: +SKIP True """ colour_checker, name = COLOURCHECKERS.get(colour_checker), colour_checker if colour_checker is None: raise KeyError( ('Colour checker "{0}" not found in ' 'factory colour checkers: "{1}".').format( name, sorted(COLOURCHECKERS.keys()))) _, data, illuminant = colour_checker colour_parameters = [] for index, label, x, y, Y in data: XYZ = xyY_to_XYZ((x, y, Y)) RGB = XYZ_to_sRGB(XYZ, illuminant) colour_parameters.append( colour_parameter(label.title(), np.clip(np.ravel(RGB), 0, 1))) background_colour = '0.1' matplotlib.pyplot.gca().patch.set_facecolor(background_colour) width = height = 1.0 spacing = 0.25 across = 6 settings = {'standalone': False, 'width': width, 'height': height, 'spacing': spacing, 'across': across, 'text_size': 8, 'margins': [-0.125, 0.125, -0.5, 0.125]} settings.update(kwargs) multi_colour_plot(colour_parameters, **settings) text_x = width * (across / 2) + (across * (spacing / 2)) - spacing / 2 text_y = -(len(colour_parameters) / across + spacing / 2) pylab.text(text_x, text_y, '{0} - {1} - Colour Rendition Chart'.format( name, RGB_COLOURSPACES.get('sRGB').name), color='0.95', clip_on=True, ha='center') settings.update({'title': name, 'facecolor': background_colour, 'edgecolor': None, 'standalone': True}) bounding_box(**settings) aspect(**settings) return display(**settings)
def multi_lightness_function_plot(functions=None, **kwargs): """ Plots given *Lightness* functions. Parameters ---------- functions : array_like, optional *Lightness* functions to plot. \*\*kwargs : \*\* Keywords arguments. Returns ------- bool Definition success. Raises ------ KeyError If one of the given *Lightness* function is not found in the factory *Lightness* functions. Examples -------- >>> fs = ('CIE 1976', 'Wyszecki 1964') >>> multi_lightness_function_plot(fs) # doctest: +SKIP True """ if functions is None: functions = ('CIE 1976', 'Wyszecki 1964') samples = np.linspace(0, 100, 1000) for i, function in enumerate(functions): function, name = LIGHTNESS_METHODS.get(function), function if function is None: raise KeyError(('"{0}" "Lightness" function not found in factory ' '"Lightness" functions: "{1}".').format( name, sorted(LIGHTNESS_METHODS.keys()))) pylab.plot(samples, [function(x) for x in samples], label=u'{0}'.format(name), linewidth=2) settings = { 'title': '{0} - Lightness Functions'.format(', '.join(functions)), 'x_label': 'Luminance Y', 'y_label': 'Lightness L*', 'x_tighten': True, 'legend': True, 'legend_location': 'upper left', 'x_ticker': True, 'y_ticker': True, 'grid': True, 'limits': [0, 100, 0, 100] } settings.update(kwargs) bounding_box(**settings) aspect(**settings) return display(**settings)
def CIE_1976_UCS_chromaticity_diagram_plot( cmfs='CIE 1931 2 Degree Standard Observer', **kwargs): """ Plots the *CIE 1976 UCS Chromaticity Diagram*. Parameters ---------- cmfs : unicode, optional Standard observer colour matching functions used for diagram bounds. \*\*kwargs : \*\* Keywords arguments. Returns ------- bool Definition success. Examples -------- >>> CIE_1976_UCS_chromaticity_diagram_plot() # doctest: +SKIP True """ cmfs, name = get_cmfs(cmfs), cmfs image = matplotlib.image.imread( os.path.join(PLOTTING_RESOURCES_DIRECTORY, 'CIE_1976_UCS_Chromaticity_Diagram_{0}_Small.png'.format( cmfs.name.replace(' ', '_')))) pylab.imshow(image, interpolation='nearest', extent=(0, 1, 0, 1)) labels = [420, 430, 440, 450, 460, 470, 480, 490, 500, 510, 520, 530, 540, 550, 560, 570, 580, 590, 600, 610, 620, 630, 640, 680] wavelengths = cmfs.wavelengths equal_energy = np.array([1 / 3] * 2) illuminant = ILLUMINANTS.get( 'CIE 1931 2 Degree Standard Observer').get('D50') Luvs = [XYZ_to_Luv(value, illuminant) for key, value in cmfs] u, v = tuple(zip(*([Luv_to_uv(x) for x in Luvs]))) wavelengths_chromaticity_coordinates = dict(zip(wavelengths, tuple(zip(u, v)))) pylab.plot(u, v, color='black', linewidth=2) pylab.plot((u[-1], u[0]), (v[-1], v[0]), color='black', linewidth=2) for label in labels: u, v = wavelengths_chromaticity_coordinates.get(label) pylab.plot(u, v, 'o', color='black', linewidth=2) index = bisect.bisect(wavelengths, label) left = wavelengths[index - 1] if index >= 0 else wavelengths[index] right = (wavelengths[index] if index < len(wavelengths) else wavelengths[-1]) dx = (wavelengths_chromaticity_coordinates.get(right)[0] - wavelengths_chromaticity_coordinates.get(left)[0]) dy = (wavelengths_chromaticity_coordinates.get(right)[1] - wavelengths_chromaticity_coordinates.get(left)[1]) norme = lambda x: x / np.linalg.norm(x) uv = np.array([u, v]) direction = np.array((-dy, dx)) normal = (np.array((-dy, dx)) if np.dot(norme(uv - equal_energy), norme(direction)) > 0 else np.array((dy, -dx))) normal = norme(normal) normal /= 25 pylab.plot([u, u + normal[0] * 0.75], [v, v + normal[1] * 0.75], color='black', linewidth=1.5) pylab.text(u + normal[0], v + normal[1], label, clip_on=True, ha='left' if normal[0] >= 0 else 'right', va='center', fontdict={'size': 'small'}) settings = { 'title': 'CIE 1976 UCS Chromaticity Diagram - {0}'.format(name), 'x_label': 'CIE u"', 'y_label': 'CIE v"', 'x_ticker': True, 'y_ticker': True, 'grid': True, 'bounding_box': [-0.1, .7, -.1, .7], 'bbox_inches': 'tight', 'pad_inches': 0} settings.update(kwargs) bounding_box(**settings) aspect(**settings) return display(**settings)
def blackbody_spectral_radiance_plot( temperature=3500, cmfs='CIE 1931 2 Degree Standard Observer', blackbody='VY Canis Major', **kwargs): """ Plots given blackbody spectral radiance. Parameters ---------- temperature : numeric, optional Blackbody temperature. cmfs : unicode, optional Standard observer colour matching functions. blackbody : unicode, optional Blackbody name. \*\*kwargs : \*\* Keywords arguments. Returns ------- bool Definition success. Examples -------- >>> blackbody_spectral_radiance_plot() # doctest: +SKIP True """ cmfs, name = get_cmfs(cmfs), cmfs matplotlib.pyplot.subplots_adjust(hspace=0.4) spd = blackbody_spd(temperature, cmfs.shape) matplotlib.pyplot.figure(1) matplotlib.pyplot.subplot(211) settings = { 'title': '{0} - Spectral Radiance'.format(blackbody), 'y_label': u'W / (sr m²) / m', 'standalone': False } settings.update(kwargs) single_spd_plot(spd, name, **settings) XYZ = spectral_to_XYZ(spd, cmfs) RGB = normalise(XYZ_to_sRGB(XYZ / 100)) matplotlib.pyplot.subplot(212) settings = { 'title': '{0} - Colour'.format(blackbody), 'x_label': '{0}K'.format(temperature), 'y_label': '', 'aspect': None, 'standalone': False } single_colour_plot(colour_parameter(name='', RGB=RGB), **settings) settings = {'standalone': True} settings.update(kwargs) bounding_box(**settings) aspect(**settings) return display(**settings)
def multi_spd_plot(spds, cmfs='CIE 1931 2 Degree Standard Observer', use_spds_colours=False, normalise_spds_colours=False, **kwargs): """ Plots given spectral power distributions. Parameters ---------- spds : list, optional Spectral power distributions to plot. cmfs : unicode, optional Standard observer colour matching functions used for spectrum creation. use_spds_colours : bool, optional Use spectral power distributions colours. normalise_spds_colours : bool Should spectral power distributions colours normalised. \*\*kwargs : \*\* Keywords arguments. Returns ------- bool Definition success. Examples -------- >>> from colour import SpectralPowerDistribution >>> data1 = {400: 0.0641, 420: 0.0645, 440: 0.0562} >>> data2 = {400: 0.134, 420: 0.789, 440: 1.289} >>> spd1 = SpectralPowerDistribution('Custom1', data1) >>> spd2 = SpectralPowerDistribution('Custom2', data2) >>> multi_spd_plot([spd1, spd2]) # doctest: +SKIP True """ cmfs, name = get_cmfs(cmfs), cmfs if use_spds_colours: illuminant = ILLUMINANTS_RELATIVE_SPDS.get('D65') x_limit_min, x_limit_max, y_limit_min, y_limit_max = [], [], [], [] for spd in spds: wavelengths, values = tuple(zip(*[(key, value) for key, value in spd])) shape = spd.shape x_limit_min.append(shape.start) x_limit_max.append(shape.end) y_limit_min.append(min(values)) y_limit_max.append(max(values)) matplotlib.pyplot.rc("axes", color_cycle=["r", "g", "b", "y"]) if use_spds_colours: XYZ = spectral_to_XYZ(spd, cmfs, illuminant) / 100 if normalise_spds_colours: XYZ = normalise(XYZ, clip=False) RGB = np.clip(XYZ_to_sRGB(XYZ), 0, 1) pylab.plot(wavelengths, values, color=RGB, label=spd.name, linewidth=2) else: pylab.plot(wavelengths, values, label=spd.name, linewidth=2) settings = {'x_label': u'Wavelength λ (nm)', 'y_label': 'Spectral Power Distribution', 'x_tighten': True, 'legend': True, 'legend_location': 'upper left', 'x_ticker': True, 'y_ticker': True, 'limits': [min(x_limit_min), max(x_limit_max), min(y_limit_min), max(y_limit_max)]} settings.update(kwargs) bounding_box(**settings) aspect(**settings) return display(**settings)
def multi_cmfs_plot(cmfss=None, **kwargs): """ Plots given colour matching functions. Parameters ---------- cmfss : array_like, optional Colour matching functions to plot. \*\*kwargs : \*\* Keywords arguments. Returns ------- bool Definition success. Examples -------- >>> cmfss = [ ... 'CIE 1931 2 Degree Standard Observer', ... 'CIE 1964 10 Degree Standard Observer'] >>> multi_cmfs_plot(cmfss) # doctest: +SKIP True """ if cmfss is None: cmfss = ('CIE 1931 2 Degree Standard Observer', 'CIE 1964 10 Degree Standard Observer') x_limit_min, x_limit_max, y_limit_min, y_limit_max = [], [], [], [] for axis, rgb in (('x', [1, 0, 0]), ('y', [0, 1, 0]), ('z', [0, 0, 1])): for i, cmfs in enumerate(cmfss): cmfs, name = get_cmfs(cmfs), cmfs rgb = [reduce(lambda y, _: y * 0.5, range(i), x) for x in rgb] wavelengths, values = tuple( zip(*[(key, value) for key, value in getattr(cmfs, axis)])) shape = cmfs.shape x_limit_min.append(shape.start) x_limit_max.append(shape.end) y_limit_min.append(min(values)) y_limit_max.append(max(values)) pylab.plot(wavelengths, values, color=rgb, label=u'{0} - {1}'.format( cmfs.labels.get(axis), cmfs.name), linewidth=2) settings = { 'title': '{0} - Colour Matching Functions'.format(', '.join(cmfss)), 'x_label': u'Wavelength λ (nm)', 'y_label': 'Tristimulus Values', 'x_tighten': True, 'legend': True, 'legend_location': 'upper right', 'x_ticker': True, 'y_ticker': True, 'grid': True, 'y_axis_line': True, 'limits': [min(x_limit_min), max(x_limit_max), min(y_limit_min), max(y_limit_max)]} settings.update(kwargs) bounding_box(**settings) aspect(**settings) return display(**settings)
def colour_rendering_index_bars_plot(illuminant, **kwargs): """ Plots the *colour rendering index* of given illuminant. Parameters ---------- illuminant : SpectralPowerDistribution Illuminant to plot the *colour rendering index*. \*\*kwargs : \*\* Keywords arguments. Returns ------- bool Definition success. Examples -------- >>> from colour import ILLUMINANTS_RELATIVE_SPDS >>> illuminant = ILLUMINANTS_RELATIVE_SPDS.get('F2') >>> colour_rendering_index_bars_plot(illuminant) # doctest: +SKIP True """ figure, axis = matplotlib.pyplot.subplots() cri, colour_rendering_indexes, additional_data = \ colour_rendering_index(illuminant, additional_data=True) colours = ([[1] * 3] + [normalise(XYZ_to_sRGB(x.XYZ / 100)) for x in additional_data[0]]) x, y = tuple(zip(*sorted(colour_rendering_indexes.items(), key=lambda x: x[0]))) x, y = np.array([0] + list(x)), np.array( [cri] + list(y)) positive = True if np.sign(min(y)) in (0, 1) else False width = 0.5 bars = pylab.bar(x, y, color=colours, width=width) y_ticks_steps = 10 pylab.yticks(range(0 if positive else -100, 100 + y_ticks_steps, y_ticks_steps)) pylab.xticks(x + width / 2, ['Ra'] + ['R{0}'.format(index) for index in x[1:]]) def label_bars(bars): """ Add labels above given bars. """ for bar in bars: y = bar.get_y() height = bar.get_height() value = height if np.sign(y) in (0, 1) else -height axis.text(bar.get_x() + bar.get_width() / 2, 0.025 * height + height + y, '{0:.1f}'.format(value), ha='center', va='bottom') label_bars(bars) settings = { 'title': 'Colour Rendering Index - {0}'.format(illuminant.name), 'grid': True, 'x_tighten': True, 'y_tighten': True, 'limits': [-width, 14 + width * 2, -10 if positive else -110, 110]} settings.update(kwargs) bounding_box(**settings) aspect(**settings) return display(**settings)
def the_blue_sky_plot( cmfs='CIE 1931 2 Degree Standard Observer', **kwargs): """ Plots the blue sky. Parameters ---------- cmfs : unicode, optional Standard observer colour matching functions. \*\*kwargs : \*\* Keywords arguments. Returns ------- bool Definition success. Examples -------- >>> the_blue_sky_plot() # doctest: +SKIP True """ cmfs, name = get_cmfs(cmfs), cmfs ASTM_G_173_spd = ASTM_G_173_ETR.clone() rayleigh_spd = rayleigh_scattering_spd() ASTM_G_173_spd.align(rayleigh_spd.shape) spd = rayleigh_spd * ASTM_G_173_spd matplotlib.pyplot.subplots_adjust(hspace=0.4) matplotlib.pyplot.figure(1) matplotlib.pyplot.subplot(211) settings = { 'title': 'The Blue Sky - Synthetic Spectral Power Distribution', 'y_label': u'W / m-2 / nm-1', 'standalone': False} settings.update(kwargs) single_spd_plot(spd, name, **settings) matplotlib.pyplot.subplot(212) settings = { 'title': 'The Blue Sky - Colour', 'x_label': ('The sky is blue because molecules in the atmosphere ' 'scatter shorter wavelengths more than longer ones.\n' 'The synthetic spectral power distribution is computed as ' 'follows: ' '(ASTM G-173 ETR * Standard Air Rayleigh Scattering).'), 'y_label': '', 'aspect': None, 'standalone': False} blue_sky_color = XYZ_to_sRGB(spectral_to_XYZ(spd)) single_colour_plot(colour_parameter('', normalise(blue_sky_color)), **settings) settings = {'standalone': True} settings.update(kwargs) bounding_box(**settings) aspect(**settings) return display(**settings)