def read(self): """ Reads and parses the spectral data XML file path. Returns ------- bool Definition success. Examples -------- >>> from os.path import dirname, join >>> directory = join(dirname(__file__), 'tests', 'resources') >>> sd = SpectralDistribution_IESTM2714( ... join(directory, 'Fluorescent.spdx')) >>> sd.read() True >>> sd.header.description 'Rare earth fluorescent lamp' >>> # Doctests ellipsis for Python 2.x compatibility. >>> sd[400] # doctest: +ELLIPSIS 0.0339999... """ formatter = './{{{0}}}{1}/{{{0}}}{2}' tree = ElementTree.parse(self._path) # nosec root = tree.getroot() namespace = re.match('{(.*)}', root.tag).group(1) self.name = os.path.splitext(os.path.basename(self._path))[0] iterator = root.iter for header_element in (self.header, self): mapping = header_element.mapping for specification in mapping.elements: element = root.find( formatter.format(namespace, mapping.element, specification.element)) if element is not None: setattr(header_element, specification.attribute, specification.read_conversion(element.text)) # Reading spectral data. wavelengths = [] values = [] for spectral_data in iterator('{{{0}}}{1}'.format( namespace, self.mapping.data.element)): wavelengths.append( DEFAULT_FLOAT_DTYPE( spectral_data.attrib[self.mapping.data.attribute])) values.append(DEFAULT_FLOAT_DTYPE(spectral_data.text)) self.wavelengths = wavelengths self.values = values return True
def load_fixtures(file_name, fixtures_directory='fixtures'): """ Loads the fixtures data with given name. Parameters ---------- file_name : unicode '.csv' fixture file name. fixtures_directory : unicode Relative directory path containing the '.csv' fixtures files. Returns ------- list Fixtures data as a *list* of *dict* where each *dict* is a fixture case. """ path = os.path.dirname(__file__) with open(os.path.join(path, fixtures_directory, file_name)) as in_file: result = [] for case_data in csv.DictReader(in_file): for key in case_data: try: case_data[key] = DEFAULT_FLOAT_DTYPE(case_data[key]) except ValueError: pass result.append(case_data) return result
def as_float(a): """ Converts given :math:`a` variable to *numeric* using the type defined by :attr:`colour.constant.DEFAULT_FLOAT_DTYPE` attribute. In the event where :math:`a` cannot be converted, it is converted to *ndarray* using the type defined by :attr:`colour.constant.DEFAULT_FLOAT_DTYPE` attribute. Parameters ---------- a : object Variable to convert. Returns ------- ndarray :math:`a` variable converted to *numeric*. Warnings -------- The behaviour of this definition is different than :func:`colour.utilities.as_numeric` definition when it comes to conversion failure: the former will forcibly convert :math:`a` variable to *ndarray* using the type defined by :attr:`colour.constant.DEFAULT_FLOAT_DTYPE` attribute while the later will pass the :math:`a` variable as is. Examples -------- >>> as_float(np.array([1])) 1.0 >>> as_float(np.arange(10)) array([ 0., 1., 2., 3., 4., 5., 6., 7., 8., 9.]) """ return DEFAULT_FLOAT_DTYPE(a)
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 read_spectral_data_from_csv_file(path, delimiter=',', fields=None, default=0): """ Reads the spectral data from given *CSV* file in the following form: 390, 4.15003E-04, 3.68349E-04, 9.54729E-03 395, 1.05192E-03, 9.58658E-04, 2.38250E-02 400, 2.40836E-03, 2.26991E-03, 5.66498E-02 ... 830, 9.74306E-07, 9.53411E-08, 0.00000 and returns it as an *OrderedDict* of *dict* as follows: OrderedDict([ ('field', {'wavelength': 'value', ..., 'wavelength': 'value'}), ..., ('field', {'wavelength': 'value', ..., 'wavelength': 'value'})]) Parameters ---------- path : unicode Absolute *CSV* file path. delimiter : unicode, optional *CSV* file content delimiter. fields : array_like, optional *CSV* file spectral data fields names. If no value is provided the first line of the file will be used as spectral data fields names. default : numeric, optional Default value for fields row with missing value. Returns ------- OrderedDict *CSV* file content. Raises ------ RuntimeError If the *CSV* spectral data file doesn't define the appropriate fields. Notes ----- - A *CSV* spectral data file should define at least define two fields: one for the wavelengths and one for the associated values of one spectral distribution. - If no value is provided for the fields names, the first line of the file will be used as spectral data fields names. Examples -------- >>> import os >>> from pprint import pprint >>> csv_file = os.path.join(os.path.dirname(__file__), 'tests', ... 'resources', 'colorchecker_n_ohta.csv') >>> sds_data = read_spectral_data_from_csv_file(csv_file) >>> pprint(list(sds_data.keys())) ['1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11', '12', '13', '14', '15', '16', '17', '18', '19', '20', '21', '22', '23', '24'] """ with open(path, 'rU') as csv_file: reader = csv.DictReader( csv_file, delimiter=str(delimiter), fieldnames=fields) if len(reader.fieldnames) == 1: raise RuntimeError(('A "CSV" spectral data file should define ' 'the following fields: ' '("wavelength", "field 1", ..., "field n")!')) wavelength = reader.fieldnames[0] fields = reader.fieldnames[1:] data = OrderedDict(zip(fields, ({} for _ in range(len(fields))))) for line in reader: for field in fields: try: value = DEFAULT_FLOAT_DTYPE(line[field]) except ValueError: value = default data[field][DEFAULT_FLOAT_DTYPE(line[wavelength])] = value return data
def read_sds_from_xrite_file(path): """ Reads the spectral data from given *X-Rite* file and returns it as an *OrderedDict* of :class:`colour.SpectralDistribution` classes. Parameters ---------- path : unicode Absolute *X-Rite* file path. Returns ------- OrderedDict :class:`colour.SpectralDistribution` classes of given *X-Rite* file. Notes ----- - This parser is minimalistic and absolutely not bullet proof. Examples -------- >>> import os >>> from pprint import pprint >>> xrite_file = os.path.join(os.path.dirname(__file__), 'tests', ... 'resources', ... 'xrite_digital_colour_checker.txt') >>> sds_data = read_sds_from_xrite_file(xrite_file) >>> pprint(list(sds_data.keys())) # doctest: +SKIP ['X1', 'X2', 'X3', 'X4', 'X5', 'X6', 'X7', 'X8', 'X9', 'X10'] """ with codecs.open(path, encoding=XRITE_FILE_ENCODING) as xrite_file: lines = xrite_file.read().strip().split('\n') xrite_sds = OrderedDict() is_spectral_data_format, is_spectral_data = False, False for line in lines: line = line.strip() if line == 'END_DATA_FORMAT': is_spectral_data_format = False if line == 'END_DATA': is_spectral_data = False if is_spectral_data_format: wavelengths = [ DEFAULT_FLOAT_DTYPE(x) for x in re.findall('nm(\\d+)', line) ] index = len(wavelengths) if is_spectral_data: tokens = line.split() values = [DEFAULT_FLOAT_DTYPE(x) for x in tokens[-index:]] xrite_sds[tokens[1]] = (SpectralDistribution( dict(zip(wavelengths, values)), name=tokens[1])) if line == 'BEGIN_DATA_FORMAT': is_spectral_data_format = True if line == 'BEGIN_DATA': is_spectral_data = True return xrite_sds