def test_n_dimensional_XYZ_to_xy(self): """ Tests :func:`colour.models.cie_xyy.XYZ_to_xy` definition n-dimensions support. """ XYZ = np.array([0.20654008, 0.12197225, 0.05136952]) illuminant = np.array([0.31270, 0.32900]) xy = np.array([0.54369557, 0.32107944]) np.testing.assert_almost_equal( XYZ_to_xy(XYZ, illuminant), xy, decimal=7) XYZ = np.tile(XYZ, (6, 1)) xy = np.tile(xy, (6, 1)) np.testing.assert_almost_equal( XYZ_to_xy(XYZ, illuminant), xy, decimal=7) illuminant = np.tile(illuminant, (6, 1)) np.testing.assert_almost_equal( XYZ_to_xy(XYZ, illuminant), xy, decimal=7) XYZ = np.reshape(XYZ, (2, 3, 3)) illuminant = np.reshape(xy, (2, 3, 2)) xy = np.reshape(xy, (2, 3, 2)) np.testing.assert_almost_equal( XYZ_to_xy(XYZ, illuminant), xy, decimal=7)
def test_n_dimensional_XYZ_to_xy(self): """ Tests :func:`colour.models.cie_xyy.XYZ_to_xy` definition n-dimensions support. """ XYZ = np.array([0.69935853, 1.00000000, 0.94824534]) illuminant = np.array([0.34570, 0.35850]) xy = np.array([0.26414772, 0.37770001]) np.testing.assert_almost_equal(XYZ_to_xy(XYZ, illuminant), xy, decimal=7) XYZ = np.tile(XYZ, (6, 1)) xy = np.tile(xy, (6, 1)) np.testing.assert_almost_equal(XYZ_to_xy(XYZ, illuminant), xy, decimal=7) illuminant = np.tile(illuminant, (6, 1)) np.testing.assert_almost_equal(XYZ_to_xy(XYZ, illuminant), xy, decimal=7) XYZ = np.reshape(XYZ, (2, 3, 3)) illuminant = np.reshape(xy, (2, 3, 2)) xy = np.reshape(xy, (2, 3, 2)) np.testing.assert_almost_equal(XYZ_to_xy(XYZ, illuminant), xy, decimal=7)
def test_XYZ_to_xy(self): """Test :func:`colour.models.cie_xyy.XYZ_to_xy` definition.""" np.testing.assert_almost_equal( XYZ_to_xy(np.array([0.20654008, 0.12197225, 0.05136952])), np.array([0.54369557, 0.32107944]), decimal=7, ) np.testing.assert_almost_equal( XYZ_to_xy(np.array([0.14222010, 0.23042768, 0.10495772])), np.array([0.29777735, 0.48246446]), decimal=7, ) np.testing.assert_almost_equal( XYZ_to_xy(np.array([0.07818780, 0.06157201, 0.28099326])), np.array([0.18582823, 0.14633764]), decimal=7, ) np.testing.assert_almost_equal( XYZ_to_xy(np.array([0.00000000, 0.00000000, 0.00000000])), np.array([0.31270000, 0.32900000]), decimal=7, )
def corresponding_chromaticities_prediction_CMCCAT2000(experiment=1, **kwargs): """ Returns the corresponding chromaticities prediction for CMCCAT2000 chromatic adaptation model. Parameters ---------- experiment : integer, optional {1, 2, 3, 4, 6, 8, 9, 11, 12} Breneman (1987) experiment number. \**kwargs : dict, optional Keywords arguments. Returns ------- tuple Corresponding chromaticities prediction. Examples -------- >>> from pprint import pprint >>> pr = corresponding_chromaticities_prediction_CMCCAT2000(2) >>> pr = [(p.uvp_m, p.uvp_p) for p in pr] >>> pprint(pr) # doctest: +SKIP [((0.207, 0.486), (0.20832101929657834, 0.47271680534693694)), ((0.449, 0.511), (0.44592707020371486, 0.50777351504395707)), ((0.263, 0.505), (0.26402624712986333, 0.4955361681706304)), ((0.322, 0.545), (0.33168840090358015, 0.54315801981008516)), ((0.316, 0.537), (0.32226245779851387, 0.53576245377085929)), ((0.265, 0.553), (0.27107058097430181, 0.5501997842556422)), ((0.221, 0.538), (0.22618269421847523, 0.52947407170848704)), ((0.135, 0.532), (0.14396930475660724, 0.51909841743126817)), ((0.145, 0.472), (0.14948357434418671, 0.45567605010224305)), ((0.163, 0.331), (0.15631720730028753, 0.31641514460738623)), ((0.176, 0.431), (0.17631993066748047, 0.41275893424542082)), ((0.244, 0.349), (0.22876382018951744, 0.3499324084859976))] """ experiment_results = list(BRENEMAN_EXPERIMENTS.get(experiment)) illuminants = experiment_results.pop(0) XYZ_w = xy_to_XYZ(Luv_uv_to_xy(illuminants.uvp_t)) * 100 XYZ_wr = xy_to_XYZ(Luv_uv_to_xy(illuminants.uvp_m)) * 100 xy_wr = XYZ_to_xy(XYZ_wr) L_A1 = L_A2 = BRENEMAN_EXPERIMENTS_PRIMARIES_CHROMATICITIES.get( experiment).Y prediction = [] for result in experiment_results: XYZ_1 = xy_to_XYZ(Luv_uv_to_xy(result.uvp_t)) * 100 XYZ_2 = chromatic_adaptation_CMCCAT2000( XYZ_1, XYZ_w, XYZ_wr, L_A1, L_A2) uvp = Luv_to_uv(XYZ_to_Luv(XYZ_2, xy_wr), xy_wr) prediction.append(CorrespondingChromaticitiesPrediction( result.name, result.uvp_t, result.uvp_m, uvp)) return tuple(prediction)
def test_domain_range_scale_XYZ_to_xy(self): """ Tests :func:`colour.models.cie_xyy.XYZ_to_xy` definition domain and range scale support. """ XYZ = np.array([0.20654008, 0.12197225, 0.05136952]) xy = XYZ_to_xy(XYZ) XYZ = np.tile(XYZ, (6, 1)).reshape(2, 3, 3) xy = np.tile(xy, (6, 1)).reshape(2, 3, 2) d_r = (('reference', 1), (1, 1), (100, 1)) for scale, factor in d_r: with domain_range_scale(scale): np.testing.assert_almost_equal( XYZ_to_xy(XYZ * factor), xy, decimal=7)
def setUp(self): """Initialise the common tests attributes.""" self._shape = SPECTRAL_SHAPE_OTSU2018 self._cmfs, self._sd_D65 = handle_spectral_arguments( shape_default=self._shape ) self._reflectances = sds_and_msds_to_msds( list(SDS_COLOURCHECKERS["ColorChecker N Ohta"].values()) + list(SDS_COLOURCHECKERS["BabelColor Average"].values()) ) self._tree = Tree_Otsu2018( self._reflectances, self._cmfs, self._sd_D65 ) self._XYZ_D65 = sd_to_XYZ(self._sd_D65) self._xy_D65 = XYZ_to_xy(self._XYZ_D65) self._temporary_directory = tempfile.mkdtemp() self._path = os.path.join( self._temporary_directory, "Test_Otsu2018.npz" )
def corresponding_chromaticities_prediction_Fairchild1990(experiment=1): """ Returns the corresponding chromaticities prediction for *Fairchild (1990)* chromatic adaptation model. Parameters ---------- experiment : integer, optional {1, 2, 3, 4, 6, 8, 9, 11, 12} *Breneman (1987)* experiment number. Returns ------- tuple Corresponding chromaticities prediction. References ---------- :cite:`Breneman1987b`, :cite:`Fairchild1991a`, :cite:`Fairchild2013s` Examples -------- >>> from pprint import pprint >>> pr = corresponding_chromaticities_prediction_Fairchild1990(2) >>> pr = [(p.uvp_m, p.uvp_p) for p in pr] >>> pprint(pr) # doctest: +SKIP [((0.207, 0.486), (0.2089528..., 0.4724034...)), ((0.449, 0.511), (0.4375652..., 0.5121030...)), ((0.263, 0.505), (0.2621362..., 0.4972538...)), ((0.322, 0.545), (0.3235312..., 0.5475665...)), ((0.316, 0.537), (0.3151390..., 0.5398333...)), ((0.265, 0.553), (0.2634745..., 0.5544335...)), ((0.221, 0.538), (0.2211595..., 0.5324470...)), ((0.135, 0.532), (0.1396949..., 0.5207234...)), ((0.145, 0.472), (0.1512288..., 0.4533041...)), ((0.163, 0.331), (0.1715691..., 0.3026264...)), ((0.176, 0.431), (0.1825792..., 0.4077892...)), ((0.244, 0.349), (0.2418904..., 0.3413401...))] """ with domain_range_scale(1): experiment_results = list(BRENEMAN_EXPERIMENTS[experiment]) illuminants = experiment_results.pop(0) XYZ_n = xy_to_XYZ(Luv_uv_to_xy(illuminants.uvp_t)) XYZ_r = xy_to_XYZ(Luv_uv_to_xy(illuminants.uvp_m)) xy_r = XYZ_to_xy(XYZ_r) Y_n = BRENEMAN_EXPERIMENTS_PRIMARIES_CHROMATICITIES[experiment].Y prediction = [] for result in experiment_results: XYZ_1 = xy_to_XYZ(Luv_uv_to_xy(result.uvp_t)) XYZ_2 = chromatic_adaptation_Fairchild1990(XYZ_1, XYZ_n, XYZ_r, Y_n) uvp = Luv_to_uv(XYZ_to_Luv(XYZ_2, xy_r), xy_r) prediction.append( CorrespondingChromaticitiesPrediction( result.name, result.uvp_t, result.uvp_m, uvp)) return tuple(prediction)
def corresponding_chromaticities_prediction_Fairchild1990(experiment=1, **kwargs): """ Returns the corresponding chromaticities prediction for Fairchild (1990) chromatic adaptation model. Parameters ---------- experiment : integer, optional {1, 2, 3, 4, 6, 8, 9, 11, 12} Breneman (1987) experiment number. \**kwargs : dict, optional Keywords arguments. Returns ------- tuple Corresponding chromaticities prediction. Examples -------- >>> from pprint import pprint >>> pr = corresponding_chromaticities_prediction_Fairchild1990(2) >>> pr = [(p.uvp_m, p.uvp_p) for p in pr] >>> pprint(pr) # doctest: +SKIP [((0.207, 0.486), (0.2089528677990308, 0.47240345174230519)), ((0.449, 0.511), (0.43756528098582792, 0.51210303139041924)), ((0.263, 0.505), (0.26213623665658092, 0.49725385033264224)), ((0.322, 0.545), (0.3235312762825191, 0.54756652922585702)), ((0.316, 0.537), (0.3151390992740366, 0.53983332031574016)), ((0.265, 0.553), (0.26347459238415272, 0.55443357809543037)), ((0.221, 0.538), (0.22115956537655593, 0.53244703908294599)), ((0.135, 0.532), (0.13969494108553854, 0.52072342107668024)), ((0.145, 0.472), (0.1512288710743511, 0.45330415352961834)), ((0.163, 0.331), (0.17156913711903982, 0.30262647410866889)), ((0.176, 0.431), (0.18257922398137369, 0.40778921192793854)), ((0.244, 0.349), (0.24189049501108895, 0.34134012046930529))] """ experiment_results = list(BRENEMAN_EXPERIMENTS.get(experiment)) illuminants = experiment_results.pop(0) XYZ_n = xy_to_XYZ(Luv_uv_to_xy(illuminants.uvp_t)) * 100 XYZ_r = xy_to_XYZ(Luv_uv_to_xy(illuminants.uvp_m)) * 100 xy_r = XYZ_to_xy(XYZ_r) Y_n = BRENEMAN_EXPERIMENTS_PRIMARIES_CHROMATICITIES.get(experiment).Y prediction = [] for result in experiment_results: XYZ_1 = xy_to_XYZ(Luv_uv_to_xy(result.uvp_t)) * 100 XYZ_2 = chromatic_adaptation_Fairchild1990( XYZ_1, XYZ_n, XYZ_r, Y_n) uvp = Luv_to_uv(XYZ_to_Luv(XYZ_2, xy_r), xy_r) prediction.append(CorrespondingChromaticitiesPrediction( result.name, result.uvp_t, result.uvp_m, uvp)) return tuple(prediction)
def get_secondaries(name='ITU-R BT.2020'): """ secondary color の座標を求める Parameters ---------- name : str a name of the color space. Returns ------- array_like secondaries. the order is magenta, yellow, cyan. """ secondary_rgb = np.array([[1.0, 0.0, 1.0], [1.0, 1.0, 0.0], [0.0, 1.0, 1.0]]) illuminant_XYZ = D65_WHITE illuminant_RGB = D65_WHITE chromatic_adaptation_transform = 'CAT02' rgb_to_xyz_matrix = get_rgb_to_xyz_matrix(name) large_xyz = RGB_to_XYZ(secondary_rgb, illuminant_RGB, illuminant_XYZ, rgb_to_xyz_matrix, chromatic_adaptation_transform) xy = XYZ_to_xy(large_xyz, illuminant_XYZ) return xy, secondary_rgb.reshape((3, 3))
def vs_colorimetry_data(spd_test, spd_reference, spds_vs, cmfs, chromatic_adaptation=False): XYZ_t = spectral_to_XYZ(spd_test, cmfs) XYZ_t /= XYZ_t[1] XYZ_r = spectral_to_XYZ(spd_reference, cmfs) XYZ_r /= XYZ_r[1] xy_r = XYZ_to_xy(XYZ_r) vs_data = [] for _key, value in sorted(VS_INDEXES_TO_NAMES.items()): spd_vs = spds_vs.get(value) XYZ_vs = spectral_to_XYZ(spd_vs, cmfs, spd_test) XYZ_vs /= 100 if chromatic_adaptation: XYZ_vs = chromatic_adaptation_VonKries(XYZ_vs, XYZ_t, XYZ_r, transform='CMCCAT2000') Lab_vs = XYZ_to_Lab(XYZ_vs, illuminant=xy_r) _L_vs, C_vs, _Hab = Lab_to_LCHab(Lab_vs) vs_data.append(VS_ColorimetryData(spd_vs.name, XYZ_vs, Lab_vs, C_vs)) return vs_data
def XYZ_to_ij(XYZ): """ Converts given *CIE XYZ* tristimulus values to *ij* chromaticity coordinates. """ return XYZ_to_xy(XYZ)
def intermediate_values(XYZ_n): """ Returns the intermediate values :math:`\\xi`, :math:`\eta`, :math:`\zeta`. Parameters ---------- XYZ_n : array_like, (3,) *CIE XYZ* colourspace matrix of reference white in domain [0, 100]. Returns ------- ndarray, (3,) Intermediate values :math:`\\xi`, :math:`\eta`, :math:`\zeta`. Examples -------- >>> XYZ_n = np.array([95.05, 100, 108.88]) >>> intermediate_values(XYZ_n) # doctest: +ELLIPSIS array([ 1.0000421..., 0.9999800..., 0.9997579...]) """ # Illuminant chromaticity coordinates. x_o, y_o = XYZ_to_xy(XYZ_n) # Computing :math:`\xi`, :math:`\eta`, :math:`\zeta` values. xi = (0.48105 * x_o + 0.78841 * y_o - 0.08081) / y_o eta = (-0.27200 * x_o + 1.11962 * y_o + 0.04570) / y_o zeta = (0.91822 * (1 - x_o - y_o)) / y_o return np.array([xi, eta, zeta])
def corresponding_chromaticities_prediction_VonKries(experiment=1, transform='CAT02'): """ Returns the corresponding chromaticities prediction for *Von Kries* chromatic adaptation model using given transform. Parameters ---------- experiment : integer, optional {1, 2, 3, 4, 6, 8, 9, 11, 12} *Breneman (1987)* experiment number. transform : unicode, optional **{'CAT02', 'XYZ Scaling', 'Von Kries', 'Bradford', 'Sharp', 'Fairchild', 'CMCCAT97', 'CMCCAT2000', 'CAT02_BRILL_CAT', 'Bianco', 'Bianco PC'}**, Chromatic adaptation transform. Returns ------- tuple Corresponding chromaticities prediction. Examples -------- >>> from pprint import pprint >>> pr = corresponding_chromaticities_prediction_VonKries(2, 'Bradford') >>> pr = [(p.uvp_m, p.uvp_p) for p in pr] >>> pprint(pr) # doctest: +SKIP [((0.207, 0.486), (0.2082014..., 0.4722922...)), ((0.449, 0.511), (0.4489102..., 0.5071602...)), ((0.263, 0.505), (0.2643545..., 0.4959631...)), ((0.322, 0.545), (0.3348730..., 0.5471220...)), ((0.316, 0.537), (0.3248758..., 0.5390589...)), ((0.265, 0.553), (0.2733105..., 0.5555028...)), ((0.221, 0.538), (0.2271480..., 0.5331317...)), ((0.135, 0.532), (0.1442730..., 0.5226804...)), ((0.145, 0.472), (0.1498745..., 0.4550785...)), ((0.163, 0.331), (0.1564975..., 0.3148795...)), ((0.176, 0.431), (0.1760593..., 0.4103772...)), ((0.244, 0.349), (0.2259805..., 0.3465291...))] """ experiment_results = list(BRENEMAN_EXPERIMENTS[experiment]) illuminants = experiment_results.pop(0) XYZ_w = xy_to_XYZ(Luv_uv_to_xy(illuminants.uvp_t)) XYZ_wr = xy_to_XYZ(Luv_uv_to_xy(illuminants.uvp_m)) xy_wr = XYZ_to_xy(XYZ_wr) prediction = [] for result in experiment_results: XYZ_1 = xy_to_XYZ(Luv_uv_to_xy(result.uvp_t)) XYZ_2 = chromatic_adaptation_VonKries(XYZ_1, XYZ_w, XYZ_wr, transform) uvp = Luv_to_uv(XYZ_to_Luv(XYZ_2, xy_wr), xy_wr) prediction.append( CorrespondingChromaticitiesPrediction(result.name, result.uvp_t, result.uvp_m, uvp)) return tuple(prediction)
def RGB_chromaticity_coordinates_CIE_1931_chromaticity_diagram_plot( RGB, colourspace, **kwargs): """ Plots given *RGB* colourspace array in *CIE 1931 Chromaticity Diagram*. Parameters ---------- RGB : array_like *RGB* colourspace array. colourspace : RGB_Colourspace *RGB* colourspace of the *RGB* array. \**kwargs : dict, optional Keywords arguments. Returns ------- bool Definition success. Examples -------- >>> RGB = np.random.random((10, 10, 3)) >>> c = 'Rec. 709' >>> RGB_chromaticity_coordinates_CIE_1931_chromaticity_diagram_plot( ... RGB, c) # doctest: +SKIP True """ settings = {} settings.update(kwargs) settings.update({'standalone': False}) settings['colourspaces'] = ( [colourspace.name] + settings.get('colourspaces', [])) RGB_colourspaces_CIE_1931_chromaticity_diagram_plot(**settings) alpha_p, colour_p = 0.85, 'black' xy = XYZ_to_xy(RGB_to_XYZ(RGB, colourspace.whitepoint, colourspace.whitepoint, colourspace.RGB_to_XYZ_matrix), colourspace.whitepoint) pylab.scatter(xy[..., 0], xy[..., 1], alpha=alpha_p / 2, color=colour_p, marker='+') settings.update({'standalone': True}) boundaries(**settings) decorate(**settings) return display(**settings)
def chromaticity_diagram_colours_CIE1931( samples=4096, cmfs='CIE 1931 2 Degree Standard Observer', antialiasing=True): """ Plots the *CIE 1931 Chromaticity Diagram* colours. Parameters ---------- samples : numeric, optional Samples count on one axis. cmfs : unicode, optional Standard observer colour matching functions used for diagram bounds. antialiasing : bool, optional Whether to apply anti-aliasing to the image. Other Parameters ---------------- \**kwargs : dict, optional {:func:`colour.plotting.render`}, Please refer to the documentation of the previously listed definition. Returns ------- Figure Current figure or None. Examples -------- >>> chromaticity_diagram_colours_CIE1931() # doctest: +SKIP """ cmfs = get_cmfs(cmfs) illuminant = DEFAULT_PLOTTING_ILLUMINANT triangulation = Delaunay(XYZ_to_xy(cmfs.values, illuminant), qhull_options='Qu QJ') xx, yy = np.meshgrid(np.linspace(0, 1, samples), np.linspace(1, 0, samples)) xy = tstack((xx, yy)) mask = (triangulation.find_simplex(xy) < 0).astype(DEFAULT_FLOAT_DTYPE) if antialiasing: kernel = np.array([ [0, 1, 0], [1, 2, 1], [0, 1, 0], ]).astype(DEFAULT_FLOAT_DTYPE) kernel /= np.sum(kernel) mask = convolve(mask, kernel) mask = 1 - mask[:, :, np.newaxis] XYZ = xy_to_XYZ(xy) RGB = normalise_maximum(XYZ_to_sRGB(XYZ, illuminant), axis=-1) return np.dstack([RGB, mask])
def setUp(self): """Initialise the common tests attributes.""" self._shape = SPECTRAL_SHAPE_OTSU2018 self._cmfs, self._sd_D65 = handle_spectral_arguments( shape_default=self._shape ) self._XYZ_D65 = sd_to_XYZ(self._sd_D65) self._xy_D65 = XYZ_to_xy(self._XYZ_D65)
def test_nan_XYZ_to_xy(self): """Test :func:`colour.models.cie_xyy.XYZ_to_xy` 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: XYZ = np.array(case) illuminant = np.array(case[0:2]) XYZ_to_xy(XYZ, illuminant)
def corresponding_chromaticities_prediction_CMCCAT2000(experiment=1): """ Returns the corresponding chromaticities prediction for CMCCAT2000 chromatic adaptation model. Parameters ---------- experiment : integer, optional {1, 2, 3, 4, 6, 8, 9, 11, 12} Breneman (1987) experiment number. Returns ------- tuple Corresponding chromaticities prediction. Examples -------- >>> from pprint import pprint >>> pr = corresponding_chromaticities_prediction_CMCCAT2000(2) >>> pr = [(p.uvp_m, p.uvp_p) for p in pr] >>> pprint(pr) # doctest: +SKIP [((0.207, 0.486), (0.2083210..., 0.4727168...)), ((0.449, 0.511), (0.4459270..., 0.5077735...)), ((0.263, 0.505), (0.2640262..., 0.4955361...)), ((0.322, 0.545), (0.3316884..., 0.5431580...)), ((0.316, 0.537), (0.3222624..., 0.5357624...)), ((0.265, 0.553), (0.2710705..., 0.5501997...)), ((0.221, 0.538), (0.2261826..., 0.5294740...)), ((0.135, 0.532), (0.1439693..., 0.5190984...)), ((0.145, 0.472), (0.1494835..., 0.4556760...)), ((0.163, 0.331), (0.1563172..., 0.3164151...)), ((0.176, 0.431), (0.1763199..., 0.4127589...)), ((0.244, 0.349), (0.2287638..., 0.3499324...))] """ experiment_results = list(BRENEMAN_EXPERIMENTS.get(experiment)) illuminants = experiment_results.pop(0) XYZ_w = xy_to_XYZ(Luv_uv_to_xy(illuminants.uvp_t)) * 100 XYZ_wr = xy_to_XYZ(Luv_uv_to_xy(illuminants.uvp_m)) * 100 xy_wr = XYZ_to_xy(XYZ_wr) L_A1 = L_A2 = BRENEMAN_EXPERIMENTS_PRIMARIES_CHROMATICITIES.get( experiment).Y prediction = [] for result in experiment_results: XYZ_1 = xy_to_XYZ(Luv_uv_to_xy(result.uvp_t)) * 100 XYZ_2 = chromatic_adaptation_CMCCAT2000(XYZ_1, XYZ_w, XYZ_wr, L_A1, L_A2) uvp = Luv_to_uv(XYZ_to_Luv(XYZ_2, xy_wr), xy_wr) prediction.append( CorrespondingChromaticitiesPrediction(result.name, result.uvp_t, result.uvp_m, uvp)) return tuple(prediction)
def primaries_whitepoint(npm): """ Returns the *primaries* and *whitepoint* :math:`xy` chromaticity coordinates using given *normalised primary matrix*. Parameters ---------- npm : array_like, (3, 3) *Normalised primary matrix*. Returns ------- tuple *Primaries* and *whitepoint* :math:`xy` chromaticity coordinates. References ---------- .. [2] Trieu, T. (2015). Private Discussion with Mansencal, T. Examples -------- >>> npm = np.array([[9.52552396e-01, 0.00000000e+00, 9.36786317e-05], ... [3.43966450e-01, 7.28166097e-01, -7.21325464e-02], ... [0.00000000e+00, 0.00000000e+00, 1.00882518e+00]]) >>> p, w = primaries_whitepoint(npm) >>> p # doctest: +ELLIPSIS array([[ 7.3470000...e-01, 2.6530000...e-01], [ 0.0000000...e+00, 1.0000000...e+00], [ 1.0000000...e-04, -7.7000000...e-02]]) >>> w # doctest: +ELLIPSIS array([ 0.32168, 0.33767]) """ npm = npm.reshape((3, 3)) primaries = XYZ_to_xy( np.transpose(np.dot(npm, np.identity(3)))) whitepoint = np.squeeze(XYZ_to_xy( np.transpose(np.dot(npm, np.ones((3, 1)))))) # TODO: Investigate if we return an ndarray here with primaries and # whitepoint stacked together. return primaries, whitepoint
def setUp(self): """Initialise the common tests attributes.""" self._xy_s = XYZ_to_xy( MSDS_CMFS["CIE 1931 2 Degree Standard Observer"].values ) self._xy_D65 = CCS_ILLUMINANTS["CIE 1931 2 Degree Standard Observer"][ "D65" ]
def vs_colorimetry_data( sd_test: SpectralDistribution, sd_reference: SpectralDistribution, sds_vs: Dict[str, SpectralDistribution], cmfs: MultiSpectralDistributions, chromatic_adaptation: Boolean = False, ) -> Tuple[VS_ColorimetryData, ...]: """ Return the *VS test colour samples* colorimetry data. Parameters ---------- sd_test Test spectral distribution. sd_reference Reference spectral distribution. sds_vs *VS test colour samples* spectral distributions. cmfs Standard observer colour matching functions. chromatic_adaptation Whether to perform chromatic adaptation. Returns ------- :class:`tuple` *VS test colour samples* colorimetry data. """ XYZ_t = sd_to_XYZ(sd_test, cmfs) XYZ_t /= XYZ_t[1] XYZ_r = sd_to_XYZ(sd_reference, cmfs) XYZ_r /= XYZ_r[1] xy_r = XYZ_to_xy(XYZ_r) vs_data = [] for _key, value in sorted(INDEXES_TO_NAMES_VS.items()): sd_vs = sds_vs[value] with domain_range_scale("1"): XYZ_vs = sd_to_XYZ(sd_vs, cmfs, sd_test) if chromatic_adaptation: XYZ_vs = chromatic_adaptation_VonKries(XYZ_vs, XYZ_t, XYZ_r, transform="CMCCAT2000") Lab_vs = XYZ_to_Lab(XYZ_vs, illuminant=xy_r) _L_vs, C_vs, _Hab = Lab_to_LCHab(Lab_vs) vs_data.append(VS_ColorimetryData(sd_vs.name, XYZ_vs, Lab_vs, C_vs)) return tuple(vs_data)
def primaries_whitepoint(npm: ArrayLike) -> Tuple[NDArray, NDArray]: """ Compute the *primaries* and *whitepoint* :math:`xy` chromaticity coordinates using given *Normalised Primary Matrix* (NPM). Parameters ---------- npm *Normalised Primary Matrix*. Returns ------- :class:`tuple` *Primaries* and *whitepoint* :math:`xy` chromaticity coordinates. References ---------- :cite:`Trieu2015a` Examples -------- >>> npm = np.array([[9.52552396e-01, 0.00000000e+00, 9.36786317e-05], ... [3.43966450e-01, 7.28166097e-01, -7.21325464e-02], ... [0.00000000e+00, 0.00000000e+00, 1.00882518e+00]]) >>> p, w = primaries_whitepoint(npm) >>> p # doctest: +ELLIPSIS array([[ 7.3470000...e-01, 2.6530000...e-01], [ 0.0000000...e+00, 1.0000000...e+00], [ 1.0000000...e-04, -7.7000000...e-02]]) >>> w # doctest: +ELLIPSIS array([ 0.32168, 0.33767]) """ npm = np.reshape(npm, (3, 3)) primaries = XYZ_to_xy(np.transpose(np.dot(npm, np.identity(3)))) whitepoint = np.squeeze(XYZ_to_xy(np.transpose(np.dot(npm, ones((3, 1)))))) # TODO: Investigate if we return an ndarray here with primaries and # whitepoint stacked together. return primaries, whitepoint
def vs_colorimetry_data(sd_test, sd_reference, sds_vs, cmfs, chromatic_adaptation=False): """ Returns the *VS test colour samples* colorimetry data. Parameters ---------- sd_test : SpectralDistribution Test spectral distribution. sd_reference : SpectralDistribution Reference spectral distribution. sds_vs : dict *VS test colour samples* spectral distributions. cmfs : XYZ_ColourMatchingFunctions Standard observer colour matching functions. chromatic_adaptation : bool, optional Perform chromatic adaptation. Returns ------- list *VS test colour samples* colorimetry data. """ XYZ_t = sd_to_XYZ(sd_test, cmfs) XYZ_t /= XYZ_t[1] XYZ_r = sd_to_XYZ(sd_reference, cmfs) XYZ_r /= XYZ_r[1] xy_r = XYZ_to_xy(XYZ_r) vs_data = [] for _key, value in sorted(VS_INDEXES_TO_NAMES.items()): sd_vs = sds_vs[value] with domain_range_scale('1'): XYZ_vs = sd_to_XYZ(sd_vs, cmfs, sd_test) if chromatic_adaptation: XYZ_vs = chromatic_adaptation_VonKries(XYZ_vs, XYZ_t, XYZ_r, transform='CMCCAT2000') Lab_vs = XYZ_to_Lab(XYZ_vs, illuminant=xy_r) _L_vs, C_vs, _Hab = Lab_to_LCHab(Lab_vs) vs_data.append(VS_ColorimetryData(sd_vs.name, XYZ_vs, Lab_vs, C_vs)) return vs_data
def test_XYZ_to_xy(self): """ Tests :func:`colour.models.cie_xyy.XYZ_to_xy` definition. """ np.testing.assert_almost_equal(XYZ_to_xy( np.array([0.07049534, 0.10080000, 0.09558313])), np.array([0.26414772, 0.37770001]), decimal=7) np.testing.assert_almost_equal(XYZ_to_xy( np.array([0.47097710, 0.34950000, 0.11301649])), np.array([0.50453169, 0.37440000]), decimal=7) np.testing.assert_almost_equal(XYZ_to_xy( np.array([0.25506814, 0.19150000, 0.08849752])), np.array([0.47670437, 0.35790000]), decimal=7) np.testing.assert_almost_equal(XYZ_to_xy( np.array([0.00000000, 0.00000000, 0.00000000])), np.array([0.34570000, 0.35850000]), decimal=7)
def _get_cmfs_xy(): """ xy色度図のプロットのための馬蹄形の外枠のxy値を求める。 Returns ------- array_like xy coordinate for chromaticity diagram """ # 基本パラメータ設定 # ------------------ cmf = CMFS.get(CMFS_NAME) d65_white = D65_WHITE # 馬蹄形のxy値を算出 # -------------------------- cmf_xy = XYZ_to_xy(cmf.values, d65_white) return cmf_xy
def spds_CIE_1931_chromaticity_diagram_plot( spds, cmfs='CIE 1931 2 Degree Standard Observer', annotate=True, **kwargs): """ Plots given spectral power distribution chromaticity coordinates into the *CIE 1931 Chromaticity Diagram*. Parameters ---------- spds : array_like, optional Spectral power distributions to plot. cmfs : unicode, optional Standard observer colour matching functions used for diagram bounds. annotate : bool Should resulting chromaticity coordinates annotated with their respective spectral power distribution names. Other Parameters ---------------- \**kwargs : dict, optional {:func:`boundaries`, :func:`canvas`, :func:`decorate`, :func:`display`}, Please refer to the documentation of the previously listed definitions. show_diagram_colours : bool, optional {:func:`CIE_1931_chromaticity_diagram_plot`}, Whether to display the chromaticity diagram background colours. Returns ------- Figure Current figure or None. Examples -------- >>> from colour import ILLUMINANTS_RELATIVE_SPDS >>> A = ILLUMINANTS_RELATIVE_SPDS['A'] >>> D65 = ILLUMINANTS_RELATIVE_SPDS['D65'] >>> spds_CIE_1931_chromaticity_diagram_plot([A, D65]) # doctest: +SKIP """ settings = {} settings.update(kwargs) settings.update({'standalone': False}) CIE_1931_chromaticity_diagram_plot(cmfs=cmfs, **settings) for spd in spds: XYZ = spectral_to_XYZ(spd) / 100 xy = XYZ_to_xy(XYZ) pylab.plot(xy[0], xy[1], 'o', color='white') if spd.name is not None and annotate: pylab.annotate(spd.name, xy=xy, xytext=(50, 30), color='black', textcoords='offset points', arrowprops=dict(arrowstyle='->', connectionstyle='arc3, rad=0.2')) settings.update({ 'x_tighten': True, 'y_tighten': True, 'limits': (-0.1, 0.9, -0.1, 0.9), 'standalone': True }) settings.update(kwargs) boundaries(**settings) decorate(**settings) return display(**settings)
def CIE_1931_chromaticity_diagram_colours_plot( surface=1, samples=4096, cmfs='CIE 1931 2 Degree Standard Observer', **kwargs): """ Plots the *CIE 1931 Chromaticity Diagram* colours. Parameters ---------- surface : numeric, optional Generated markers surface. samples : numeric, optional Samples count on one axis. cmfs : unicode, optional Standard observer colour matching functions used for diagram bounds. Other Parameters ---------------- \**kwargs : dict, optional {:func:`boundaries`, :func:`canvas`, :func:`decorate`, :func:`display`}, Please refer to the documentation of the previously listed definitions. Returns ------- Figure Current figure or None. Examples -------- >>> CIE_1931_chromaticity_diagram_colours_plot() # doctest: +SKIP """ settings = {'figure_size': (64, 64)} settings.update(kwargs) canvas(**settings) cmfs = get_cmfs(cmfs) illuminant = DEFAULT_PLOTTING_ILLUMINANT triangulation = Delaunay(XYZ_to_xy(cmfs.values, illuminant), qhull_options='QJ') xx, yy = np.meshgrid(np.linspace(0, 1, samples), np.linspace(0, 1, samples)) xy = tstack((xx, yy)) xy = xy[triangulation.find_simplex(xy) > 0] XYZ = xy_to_XYZ(xy) RGB = normalise_maximum(XYZ_to_sRGB(XYZ, illuminant), axis=-1) x_dot, y_dot = tsplit(xy) pylab.scatter(x_dot, y_dot, color=RGB, s=surface) settings.update({ 'x_ticker': False, 'y_ticker': False, 'bounding_box': (0, 1, 0, 1) }) settings.update(kwargs) ax = matplotlib.pyplot.gca() matplotlib.pyplot.setp(ax, frame_on=False) boundaries(**settings) decorate(**settings) return display(**settings)
def CIE_1931_chromaticity_diagram_plot( cmfs='CIE 1931 2 Degree Standard Observer', show_diagram_colours=True, **kwargs): """ Plots the *CIE 1931 Chromaticity Diagram*. Parameters ---------- cmfs : unicode, optional Standard observer colour matching functions used for diagram bounds. show_diagram_colours : bool, optional Whether to display the chromaticity diagram background colours. Other Parameters ---------------- \**kwargs : dict, optional {:func:`boundaries`, :func:`canvas`, :func:`decorate`, :func:`display`}, Please refer to the documentation of the previously listed definitions. Returns ------- Figure Current figure or None. Examples -------- >>> CIE_1931_chromaticity_diagram_plot() # doctest: +SKIP """ settings = {'figure_size': (DEFAULT_FIGURE_WIDTH, DEFAULT_FIGURE_WIDTH)} settings.update(kwargs) canvas(**settings) cmfs = get_cmfs(cmfs) illuminant = DEFAULT_PLOTTING_ILLUMINANT if show_diagram_colours: image = matplotlib.image.imread( os.path.join( PLOTTING_RESOURCES_DIRECTORY, 'CIE_1931_Chromaticity_Diagram_{0}.png'.format( cmfs.name.replace(' ', '_')))) pylab.imshow(image, interpolation=None, 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) xy = XYZ_to_xy(cmfs.values, illuminant) wavelengths_chromaticity_coordinates = dict(tuple(zip(wavelengths, xy))) pylab.plot(xy[..., 0], xy[..., 1], color='black', linewidth=2) pylab.plot((xy[-1][0], xy[0][0]), (xy[-1][1], xy[0][1]), color='black', linewidth=2) for label in labels: x, y = wavelengths_chromaticity_coordinates[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[right][0] - wavelengths_chromaticity_coordinates[left][0]) dy = (wavelengths_chromaticity_coordinates[right][1] - wavelengths_chromaticity_coordinates[left][1]) xy = np.array([x, y]) direction = np.array([-dy, dx]) normal = (np.array([ -dy, dx ]) if np.dot(normalise_vector(xy - equal_energy), normalise_vector(direction)) > 0 else np.array([dy, -dx])) normal = normalise_vector(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, color='black', clip_on=True, ha='left' if normal[0] >= 0 else 'right', va='center', fontdict={'size': 'small'}) ticks = np.arange(-10, 10, 0.1) pylab.xticks(ticks) pylab.yticks(ticks) settings.update({ 'title': 'CIE 1931 Chromaticity Diagram - {0}'.format(cmfs.title), 'x_label': 'CIE x', 'y_label': 'CIE y', 'grid': True, 'bounding_box': (0, 1, 0, 1) }) settings.update(kwargs) boundaries(**settings) decorate(**settings) return display(**settings)
def dominant_wavelength(xy, xy_n, cmfs=CMFS['CIE 1931 2 Degree Standard Observer'], inverse=False): """ Returns the *dominant wavelength* :math:`\\lambda_d` for given colour stimulus :math:`xy` and the related :math:`xy_wl` first and :math:`xy_{cw}` second intersection coordinates with the spectral locus. In the eventuality where the :math:`xy_wl` first intersection coordinates are on the line of purples, the *complementary wavelength* will be computed in lieu. The *complementary wavelength* is indicated by a negative sign and the :math:`xy_{cw}` second intersection coordinates which are set by default to the same value than :math:`xy_wl` first intersection coordinates will be set to the *complementary dominant wavelength* intersection coordinates with the spectral locus. Parameters ---------- xy : array_like Colour stimulus *CIE xy* chromaticity coordinates. xy_n : array_like Achromatic stimulus *CIE xy* chromaticity coordinates. cmfs : XYZ_ColourMatchingFunctions, optional Standard observer colour matching functions. inverse : bool, optional Inverse the computation direction to retrieve the *complementary wavelength*. Returns ------- tuple *Dominant wavelength*, first intersection point *CIE xy* chromaticity coordinates, second intersection point *CIE xy* chromaticity coordinates. References ---------- :cite:`CIETC1-482004o`, :cite:`Erdogana` Examples -------- *Dominant wavelength* computation: >>> from pprint import pprint >>> xy = np.array([0.54369557, 0.32107944]) >>> xy_n = np.array([0.31270000, 0.32900000]) >>> cmfs = CMFS['CIE 1931 2 Degree Standard Observer'] >>> pprint(dominant_wavelength(xy, xy_n, cmfs)) # doctest: +ELLIPSIS (array(616...), array([ 0.6835474..., 0.3162840...]), array([ 0.6835474..., 0.3162840...])) *Complementary dominant wavelength* is returned if the first intersection is located on the line of purples: >>> xy = np.array([0.37605506, 0.24452225]) >>> pprint(dominant_wavelength(xy, xy_n, cmfs)) # doctest: +ELLIPSIS (array(-509.0), array([ 0.4572314..., 0.1362814...]), array([ 0.0104096..., 0.7320745...])) """ xy = as_float_array(xy) xy_n = np.resize(xy_n, xy.shape) xy_s = XYZ_to_xy(cmfs.values) i_wl, xy_wl = closest_spectral_locus_wavelength(xy, xy_n, xy_s, inverse) xy_cwl = xy_wl wl = cmfs.wavelengths[i_wl] xy_e = (extend_line_segment(xy, xy_n) if inverse else extend_line_segment( xy_n, xy)) intersect = intersect_line_segments(np.concatenate((xy_n, xy_e), -1), np.hstack([xy_s[0], xy_s[-1]])).intersect intersect = np.reshape(intersect, wl.shape) i_wl_r, xy_cwl_r = closest_spectral_locus_wavelength( xy, xy_n, xy_s, not inverse) wl_r = -cmfs.wavelengths[i_wl_r] wl = np.where(intersect, wl_r, wl) xy_cwl = np.where(intersect[..., np.newaxis], xy_cwl_r, xy_cwl) return wl, np.squeeze(xy_wl), np.squeeze(xy_cwl)
def plot_spectral_locus(cmfs='CIE 1931 2 Degree Standard Observer', spectral_locus_colours=None, spectral_locus_labels=None, method='CIE 1931', **kwargs): """ Plots the *Spectral Locus* according to given method. Parameters ---------- cmfs : unicode, optional Standard observer colour matching functions defining the *Spectral Locus*. spectral_locus_colours : array_like or unicode, optional *Spectral Locus* colours, if ``spectral_locus_colours`` is set to *RGB*, the colours will be computed according to the corresponding chromaticity coordinates. spectral_locus_labels : array_like, optional Array of wavelength labels used to customise which labels will be drawn around the spectral locus. Passing an empty array will result in no wavelength labels being drawn. method : unicode, optional **{'CIE 1931', 'CIE 1960 UCS', 'CIE 1976 UCS'}**, *Chromaticity Diagram* method. Other Parameters ---------------- \\**kwargs : dict, optional {:func:`colour.plotting.artist`, :func:`colour.plotting.render`}, Please refer to the documentation of the previously listed definitions. Returns ------- tuple Current figure and axes. Examples -------- >>> plot_spectral_locus(spectral_locus_colours='RGB') # doctest: +SKIP .. image:: ../_static/Plotting_Plot_Spectral_Locus.png :align: center :alt: plot_spectral_locus """ if spectral_locus_colours is None: spectral_locus_colours = COLOUR_STYLE_CONSTANTS.colour.dark settings = {'uniform': True} settings.update(kwargs) figure, axes = artist(**settings) method = method.upper() cmfs = first_item(filter_cmfs(cmfs).values()) illuminant = COLOUR_STYLE_CONSTANTS.colour.colourspace.whitepoint wavelengths = cmfs.wavelengths equal_energy = np.array([1 / 3] * 2) if method == 'CIE 1931': ij = XYZ_to_xy(cmfs.values, illuminant) labels = ((390, 460, 470, 480, 490, 500, 510, 520, 540, 560, 580, 600, 620, 700) if spectral_locus_labels is None else spectral_locus_labels) elif method == 'CIE 1960 UCS': ij = UCS_to_uv(XYZ_to_UCS(cmfs.values)) labels = ((420, 440, 450, 460, 470, 480, 490, 500, 510, 520, 530, 540, 550, 560, 570, 580, 590, 600, 610, 620, 630, 645, 680) if spectral_locus_labels is None else spectral_locus_labels) elif method == 'CIE 1976 UCS': ij = Luv_to_uv(XYZ_to_Luv(cmfs.values, illuminant), illuminant) labels = ((420, 440, 450, 460, 470, 480, 490, 500, 510, 520, 530, 540, 550, 560, 570, 580, 590, 600, 610, 620, 630, 645, 680) if spectral_locus_labels is None else spectral_locus_labels) else: raise ValueError( 'Invalid method: "{0}", must be one of ' '{\'CIE 1931\', \'CIE 1960 UCS\', \'CIE 1976 UCS\'}'.format( method)) pl_ij = tstack([ np.linspace(ij[0][0], ij[-1][0], 20), np.linspace(ij[0][1], ij[-1][1], 20) ]).reshape(-1, 1, 2) sl_ij = np.copy(ij).reshape(-1, 1, 2) if spectral_locus_colours.upper() == 'RGB': spectral_locus_colours = normalise_maximum( XYZ_to_plotting_colourspace(cmfs.values), axis=-1) if method == 'CIE 1931': XYZ = xy_to_XYZ(pl_ij) elif method == 'CIE 1960 UCS': XYZ = xy_to_XYZ(UCS_uv_to_xy(pl_ij)) elif method == 'CIE 1976 UCS': XYZ = xy_to_XYZ(Luv_uv_to_xy(pl_ij)) purple_line_colours = normalise_maximum( XYZ_to_plotting_colourspace(XYZ.reshape(-1, 3)), axis=-1) else: purple_line_colours = spectral_locus_colours for slp_ij, slp_colours in ((pl_ij, purple_line_colours), (sl_ij, spectral_locus_colours)): line_collection = LineCollection( np.concatenate([slp_ij[:-1], slp_ij[1:]], axis=1), colors=slp_colours) axes.add_collection(line_collection) wl_ij = dict(tuple(zip(wavelengths, ij))) for label in labels: i, j = wl_ij[label] 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 = wl_ij[right][0] - wl_ij[left][0] dy = wl_ij[right][1] - wl_ij[left][1] ij = np.array([i, j]) direction = np.array([-dy, dx]) normal = (np.array([-dy, dx]) if np.dot( normalise_vector(ij - equal_energy), normalise_vector(direction)) > 0 else np.array([dy, -dx])) normal = normalise_vector(normal) / 30 label_colour = (spectral_locus_colours if is_string(spectral_locus_colours) else spectral_locus_colours[index]) axes.plot( (i, i + normal[0] * 0.75), (j, j + normal[1] * 0.75), color=label_colour) axes.plot(i, j, 'o', color=label_colour) axes.text( i + normal[0], j + normal[1], label, clip_on=True, ha='left' if normal[0] >= 0 else 'right', va='center', fontdict={'size': 'small'}) settings = {'axes': axes} settings.update(kwargs) return render(**kwargs)