def test_n_dimensional_delta_E_CIE1976(self): """ Tests :func:`colour.difference.delta_e.delta_E_CIE1976` definition n-dimensional arrays support. """ Lab1 = np.array([100.00000000, 21.57210357, 272.22819350]) Lab2 = np.array([100.00000000, 426.67945353, 72.39590835]) delta_E = 451.71330197359117 np.testing.assert_almost_equal( delta_E_CIE1976(Lab1, Lab2), delta_E, decimal=7) Lab1 = np.tile(Lab1, (6, 1)) Lab2 = np.tile(Lab2, (6, 1)) delta_E = np.tile(delta_E, 6) np.testing.assert_almost_equal( delta_E_CIE1976(Lab1, Lab2), delta_E, decimal=7) Lab1 = np.reshape(Lab1, (2, 3, 3)) Lab2 = np.reshape(Lab2, (2, 3, 3)) delta_E = np.reshape(delta_E, (2, 3)) np.testing.assert_almost_equal( delta_E_CIE1976(Lab1, Lab2), delta_E, decimal=7)
def test_n_dimensional_delta_E_CIE1976(self): """ Tests :func:`colour.difference.delta_e.delta_E_CIE1976` definition n-dimensional arrays support. """ Lab1 = np.array([100.00000000, 21.57210357, 272.22819350]) Lab2 = np.array([100.00000000, 426.67945353, 72.39590835]) delta_E = 451.71330197359117 np.testing.assert_almost_equal(delta_E_CIE1976(Lab1, Lab2), delta_E, decimal=7) Lab1 = np.tile(Lab1, (6, 1)) Lab2 = np.tile(Lab2, (6, 1)) delta_E = np.tile(delta_E, 6) np.testing.assert_almost_equal(delta_E_CIE1976(Lab1, Lab2), delta_E, decimal=7) Lab1 = np.reshape(Lab1, (2, 3, 3)) Lab2 = np.reshape(Lab2, (2, 3, 3)) delta_E = np.reshape(delta_E, (2, 3)) np.testing.assert_almost_equal(delta_E_CIE1976(Lab1, Lab2), delta_E, decimal=7)
def test_delta_E_CIE1976(self): """ Tests :func:`colour.difference.delta_e.delta_E_CIE1976` definition. """ self.assertAlmostEqual( delta_E_CIE1976( np.array([100.00000000, 21.57210357, 272.22819350]), np.array([100.00000000, 426.67945353, 72.39590835])), 451.713301974, places=7) self.assertAlmostEqual( delta_E_CIE1976( np.array([100.00000000, 21.57210357, 272.22819350]), np.array([100.00000000, 74.05216981, 276.45318193])), 52.6498611564, places=7) self.assertAlmostEqual( delta_E_CIE1976( np.array([100.00000000, 21.57210357, 272.22819350]), np.array([100.00000000, 8.32281957, -73.58297716])), 346.064891718, places=7)
def test_nan_delta_E_CIE1976(self): """ Tests :func:`colour.difference.delta_e.delta_E_CIE1976` definition nan support. """ cases = [-1.0, 0.0, 1.0, -np.inf, np.inf, np.nan] cases = set(permutations(cases * 3, r=3)) for case in cases: Lab1 = np.array(case) Lab2 = np.array(case) delta_E_CIE1976(Lab1, Lab2)
def test_optimise(self): """Test :class:`colour.recovery.otsu2018.Tree_Otsu2018.optimise` method.""" node_tree = Tree_Otsu2018(self._reflectances, self._cmfs, self._sd_D65) node_tree.optimise(iterations=5) dataset = node_tree.to_dataset() dataset.write(self._path) dataset = Dataset_Otsu2018() dataset.read(self._path) for sd in SDS_COLOURCHECKERS["ColorChecker N Ohta"].values(): XYZ = sd_to_XYZ(sd, self._cmfs, self._sd_D65) / 100 Lab = XYZ_to_Lab(XYZ, self._xy_D65) recovered_sd = XYZ_to_sd_Otsu2018( XYZ, self._cmfs, self._sd_D65, dataset, False ) recovered_XYZ = ( sd_to_XYZ(recovered_sd, self._cmfs, self._sd_D65) / 100 ) recovered_Lab = XYZ_to_Lab(recovered_XYZ, self._xy_D65) error = metric_mse( reshape_sd(sd, SPECTRAL_SHAPE_OTSU2018).values, recovered_sd.values, ) self.assertLess(error, 0.075) delta_E = delta_E_CIE1976(Lab, recovered_Lab) self.assertLess(delta_E, 1e-12)
def test_intermediates(self): """ Tests intermediate results of :func:`colour.recovery.jakob2019.error_function` with :func:`colour.sd_to_XYZ`, :func:`colour.XYZ_to_Lab` and checks if the error is computed correctly by comparing it with :func:`colour.difference.delta_E_CIE1976`. """ # Quoted names refer to colours from ColorChecker N Ohta (using D65). coefficient_list = [ np.array([0, 0, 0]), # 50% gray np.array([0, 0, -1e+9]), # Pure black np.array([0, 0, +1e+9]), # Pure white np.array([1e+9, -1e+9, 2.1e+8]), # A pathological example np.array([2.2667394, -7.6313081, 1.03185]), # 'blue' np.array([-31.377077, 26.810094, -6.1139927]), # 'green' np.array([25.064246, -16.072039, 0.10431365]), # 'red' np.array([-19.325667, 22.242319, -5.8144924]), # 'yellow' np.array([21.909902, -17.227963, 2.142351]), # 'magenta' np.array([-15.864009, 8.6735071, -1.4012552]), # 'cyan' ] for coefficients in coefficient_list: error, _derror, R, XYZ, Lab = error_function(coefficients, self._Lab_e, self._cmfs, self._sd_D65, additional_data=True) sd = sd_Jakob2019( dimensionalise_coefficients(coefficients, self._shape), self._shape) sd_XYZ = sd_to_XYZ(sd, self._cmfs, self._sd_D65) / 100 sd_Lab = XYZ_to_Lab(XYZ, self._xy_D65) error_reference = delta_E_CIE1976(self._Lab_e, Lab) np.testing.assert_allclose(sd.values, R, atol=1e-14) np.testing.assert_allclose(XYZ, sd_XYZ, atol=1e-14) self.assertLess(abs(error_reference - error), JND_CIE1976 / 100) self.assertLess(delta_E_CIE1976(Lab, sd_Lab), JND_CIE1976 / 100)
def test_delta_E_CIE1976(self): """ Tests :func:`colour.difference.delta_e.delta_E_CIE1976` definition. """ self.assertAlmostEqual(delta_E_CIE1976( np.array([100.00000000, 21.57210357, 272.22819350]), np.array([100.00000000, 426.67945353, 72.39590835])), 451.713301974, places=7) self.assertAlmostEqual(delta_E_CIE1976( np.array([100.00000000, 21.57210357, 272.22819350]), np.array([100.00000000, 74.05216981, 276.45318193])), 52.6498611564, places=7) self.assertAlmostEqual(delta_E_CIE1976( np.array([100.00000000, 21.57210357, 272.22819350]), np.array([100.00000000, 8.32281957, -73.58297716])), 346.064891718, places=7)
def test_delta_E_CIE1976(self): """ Tests :func:`colour.difference.delta_e.delta_E_CIE1976` definition. """ Lab_1 = np.array([100.00000000, 21.57210357, 272.22819350]) Lab_2 = np.array([100.00000000, 426.67945353, 72.39590835]) Lab_1 = np.tile(Lab_1, (6, 1)).reshape((2, 3, 3)) Lab_2 = np.tile(Lab_2, (6, 1)).reshape((2, 3, 3)) np.testing.assert_almost_equal(delta_E_CIE1976(Lab_1, Lab_2), euclidean_distance(Lab_1, Lab_2), decimal=7)
def test_delta_E_CIE1976(self): """ Tests :func:`colour.difference.delta_e.delta_E_CIE1976` definition. """ Lab_1 = np.array([100.00000000, 21.57210357, 272.22819350]) Lab_2 = np.array([100.00000000, 426.67945353, 72.39590835]) Lab_1 = np.tile(Lab_1, (6, 1)).reshape([2, 3, 3]) Lab_2 = np.tile(Lab_2, (6, 1)).reshape([2, 3, 3]) np.testing.assert_almost_equal( delta_E_CIE1976(Lab_1, Lab_2), euclidean_distance(Lab_1, Lab_2), decimal=7)
def test_domain_range_scale_delta_E_CIE1976(self): """ Tests :func:`colour.difference.delta_e.delta_E_CIE1976` definition domain and range scale support. """ Lab_1 = np.array([100.00000000, 21.57210357, 272.22819350]) Lab_2 = np.array([100.00000000, 426.67945353, 72.39590835]) d_r = (('reference', 1), (1, 0.01), (100, 1)) for scale, factor in d_r: with domain_range_scale(scale): np.testing.assert_almost_equal( delta_E_CIE1976(Lab_1 * factor, Lab_2 * factor), euclidean_distance(Lab_1, Lab_2), decimal=7)
def check_basis_functions(self): """ Test :func:`colour.recovery.RGB_to_sd_Mallett2019` definition or the more specialised :func:`colour.recovery.RGB_to_sd_Mallett2019` definition. """ # Make sure the white point is reconstructed as a perfectly flat # spectrum. RGB = np.full(3, 1.0) sd = RGB_to_sd_Mallett2019(RGB, self._basis) self.assertLess(np.var(sd.values), 1e-5) # Check if the primaries or their combination exceeds the [0, 1] range. lower = np.zeros_like(sd.values) - 1e-12 upper = np.ones_like(sd.values) + 1e12 for RGB in [[1, 1, 1], [1, 0, 0], [0, 1, 0], [0, 0, 1]]: sd = RGB_to_sd_Mallett2019(RGB, self._basis) np.testing.assert_array_less(sd.values, upper) np.testing.assert_array_less(lower, sd.values) # Check Delta E's using a colour checker. for name, sd in SDS_COLOURCHECKERS["ColorChecker N Ohta"].items(): XYZ = sd_to_XYZ(sd, self._cmfs, self._sd_D65) / 100 Lab = XYZ_to_Lab(XYZ, self._xy_D65) RGB = XYZ_to_RGB( XYZ, self._RGB_colourspace.whitepoint, self._xy_D65, self._RGB_colourspace.matrix_XYZ_to_RGB, ) recovered_sd = RGB_to_sd_Mallett2019(RGB, self._basis) recovered_XYZ = ( sd_to_XYZ(recovered_sd, self._cmfs, self._sd_D65) / 100 ) recovered_Lab = XYZ_to_Lab(recovered_XYZ, self._xy_D65) error = delta_E_CIE1976(Lab, recovered_Lab) if error > 4 * JND_CIE1976 / 100: # pragma: no cover self.fail(f'Delta E for "{name}" is {error}!')
def test_NodeTree_Otsu2018_and_Dataset_Otsu2018(self): """ Tests :class:`colour.recovery.otsu2018.NodeTree_Otsu2018` dataset generation and :class:`colour.recovery.otsu2018.Dataset_Otsu2018` input and output. The generated dataset is also tested for reconstruction errors. """ reflectances = [] for colourchecker in ['ColorChecker N Ohta', 'BabelColor Average']: for sd in SDS_COLOURCHECKERS[colourchecker].values(): reflectances.append(sd.copy().align(self._shape).values) node_tree = NodeTree_Otsu2018(reflectances, self._cmfs, self._sd_D65) node_tree.optimise(iterations=2) path = os.path.join(self._temporary_directory, 'Test_Otsu2018.npz') dataset = node_tree.to_dataset() dataset.write(path) dataset = Dataset_Otsu2018() dataset.read(path) for sd in SDS_COLOURCHECKERS['ColorChecker N Ohta'].values(): XYZ = sd_to_XYZ(sd, self._cmfs, self._sd_D65) / 100 Lab = XYZ_to_Lab(XYZ, self._xy_D65) recovered_sd = XYZ_to_sd_Otsu2018(XYZ, self._cmfs, self._sd_D65, dataset, False) recovered_XYZ = sd_to_XYZ(recovered_sd, self._cmfs, self._sd_D65) / 100 recovered_Lab = XYZ_to_Lab(recovered_XYZ, self._xy_D65) error = metric_mse(sd.copy().align(SPECTRAL_SHAPE_OTSU2018).values, recovered_sd.values) self.assertLess(error, 0.075) delta_E = delta_E_CIE1976(Lab, recovered_Lab) self.assertLess(delta_E, 1e-12)
def test_LUT3D_Jakob2019(self): """ Tests the entirety of the :class:`colour.recovery.jakob2019.LUT3D_Jakob2019`class. """ LUT = LUT3D_Jakob2019() LUT.generate(self._RGB_colourspace, self._cmfs, self._sd_D65, 5) path = os.path.join(self._temporary_directory, 'Test_Jakob2019.coeff') LUT.write(path) LUT.read(path) for RGB in [ np.array([1, 0, 0]), np.array([0, 1, 0]), np.array([0, 0, 1]), zeros(3), full(3, 0.5), ones(3), ]: XYZ = RGB_to_XYZ(RGB, self._RGB_colourspace.whitepoint, self._xy_D65, self._RGB_colourspace.matrix_RGB_to_XYZ) Lab = XYZ_to_Lab(XYZ, self._xy_D65) recovered_sd = LUT.RGB_to_sd(RGB) recovered_XYZ = sd_to_XYZ(recovered_sd, self._cmfs, self._sd_D65) / 100 recovered_Lab = XYZ_to_Lab(recovered_XYZ, self._xy_D65) error = delta_E_CIE1976(Lab, recovered_Lab) if error > 2 * JND_CIE1976 / 100: self.fail( 'Delta E for RGB={0} in colourspace {1} is {2}!'.format( RGB, self._RGB_colourspace.name, error))
def test_XYZ_to_sd_Otsu2018(self): """Test :func:`colour.recovery.otsu2018.XYZ_to_sd_Otsu2018` definition.""" # Tests the round-trip with values of a colour checker. for _name, sd in SDS_COLOURCHECKERS["ColorChecker N Ohta"].items(): XYZ = sd_to_XYZ(sd, self._cmfs, self._sd_D65) / 100 Lab = XYZ_to_Lab(XYZ, self._xy_D65) recovered_sd = XYZ_to_sd_Otsu2018( XYZ, self._cmfs, self._sd_D65, clip=False ) recovered_XYZ = ( sd_to_XYZ(recovered_sd, self._cmfs, self._sd_D65) / 100 ) recovered_Lab = XYZ_to_Lab(recovered_XYZ, self._xy_D65) error = metric_mse( reshape_sd(sd, SPECTRAL_SHAPE_OTSU2018).values, recovered_sd.values, ) self.assertLess(error, 0.02) delta_E = delta_E_CIE1976(Lab, recovered_Lab) self.assertLess(delta_E, 1e-12)
x = [] y = [] errors = [] above_JND = 0 for i, sd in tqdm.tqdm(enumerate(sds), total=len(sds)): XYZ = sd_to_XYZ(sd, illuminant=D65) / 100 xy = XYZ_to_xy(XYZ) x.append(xy[0]) y.append(xy[1]) Lab = XYZ_to_Lab(XYZ, xy_w) recovered_sd = XYZ_to_sd_Otsu2018(XYZ) recovered_XYZ = sd_to_XYZ(recovered_sd, illuminant=D65) / 100 recovered_Lab = XYZ_to_Lab(recovered_XYZ, xy_w) error = delta_E_CIE1976(Lab, recovered_Lab) errors.append(error) if error > 2.4: above_JND += 1 print('Min. error: %g' % min(errors)) print('Max. error: %g' % max(errors)) print('Avg. error: %g' % np.mean(errors)) print('Errors above JND: %d (%.1f%%)' % (above_JND, 100 * above_JND / len(sds))) bins = [int((max(y) - min(y)) / 0.01), int((max(x) - min(x)) / 0.01)] histogram, _, _ = np.histogram2d(x, y, bins=bins, weights=errors) plot_chromaticity_diagram_CIE1931(standalone=False) plt.imshow(histogram,