def load(self): """ Syncs, parses, converts and returns the *Jiang et al. (2013)* *Camera Spectral Sensitivity Database* dataset content. Returns ------- OrderedDict *Jiang et al. (2013)* *Camera Spectral Sensitivity Database* dataset content. Examples -------- >>> from colour_datasets.utilities import suppress_stdout >>> dataset = DatasetLoader_Jiang2013() >>> with suppress_stdout(): ... dataset.load() >>> len(dataset.content.keys()) 28 """ super(DatasetLoader_Jiang2013, self).sync() shape = SpectralShape(400, 720, 10) self._content = OrderedDict() database_path = os.path.join(self.record.repository, 'dataset', 'camspec_database.txt') with codecs.open(database_path, encoding='utf-8') as database_file: lines = filter(None, (line.strip() for line in database_file.readlines())) camera = None for line in lines: if re.match('[a-zA-Z]+', line): camera = line self._content[camera] = [] continue self._content[camera].extend( [float(value) for value in line.split('\t')]) for camera, values in self._content.items(): self._content[camera] = RGB_CameraSensitivities(np.transpose( as_float_array(values).reshape([3, 33])), shape.range(), name=camera) return self._content
def from_file(self, path): """ Loads a dataset from an .npz file. Parameters ========== path : unicode Path to file. Raises ====== ValueError, KeyError Raised when loading the file succeeded but it did not contain the expected data. """ npz = np.load(path, allow_pickle=False) if not isinstance(npz, np.lib.npyio.NpzFile): raise ValueError('the loaded file is not an .npz file') start, end, interval = npz['shape'] self.shape = SpectralShape(start, end, interval) self.basis_functions = npz['basis_functions'] self.means = npz['means'] self.selector_array = npz['selector_array'] n, three, m = self.basis_functions.shape if (three != 3 or self.means.shape != (n, m) or self.selector_array.shape[1] != 4): raise ValueError('array shapes are not correct, the file could be ' 'corrupted or in a wrong format')
def test_load(self): """ Tests :func:`colour_datasets.loaders.labsphere2019.\ DatasetLoader_Labsphere2019.load` method. """ dataset = DatasetLoader_Labsphere2019() self.assertEqual(sorted(dataset.load().keys()), ['Labsphere SRS-99-020']) self.assertEqual(dataset.content['Labsphere SRS-99-020'].shape, SpectralShape(250, 2500, 1))
def test_load(self): """ Tests :func:`colour_datasets.loaders.brendel2020.\ DatasetLoader_Brendel2020.load` method. """ dataset = DatasetLoader_Brendel2020() self.assertEqual(len(dataset.load()), 29) self.assertEqual( dataset.content['556nm - LED 11 - Brendel (2020)'].shape, SpectralShape(350, 700, 2))
def test_load(self): """ Tests :func:`colour_datasets.loaders.jiang2013.\ DatasetLoader_Jiang2013.load` method. """ dataset = DatasetLoader_Jiang2013() self.assertEqual(sorted(dataset.load().keys()), [ 'Canon 1DMarkIII', 'Canon 20D', 'Canon 300D', 'Canon 40D', 'Canon 500D', 'Canon 50D', 'Canon 5DMarkII', 'Canon 600D', 'Canon 60D', 'Hasselblad H2', 'Nikon D200', 'Nikon D3', 'Nikon D300s', 'Nikon D3X', 'Nikon D40', 'Nikon D50', 'Nikon D5100', 'Nikon D700', 'Nikon D80', 'Nikon D90', 'Nokia N900', 'Olympus E-PL2', 'Pentax K-5', 'Pentax Q', 'Phase One', 'Point Grey Grasshopper 50S5C', 'Point Grey Grasshopper2 14S5C', 'SONY NEX-5N' ]) self.assertEqual(dataset.content['Canon 1DMarkIII'].shape, SpectralShape(400, 720, 10))
def load(self): """ Syncs, parses, converts and returns the *Brendel (2020)* *Measured Commercial LED Spectra* dataset content. Returns ------- OrderedDict *Brendel (2020)* *Measured Commercial LED Spectra* dataset content. Examples -------- >>> from colour_datasets.utilities import suppress_stdout >>> dataset = DatasetLoader_Brendel2020() >>> with suppress_stdout(): ... dataset.load() >>> len(dataset.content.keys()) 29 """ super(DatasetLoader_Brendel2020, self).sync() self._content = OrderedDict() wavelengths = SpectralShape(350, 700, 2).range() csv_path = os.path.join(self.record.repository, 'dataset', 'led_spd_350_700.csv') for i, values in enumerate( np.loadtxt(csv_path, delimiter=',', skiprows=1)): peak = as_int(wavelengths[np.argmax(values)]) name = '{0}nm - LED {1} - Brendel (2020)'.format(peak, i) self._content[name] = SpectralDistribution( values, wavelengths, name=name, interpolator=LinearInterpolator) return self._content
setattr(module, dataset_loader_attribute, dataset_loader_class()) if load: getattr(module, dataset_loader_attribute).load() return getattr(module, dataset_loader_attribute) # TODO: Implement support for *Natural Colors*: # https://sandbox.zenodo.org/record/315640 # http://www.uef.fi/web/spectral/natural-colors DATA_KUOPIO_UNIVERSITY = { '3269912': [ 'Munsell Colors Matt (Spectrofotometer Measured)', 'Hauta-Kasari', { ('munsell380_800_1_mat', 'munsell380_800_1.mat'): MatFileMetadata_KuopioUniversity('munsell', SpectralShape(380, 800, 1), True, 'S') } ], '3269914': [ 'Munsell Colors Matt (AOTF Measured)', 'Hauta-Kasaria', { ('munsell400_700_5_mat', 'munsell400_700_5.mat'): MatFileMetadata_KuopioUniversity('munsell', SpectralShape(400, 700, 5), True, 'S') } ], '3269916': [ 'Munsell Colors Glossy (Spectrofotometer Measured)', 'Haanpalo', { ('munsell400_700_10_mat', 'munsell400_700_10.mat'): MatFileMetadata_KuopioUniversity('munsell',
import os import matplotlib.pyplot as plt import tqdm from colour import SpectralShape, COLOURCHECKER_SDS, sd_to_XYZ from otsu2018 import load_Otsu2018_spectra, Otsu2018Tree if __name__ == '__main__': print('Loading spectral data...') sds = load_Otsu2018_spectra('CommonData/spectrum_m.csv', every_nth=100) shape = SpectralShape(380, 730, 10) print('Initializing the tree...') tree = Otsu2018Tree(sds, shape) print('Clustering...') before = tree.total_reconstruction_error() tree.optimise(progress_bar=tqdm.tqdm) after = tree.total_reconstruction_error() print('Error before: %g' % before) print('Error after: %g' % after) print('Saving the dataset...') if not os.path.exists('datasets'): os.makedirs('datasets') data = tree.to_dataset() data.to_file('datasets/otsu2018.npz') data.to_Python_file('datasets/otsu2018.py')
def reshape(spd, min=360, max=780, interval=1): spd = spd.extrapolate(SpectralShape(start=min, end=max)) spd = spd.interpolate(SpectralShape(interval=interval)) return spd
def test_load(self): """ Tests :func:`colour_datasets.loaders.asano2015.\ DatasetLoader_Asano2015.load` method. """ dataset = DatasetLoader_Asano2015() self.assertEqual(sorted(dataset.load().keys()), ['Categorical Observers', 'Colour Normal Observers']) self.assertEqual( dataset.content['Categorical Observers'][1].XYZ_2.shape, SpectralShape(390, 780, 5)) np.testing.assert_almost_equal( dataset.content['Categorical Observers'][1].XYZ_2[390], np.array([ 0.003774670254076, 0.000033807427536, 0.017705556255144, ]), decimal=7) np.testing.assert_almost_equal( dataset.content['Categorical Observers'][10].LMS_10[780], np.array([ 0.000101460310461, 9.67131698024335e-06, 0.000000000000000, ]), decimal=7) self.assertAlmostEqual(dataset.content['Categorical Observers'] [5].parameters['Shift in S [nm]'], 0.233255808, places=7) self.assertEqual( dataset.content['Colour Normal Observers'][1].XYZ_2.shape, SpectralShape(390, 780, 5)) np.testing.assert_almost_equal( dataset.content['Colour Normal Observers'][1].XYZ_2[390], np.array([ 0.001627436785620, 0.000021871064674, 0.007492391403616, ]), decimal=7) np.testing.assert_almost_equal( dataset.content['Colour Normal Observers'][10].LMS_10[780], np.array([ 0.000092440377130, 6.93870146211108e-06, 0.000000000000000, ]), decimal=7) self.assertAlmostEqual(dataset.content['Colour Normal Observers'] [5].parameters['Shift in S [nm]'], 0.000649602695013, places=7) self.assertEqual( dataset.content['Colour Normal Observers'][151].others['Location'], 'Darmstadt')
def parse_workbook_Asano2015(workbook, template, observers=(1, 10)): """ Parses given *Asano (2015)* *Observer Function Database* workbook. Parameters ---------- workbook : unicode *Asano (2015)* *Observer Function Database* workbook path. template : unicode Template used to create the *CMFS* names. observers : tuple, optional Observers range. Returns ------- OrderedDict *Asano (2015)* *Observer Function Database* workbook observer data. """ workbook = xlrd.open_workbook(workbook) # "CIE XYZ" and "LMS" CMFS. column_in, column_out = (index_to_column(observers[0] + 1), index_to_column(observers[1] + 1)) shape = SpectralShape(390, 780, 5) wavelengths = shape.range() data = OrderedDict() for i, cmfs in enumerate([(XYZ_ColourMatchingFunctions, 'XYZ'), (LMS_ConeFundamentals, 'LMS')]): for j, degree in enumerate([(2, '2$^\\circ$'), (10, '10$^\\circ$')]): sheet = workbook.sheet_by_index(j + (i * 2)) x = np.transpose( cell_range_values( sheet, '{0}3:{1}81'.format(column_in, column_out))) y = np.transpose( cell_range_values( sheet, '{0}82:{1}160'.format(column_in, column_out))) z = np.transpose( cell_range_values( sheet, '{0}161:{1}239'.format(column_in, column_out))) for k in range(observers[1]): observer = k + 1 rgb = tstack([x[k], y[k], z[k]]) if data.get(observer) is None: data[observer] = OrderedDict() key = '{0}_{1}'.format(cmfs[1], degree[0]) data[observer][key] = cmfs[0]( rgb, domain=wavelengths, name=template.format(degree[0], observer, cmfs[1]), strict_name=template.format(degree[0], observer, cmfs[1])) # Parameters column_in, column_out = (index_to_column(observers[0] - 1), index_to_column(observers[1])) values = np.transpose( cell_range_values(workbook.sheet_by_index(4), '{0}2:{1}10'.format(column_in, column_out))) header, values = values[0], values[1:] for i in range(observers[1]): observer = i + 1 data[observer]['parameters'] = OrderedDict( zip(header, as_float_array(values[i]))) return data
sd = SpectralDistribution(mean, OTSU_2018_SPECTRAL_SHAPE.range()) XYZ_mu = sd_to_XYZ(sd, illuminant=illuminant) / 100 weights = np.dot(M_inverse, XYZ - XYZ_mu) recovered_sd = np.dot(weights, basis_functions) + mean if clip: recovered_sd = np.clip(recovered_sd, 0, 1) return SpectralDistribution(recovered_sd, OTSU_2018_SPECTRAL_SHAPE.range()) if __name__ == '__main__': print('Loading spectral data...') data = load_Otsu2018_spectra('CommonData/spectrum_m.csv') shape = SpectralShape(380, 730, 10) sds = [ SpectralDistribution(data[i, :], shape.range()) for i in range(data.shape[0]) ] for name, colourchecker in COLOURCHECKER_SDS.items(): print('Adding %s...' % name) sds += colourchecker.values() D65 = ILLUMINANT_SDS['D65'] xy_w = ILLUMINANTS['CIE 1931 2 Degree Standard Observer']['D65'] x = [] y = [] errors = []
def XYZ_to_spectral_Meng2015( XYZ, cmfs=STANDARD_OBSERVERS_CMFS['CIE 1931 2 Degree Standard Observer'], interval=5, tolerance=1e-10, maximum_iterations=2000): """ Recovers the spectral power distribution of given *CIE XYZ* tristimulus values using *Meng et al. (2015)* method. Parameters ---------- XYZ : array_like, (3,) *CIE XYZ* tristimulus values. cmfs : XYZ_ColourMatchingFunctions Standard observer colour matching functions. interval : numeric, optional Wavelength :math:`\lambda_{i}` range interval in nm. The smaller `interval` is, the longer the computations will be. tolerance : numeric, optional Tolerance for termination. The lower `tolerance` is, the smoother the recovered spectral power distribution will be. maximum_iterations : int, optional Maximum number of iterations to perform. Returns ------- SpectralPowerDistribution Recovered spectral power distribution. Notes ----- - The definition used to convert spectrum to *CIE XYZ* tristimulus values is :func:`colour.spectral_to_XYZ_integration` definition because it processes any measurement interval opposed to :func:`colour.spectral_to_XYZ_ASTME30815` definition that handles only measurement interval of 1, 5, 10 or 20nm. Examples -------- >>> XYZ = np.array([0.07049534, 0.10080000, 0.09558313]) >>> spd = XYZ_to_spectral_Meng2015(XYZ) >>> print(spd) SpectralPowerDistribution('Meng (2015) - \ [ 0.07049534 0.1008 0.09558313]', (360.0, 830.0, 5.0)) >>> spectral_to_XYZ_integration(spd) # doctest: +ELLIPSIS array([ 0.0704952..., 0.1007999..., 0.0955824...]) """ XYZ = np.asarray(XYZ) shape = SpectralShape(cmfs.shape.start, cmfs.shape.end, interval) cmfs = cmfs.clone().align(shape) illuminant = ones_spd(shape) spd = ones_spd(shape) def function_objective(a): """ Objective function. """ return np.sum(np.diff(a)**2) def function_constraint(a): """ Function defining the constraint. """ spd[:] = a return spectral_to_XYZ_integration( spd, cmfs=cmfs, illuminant=illuminant) - XYZ wavelengths = spd.wavelengths bins = wavelengths.size constraints = {'type': 'eq', 'fun': function_constraint} bounds = np.tile(np.array([0, 1000]), (bins, 1)) result = minimize(function_objective, spd.values, method='SLSQP', constraints=constraints, bounds=bounds, options={ 'ftol': tolerance, 'maxiter': maximum_iterations }) if not result.success: raise RuntimeError( 'Optimization failed for {0} after {1} iterations: "{2}".'.format( XYZ, result.nit, result.message)) return SpectralPowerDistribution('Meng (2015) - {0}'.format(XYZ), dict(zip(wavelengths, result.x)))