def _rec_709_EOCF(value): """ Defines the *Rec. 709* colourspace electro-optical conversion function. Parameters ---------- value : numeric or array_like Value. Returns ------- numeric or ndarray Companded value. Warning ------- *Recommendation ITU-R BT.709-5* doesn't specify an electro-optical conversion function. This definition is used for symmetry in unit tests and other computations but should not be used as an *EOCF* for *Rec. 709* colourspace! """ warning(('*Recommendation ITU-R BT.709-5* doesn\'t specify an' 'electro-optical conversion function. This definition is used ' 'for symmetry in unit tests and others computations but should ' 'not be used as an *EOCF* for *Rec. 709* colourspace!')) value = np.asarray(value) return np.where(value < _rec_709_OECF(0.018), value / 4.5, ((value + 0.099) / 1.099)**(1 / 0.45))
def test_suppress_warnings(self): """ Tests :func:`colour.utilities.verbose.suppress_warnings` definition. """ with suppress_warnings(): warning('This is a suppressed unit test warning!')
def x(self, value): """ Setter for the **self.x** property. """ if value is not None: value = np.atleast_1d(value).astype(self._dtype) assert value.ndim == 1, ( '"x" independent variable must have exactly one dimension!') value_interval = interval(value) if value_interval.size != 1: warning(('"x" independent variable is not uniform, ' 'unpredictable results may occur!')) self._x = value if self._window is not None: self._x_p = np.pad( self._x, (self._window, self._window), 'linear_ramp', end_values=( np.min(self._x) - self._window * value_interval[0], np.max(self._x) + self._window * value_interval[0]))
def eotf_sRGB(V): """ Defines the *sRGB* colourspace electro-optical transfer function (EOTF / EOCF). Parameters ---------- V : numeric or array_like Electrical signal :math:`V`. Returns ------- numeric or ndarray Corresponding *luminance* :math:`L` of the image. Examples -------- >>> eotf_sRGB(0.461356129500442) # doctest: +ELLIPSIS 0.1... """ warning(('*sRGB* *OETF* is a piece-wise function: in order to reduce ' 'noise in dark region, a line segment limits the slope of the ' 'power function (slope of a power function is infinite at zero). ' 'This is not needed for *sRGB* *EOTF*, a pure gamma 2.2 function ' 'should be use instead. This definition is used for symmetry in ' 'unit tests and others computations but should not be used as an ' '*EOTF*!')) V = np.asarray(V) return as_numeric( np.where(V <= oetf_sRGB(0.0031308), V / 12.92, ((V + 0.055) / 1.055)**2.4))
def _rec_709_EOCF(value): """ Defines the *Rec. 709* colourspace electro-optical conversion function. Parameters ---------- value : numeric or array_like Value. Returns ------- numeric or ndarray Companded value. Warning ------- *Recommendation ITU-R BT.709-5* doesn't specify an electro-optical conversion function. This definition is used for symmetry in unit tests and other computations but should not be used as an *EOCF* for *Rec. 709* colourspace! """ warning( ( "*Recommendation ITU-R BT.709-5* doesn't specify an" "electro-optical conversion function. This definition is used " "for symmetry in unit tests and others computations but should " "not be used as an *EOCF* for *Rec. 709* colourspace!" ) ) value = np.asarray(value) return np.where(value < _rec_709_OECF(0.018), value / 4.5, ((value + 0.099) / 1.099) ** (1 / 0.45))
def __getattr__(self, attribute): """ Returns given attribute value while handling deprecation. Parameters ---------- attribute : unicode Attribute name. Returns ------- object Attribute value. Raises ------ AttributeError If the attribute is not defined. """ change = self._changes.get(attribute) if change is not None: if not isinstance(change, Removed): warning(str(change)) return get_attribute(change[1]) else: raise AttributeError(str(change)) return getattr(self._module, attribute)
def random_triplet_generator(size, limits=np.array([[0, 1], [0, 1], [0, 1]]), random_state=RANDOM_STATE): """ Returns a generator yielding random triplets. Parameters ---------- size : integer Generator size. limits : array_like, (3, 2) Random values limits on each triplet axis. random_state : RandomState Mersenne Twister pseudo-random number generator. Returns ------- generator Random triplets generator. Notes ----- The doctest is assuming that :func:`np.random.RandomState` definition will return the same sequence no matter which *OS* or *Python* version is used. There is however no formal promise about the *prng* sequence reproducibility of either *Python* or *Numpy* implementations: Laurent. (2012). Reproducibility of python pseudo-random numbers across systems and versions? Retrieved January 20, 2015, from http://stackoverflow.com/\ questions/8786084/reproducibility-of-python-pseudo-random-numbers-\ across-systems-and-versions Examples -------- >>> from pprint import pprint >>> prng = np.random.RandomState(4) >>> pprint( # doctest: +ELLIPSIS ... tuple(random_triplet_generator(10, random_state=prng))) (array([ 0.9670298..., 0.5472322..., 0.9726843...]), array([ 0.7148159..., 0.6977288..., 0.2160895...]), array([ 0.9762744..., 0.0062302..., 0.2529823...]), array([ 0.4347915..., 0.7793829..., 0.1976850...]), array([ 0.8629932..., 0.9834006..., 0.1638422...]), array([ 0.5973339..., 0.0089861..., 0.3865712...]), array([ 0.0441600..., 0.9566529..., 0.4361466...]), array([ 0.9489773..., 0.7863059..., 0.8662893...]), array([ 0.1731654..., 0.0749485..., 0.6007427...]), array([ 0.1679721..., 0.7333801..., 0.4084438...])) """ integer_size = int(size) if integer_size != size: warning(('"size" has been cast to integer: {0}'.format(integer_size))) for _ in range(integer_size): yield np.array([ random_state.uniform(*limits[0]), random_state.uniform(*limits[1]), random_state.uniform(*limits[2]) ])
def random_triplet_generator(size, limits=np.array([[0, 1], [0, 1], [0, 1]]), random_state=RANDOM_STATE): """ Returns a generator yielding random triplets. Parameters ---------- size : integer Generator size. limits : array_like, (3, 2) Random values limits on each triplet axis. random_state : RandomState Mersenne Twister pseudo-random number generator. Returns ------- generator Random triplets generator. Notes ----- The doctest is assuming that :func:`np.random.RandomState` definition will return the same sequence no matter which *OS* or *Python* version is used. There is however no formal promise about the *prng* sequence reproducibility of either *Python* or *Numpy* implementations: Laurent. (2012). Reproducibility of python pseudo-random numbers across systems and versions? Retrieved January 20, 2015, from http://stackoverflow.com/\ questions/8786084/reproducibility-of-python-pseudo-random-numbers-\ across-systems-and-versions Examples -------- >>> from pprint import pprint >>> prng = np.random.RandomState(4) >>> pprint( # doctest: +ELLIPSIS ... tuple(random_triplet_generator(10, random_state=prng))) (array([ 0.9670298..., 0.5472322..., 0.9726843...]), array([ 0.7148159..., 0.6977288..., 0.2160895...]), array([ 0.9762744..., 0.0062302..., 0.2529823...]), array([ 0.4347915..., 0.7793829..., 0.1976850...]), array([ 0.8629932..., 0.9834006..., 0.1638422...]), array([ 0.5973339..., 0.0089861..., 0.3865712...]), array([ 0.0441600..., 0.9566529..., 0.4361466...]), array([ 0.9489773..., 0.7863059..., 0.8662893...]), array([ 0.1731654..., 0.0749485..., 0.6007427...]), array([ 0.1679721..., 0.7333801..., 0.4084438...])) """ integer_size = int(size) if integer_size != size: warning(('"size" has been cast to integer: {0}'.format( integer_size))) for _ in range(integer_size): yield np.array([random_state.uniform(*limits[0]), random_state.uniform(*limits[1]), random_state.uniform(*limits[2])])
def CCT_to_xy_Kang2002(CCT): """ Returns the *CIE XYZ* tristimulus values *xy* chromaticity coordinates from given correlated colour temperature :math:`T_{cp}` using Kang et al. (2002) method. Parameters ---------- CCT : numeric or array_like Correlated colour temperature :math:`T_{cp}`. Returns ------- ndarray *xy* chromaticity coordinates. Raises ------ ValueError If the correlated colour temperature is not in appropriate domain. References ---------- .. [11] Kang, B., Moon, O., Hong, C., Lee, H., Cho, B., & Kim, Y. (2002). Design of advanced color: Temperature control system for HDTV applications. Journal of the Korean …, 41(6), 865–871. Retrieved from http://cat.inist.fr/?aModele=afficheN&cpsidt=14448733 Examples -------- >>> CCT_to_xy_Kang2002(6504.38938305) # doctest: +ELLIPSIS array([ 0.313426..., 0.3235959...]) """ CCT = np.asarray(CCT) if np.any(CCT[np.asarray(np.logical_or(CCT < 1667, CCT > 25000))]): warning(('Correlated colour temperature must be in domain ' '[1667, 25000], unpredictable results may occur!')) x = np.where( CCT <= 4000, -0.2661239 * 10**9 / CCT**3 - 0.2343589 * 10**6 / CCT**2 + 0.8776956 * 10**3 / CCT + 0.179910, -3.0258469 * 10**9 / CCT**3 + 2.1070379 * 10**6 / CCT**2 + 0.2226347 * 10**3 / CCT + 0.24039) y = np.select([ CCT <= 2222, np.logical_and(CCT > 2222, CCT <= 4000), CCT > 4000 ], [ -1.1063814 * x**3 - 1.34811020 * x**2 + 2.18555832 * x - 0.20219683, -0.9549476 * x**3 - 1.37418593 * x**2 + 2.09137015 * x - 0.16748867, 3.0817580 * x**3 - 5.8733867 * x**2 + 3.75112997 * x - 0.37001483 ]) xy = tstack((x, y)) return xy
def CCT_to_xy_CIE_D(CCT): """ Converts from the correlated colour temperature :math:`T_{cp}` of a *CIE Illuminant D Series* to the chromaticity of that *CIE Illuminant D Series* illuminant. Parameters ---------- CCT : numeric or array_like Correlated colour temperature :math:`T_{cp}`. Returns ------- ndarray *xy* chromaticity coordinates. Raises ------ ValueError If the correlated colour temperature is not in appropriate domain. References ---------- .. [12] Wyszecki, G., & Stiles, W. S. (2000). CIE Method of Calculating D-Illuminants. In Color Science: Concepts and Methods, Quantitative Data and Formulae (pp. 145–146). Wiley. ISBN:978-0471399186 Examples -------- >>> CCT_to_xy_CIE_D(6504.38938305) # doctest: +ELLIPSIS array([ 0.3127077..., 0.3291128...]) """ CCT = np.asarray(CCT) if np.any(CCT[np.asarray(np.logical_or(CCT < 4000, CCT > 25000))]): warning(('Correlated colour temperature must be in domain ' '[4000, 25000], unpredictable results may occur!')) x = np.where(CCT <= 7000, -4.607 * 10 ** 9 / CCT ** 3 + 2.9678 * 10 ** 6 / CCT ** 2 + 0.09911 * 10 ** 3 / CCT + 0.244063, -2.0064 * 10 ** 9 / CCT ** 3 + 1.9018 * 10 ** 6 / CCT ** 2 + 0.24748 * 10 ** 3 / CCT + 0.23704) y = -3 * x ** 2 + 2.87 * x - 0.275 xy = tstack((x, y)) return xy
def read_metadata(self): """ Reads image relevant exif metadata at :attr:`Image.path` attribute. Returns ------- Metadata Image relevant exif metadata. """ logging.info('Reading "{0}" image metadata.'.format(self._path)) exif_data = read_exif_tags(self._path) if not exif_data.get('EXIF'): warning( '"{0}" file has no "Exif" data, metadata will be undefined!'. format(self._path)) self.metadata = Metadata(*[None] * 6) return self.metadata f_number = exif_data['EXIF'].get('F Number') if f_number is not None: f_number = parse_exif_numeric(f_number[0]) exposure_time = exif_data['EXIF'].get('Exposure Time') if exposure_time is not None: exposure_time = parse_exif_fraction(exposure_time[0]) iso = exif_data['EXIF'].get('ISO') if iso is not None: iso = parse_exif_numeric(iso[0]) black_level = exif_data['EXIF'].get('Black Level') if black_level is not None: black_level = parse_exif_array(black_level[0]) black_level = as_float_array(black_level) / 65535 white_level = exif_data['EXIF'].get('White Level') if white_level is not None: white_level = parse_exif_array(white_level[0]) white_level = as_float_array(white_level) / 65535 white_balance_multipliers = exif_data['EXIF'].get('As Shot Neutral') if white_balance_multipliers is not None: white_balance_multipliers = parse_exif_array( white_balance_multipliers[0]) white_balance_multipliers = as_float_array( white_balance_multipliers) / white_balance_multipliers[1] self.metadata = Metadata(f_number, exposure_time, iso, black_level, white_level, white_balance_multipliers) return self.metadata
def CCT_to_xy_Kang2002(CCT): """ Returns the *CIE XYZ* tristimulus values *xy* chromaticity coordinates from given correlated colour temperature :math:`T_{cp}` using *Kang et alii (2002)* method. Parameters ---------- CCT : numeric or array_like Correlated colour temperature :math:`T_{cp}`. Returns ------- ndarray *xy* chromaticity coordinates. Raises ------ ValueError If the correlated colour temperature is not in appropriate domain. References ---------- - :cite:`Kang2002a` Examples -------- >>> CCT_to_xy_Kang2002(6504.38938305) # doctest: +ELLIPSIS array([ 0.313426..., 0.3235959...]) """ CCT = np.asarray(CCT) if np.any(CCT[np.asarray(np.logical_or(CCT < 1667, CCT > 25000))]): warning(('Correlated colour temperature must be in domain ' '[1667, 25000], unpredictable results may occur!')) x = np.where( CCT <= 4000, -0.2661239 * 10 ** 9 / CCT ** 3 - 0.2343589 * 10 ** 6 / CCT ** 2 + 0.8776956 * 10 ** 3 / CCT + 0.179910, -3.0258469 * 10 ** 9 / CCT ** 3 + 2.1070379 * 10 ** 6 / CCT ** 2 + 0.2226347 * 10 ** 3 / CCT + 0.24039) cnd_l = [CCT <= 2222, np.logical_and(CCT > 2222, CCT <= 4000), CCT > 4000] i = -1.1063814 * x ** 3 - 1.34811020 * x ** 2 + 2.18555832 * x - 0.20219683 j = -0.9549476 * x ** 3 - 1.37418593 * x ** 2 + 2.09137015 * x - 0.16748867 k = 3.0817580 * x ** 3 - 5.8733867 * x ** 2 + 3.75112997 * x - 0.37001483 y = np.select(cnd_l, [i, j, k]) xy = tstack((x, y)) return xy
def light_probe_sampling_variance_minimization_Viriyothai2009( light_probe, lights_count=16, colourspace=RGB_COLOURSPACES['sRGB']): """ Sample given light probe to find lights using *Viriyothai (2009)* variance minimization light probe sampling algorithm. Parameters ---------- light_probe : array_like Array to sample for lights. lights_count : int Amount of lights to generate. colourspace : `colour.RGB_Colourspace`, optional *RGB* colourspace used for internal *Luminance* computation. Returns ------- list list of :class:`colour_hdri.sampling.variance_minimization.Light_Specification` lights. References ---------- :cite:`Viriyothai2009` """ light_probe = as_float_array(light_probe) iterations = np.sqrt(lights_count).astype(np.int_) if iterations**2 != lights_count: warning( '{0} lights requested, {1} will be effectively computed!'.format( lights_count, iterations**2)) Y = RGB_luminance(light_probe, colourspace.primaries, colourspace.whitepoint) regions = find_regions_variance_minimization_Viriyothai2009(Y, iterations) lights = [] for region in regions: y_min, y_max, x_min, x_max = region c = centroid(Y[y_min:y_max, x_min:x_max]) c = (c + np.array([y_min, x_min])) light_probe_c = light_probe[y_min:y_max, x_min:x_max] lights.append( Light_Specification((c / np.array(Y.shape))[::-1], np.sum(np.sum(light_probe_c, 0), 0), c)) return lights
def oetf_BT1886(L, L_B=64, L_W=940): """ Defines *Recommendation ITU-R BT.1886* opto-electrical transfer function (OETF / OECF). Parameters ---------- L : numeric or array_like Screen luminance in :math:`cd/m^2`. L_B : numeric, optional Screen luminance for black. L_W : numeric, optional Screen luminance for white. Returns ------- numeric or ndarray Input video signal level (normalized, black at :math:`V = 0`, to white at :math:`V = 1`. Warning ------- *Recommendation ITU-R BT.1886* doesn't specify an opto-electrical transfer function. This definition is used for symmetry in unit tests and other computations but should not be used as an *OETF*. Examples -------- >>> oetf_BT1886(277.98159179331145) # doctest: +ELLIPSIS 0.4090077... """ warning(('*Recommendation ITU-R BT.1886* doesn\'t specify an ' 'opto-electrical transfer function. This definition is used ' 'for symmetry in unit tests and others computations but should ' 'not be used as an *OETF*!')) L = np.asarray(L) gamma = 2.40 gamma_d = 1 / gamma n = L_W ** gamma_d - L_B ** gamma_d a = n ** gamma b = L_B ** gamma_d / n V = (L / a) ** gamma_d - b return V
def oetf_BT1886(L, L_B=0, L_W=1): """ Defines *Recommendation ITU-R BT.1886* opto-electrical transfer function (OETF / OECF). Parameters ---------- L : numeric or array_like Screen luminance in :math:`cd/m^2`. L_B : numeric, optional Screen luminance for black. L_W : numeric, optional Screen luminance for white. Returns ------- numeric or ndarray Input video signal level (normalized, black at :math:`V = 0`, to white at :math:`V = 1`. Warning ------- *Recommendation ITU-R BT.1886* doesn't specify an opto-electrical transfer function. This definition is used for symmetry in unit tests and other computations but should not be used as an *OETF*. Examples -------- >>> oetf_BT1886(0.11699185725296059) # doctest: +ELLIPSIS 0.4090077... """ warning(('*Recommendation ITU-R BT.1886* doesn\'t specify an ' 'opto-electrical transfer function. This definition is used ' 'for symmetry in unit tests and others computations but should ' 'not be used as an *OETF*!')) L = np.asarray(L) gamma = 2.40 gamma_d = 1 / gamma n = L_W**gamma_d - L_B**gamma_d a = n**gamma b = L_B**gamma_d / n V = (L / a)**gamma_d - b return V
def luminance_average_key(image): """ Comparison key function. """ f_number = image.metadata.f_number exposure_time = image.metadata.exposure_time iso = image.metadata.iso if None in (f_number, exposure_time, iso): warning('"{0}" exposure data is missing, average luminance ' 'sorting is inapplicable!'.format(image.path)) return None return 1 / average_luminance(f_number, exposure_time, iso)
def CCT_to_xy_CIE_D(CCT): """ Converts from the correlated colour temperature :math:`T_{cp}` of a *CIE Illuminant D Series* to the chromaticity of that *CIE Illuminant D Series* illuminant. Parameters ---------- CCT : numeric or array_like Correlated colour temperature :math:`T_{cp}`. Returns ------- ndarray *xy* chromaticity coordinates. Raises ------ ValueError If the correlated colour temperature is not in appropriate domain. References ---------- - :cite:`Wyszecki2000z` Examples -------- >>> CCT_to_xy_CIE_D(6504.38938305) # doctest: +ELLIPSIS array([ 0.3127077..., 0.3291128...]) """ CCT = np.asarray(CCT) if np.any(CCT[np.asarray(np.logical_or(CCT < 4000, CCT > 25000))]): warning(('Correlated colour temperature must be in domain ' '[4000, 25000], unpredictable results may occur!')) x = np.where( CCT <= 7000, -4.607 * 10 ** 9 / CCT ** 3 + 2.9678 * 10 ** 6 / CCT ** 2 + 0.09911 * 10 ** 3 / CCT + 0.244063, -2.0064 * 10 ** 9 / CCT ** 3 + 1.9018 * 10 ** 6 / CCT ** 2 + 0.24748 * 10 ** 3 / CCT + 0.23704) y = -3 * x ** 2 + 2.87 * x - 0.275 xy = tstack((x, y)) return xy
def eotf_ARIBSTDB67(E_p, r=0.5): """ Defines *ARIB STD-B67 (Hybrid Log-Gamma)* electro-optical transfer function (EOTF / EOCF). Parameters ---------- E_p : numeric or array_like Non-linear signal :math:`E'`. r : numeric, optional Video level corresponding to reference white level. Returns ------- numeric or ndarray Voltage :math:`E` normalized by the reference white level and proportional to the implicit light intensity that would be detected with a reference camera color channel R, G, B. Warning ------- *ARIB STD-B67 (Hybrid Log-Gamma)* doesn't specify an electro-optical transfer function. This definition is used for symmetry in unit tests and other computations but should not be used as an *EOTF*. Examples -------- >>> eotf_ARIBSTDB67(0.212132034355964) # doctest: +ELLIPSIS 0.1799999... """ warning(('*ARIB STD-B67 (Hybrid Log-Gamma)* doesn\'t specify an ' 'electro-optical transfer function. This definition is used ' 'for symmetry in unit tests and others computations but should ' 'not be used as an *EOTF*!')) E_p = np.asarray(E_p) a = ARIBSTDB67_CONSTANTS.a b = ARIBSTDB67_CONSTANTS.b c = ARIBSTDB67_CONSTANTS.c E = np.where(E_p <= oetf_ARIBSTDB67(1), (E_p / r)**2, np.exp((E_p - c) / a) + b) return as_numeric(E)
def test_nan_first_order_colour_fit(self): """ Tests :func:`colour.characterisation.fitting.first_order_colour_fit` definition nan support. """ cases = [-1.0, 0.0, 1.0, np.nan] cases = set(permutations(cases * 3, r=3)) for case in cases: try: first_order_colour_fit(np.vstack((M1, case)), np.vstack((M2, case))) except ValueError: import traceback from colour.utilities import warning warning(traceback.format_exc())
def test_nan_first_order_colour_fit(self): """ Tests :func:`colour.characterisation.fitting.first_order_colour_fit` definition nan support. """ cases = [-1.0, 0.0, 1.0, np.nan] cases = set(permutations(cases * 3, r=3)) for case in cases: try: first_order_colour_fit(np.vstack((M1, case)), np.vstack((M2, case))) except (ValueError, LinAlgError): import traceback from colour.utilities import warning warning(traceback.format_exc())
def test_nan_normalised_primary_matrix(self): """ Tests :func:`colour.models.rgb.derivation.normalised_primary_matrix` definition nan support. """ cases = [-1.0, 0.0, 1.0, -np.inf, np.inf, np.nan] cases = set(permutations(cases * 3, r=2)) for case in cases: P = np.array(np.vstack((case, case, case))) W = np.array(case) try: normalised_primary_matrix(P, W) except np.linalg.linalg.LinAlgError: import traceback from colour.utilities import warning warning(traceback.format_exc())
def test_nan__call__(self): """ Tests :func:`colour.algebra.interpolation.SpragueInterpolator.__call__` method 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: try: sprague_interpolator = SpragueInterpolator( np.array(case), np.array(case)) sprague_interpolator(case[0]) except AssertionError: import traceback from colour.utilities import warning warning(traceback.format_exc())
def range(self, value): """ Setter for the **self.range** property. """ if value is not None: if not np.all(np.isfinite(value)): warning('"range" variable is not finite, ' 'unpredictable results may occur!\n{0}'.format(value)) value = np.copy(value).astype(self.dtype) if self._domain is not None: assert value.size == self._domain.size, ( '"domain" and "range" variables must have same size!') self._range = value self._create_function()
def lightness_Wyszecki1963(Y, **kwargs): """ Returns the *Lightness* :math:`W` of given *luminance* :math:`Y` using *Wyszecki (1963)* method. Parameters ---------- Y : numeric or array_like *luminance* :math:`Y`. \**kwargs : dict, optional Unused parameter provided for signature compatibility with other *Lightness* computation objects. Returns ------- numeric or array_like *Lightness* :math:`W`. Notes ----- - Input *luminance* :math:`Y` is in domain [0, 100]. - Output *Lightness* :math:`W` is in domain [0, 100]. References ---------- .. [3] Wyszecki, G. (1963). Proposal for a New Color-Difference Formula. J. Opt. Soc. Am., 53(11), 1318–1319. doi:10.1364/JOSA.53.001318 Examples -------- >>> lightness_Wyszecki1963(10.08) # doctest: +ELLIPSIS 37.0041149... """ Y = np.asarray(Y) if np.any(Y < 1) or np.any(Y > 98): warning(('"W*" Lightness computation is only applicable for ' '1% < "Y" < 98%, unpredictable results may occur!')) W = 25 * (Y**(1 / 3)) - 17 return W
def lightness_Wyszecki1963(Y, **kwargs): """ Returns the *Lightness* :math:`W` of given *luminance* :math:`Y` using *Wyszecki (1963)* method. Parameters ---------- Y : numeric or array_like *luminance* :math:`Y`. \**kwargs : dict, optional Unused parameter provided for signature compatibility with other *Lightness* computation objects. Returns ------- numeric or array_like *Lightness* :math:`W`. Notes ----- - Input *luminance* :math:`Y` is in domain [0, 100]. - Output *Lightness* :math:`W` is in domain [0, 100]. References ---------- .. [3] Wyszecki, G. (1963). Proposal for a New Color-Difference Formula. J. Opt. Soc. Am., 53(11), 1318–1319. doi:10.1364/JOSA.53.001318 Examples -------- >>> lightness_Wyszecki1963(10.08) # doctest: +ELLIPSIS 37.0041149... """ Y = np.asarray(Y) if np.any(Y < 1) or np.any(Y > 98): warning(('"W*" Lightness computation is only applicable for ' '1% < "Y" < 98%, unpredictable results may occur!')) W = 25 * (Y ** (1 / 3)) - 17 return W
def test_nan_RGB_luminance(self): """ Tests :func:`colour.models.rgb.derivation.RGB_luminance` 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: RGB = np.array(case) P = np.array(np.vstack((case[0:2], case[0:2], case[0:2]))) W = np.array(case[0:2]) try: RGB_luminance(RGB, P, W) except np.linalg.linalg.LinAlgError: import traceback from colour.utilities import warning warning(traceback.format_exc())
def lightness_wyszecki1964(Y, **kwargs): """ Returns the *Lightness* :math:`W^*` of given *luminance* :math:`Y` using *Wyszecki (1964)* method. Parameters ---------- Y : numeric *luminance* :math:`Y`. \*\*kwargs : \*\*, optional Unused parameter provided for signature compatibility with other *Lightness* computation objects. Returns ------- numeric *Lightness* :math:`W^*`. Notes ----- - Input *luminance* :math:`Y` is in domain [0, 100]. - Output *Lightness* :math:`W^*` is in domain [0, 100]. References ---------- .. [2] http://en.wikipedia.org/wiki/Lightness (Last accessed 13 April 2014) Examples -------- >>> lightness_wyszecki1964(10.08) # doctest: +ELLIPSIS 37.0041149... """ if not 1 < Y < 98: warning(('"W*" Lightness computation is only applicable for ' '1% < "Y" < 98%, unpredictable results may occur!')) W = 25 * (Y**(1 / 3)) - 17 return W
def lightness_wyszecki1964(Y, **kwargs): """ Returns the *Lightness* :math:`W^*` of given *luminance* :math:`Y` using *Wyszecki (1964)* method. Parameters ---------- Y : numeric *luminance* :math:`Y`. \*\*kwargs : \*\*, optional Unused parameter provided for signature compatibility with other *Lightness* computation objects. Returns ------- numeric *Lightness* :math:`W^*`. Notes ----- - Input *luminance* :math:`Y` is in domain [0, 100]. - Output *Lightness* :math:`W^*` is in domain [0, 100]. References ---------- .. [2] http://en.wikipedia.org/wiki/Lightness (Last accessed 13 April 2014) Examples -------- >>> lightness_wyszecki1964(10.08) # doctest: +ELLIPSIS 37.0041149... """ if not 1 < Y < 98: warning(('"W*" Lightness computation is only applicable for ' '1% < "Y" < 98%, unpredictable results may occur!')) W = 25 * (Y ** (1 / 3)) - 17 return W
def test_nan_chromatic_adaptation_Fairchild1990(self): """ Tests :func:`colour.adaptation.fairchild1990.\ chromatic_adaptation_Fairchild1990` 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_1 = np.array(case) XYZ_n = np.array(case) XYZ_r = np.array(case) Y_n = case[0] try: chromatic_adaptation_Fairchild1990(XYZ_1, XYZ_n, XYZ_r, Y_n) except np.linalg.linalg.LinAlgError: import traceback from colour.utilities import warning warning(traceback.format_exc())
def load(dataset): """ Loads given dataset: The dataset is pulled locally, i.e. synced if required and then its data is loaded. Parameters ---------- dataset : unicode or int Dataset id, i.e. the *Zenodo* record number or title. Returns ------- object Dataset data. Examples -------- >>> len(load('3245883').keys()) 28 >>> len(load( ... 'Camera Spectral Sensitivity Database - ' ... 'Jiang et al. (2013)').keys()) 28 """ global _HAS_TITLE_KEYS if not _HAS_TITLE_KEYS: for key in list(DATASET_LOADERS.keys())[:]: dataset_loader = datasets().get(key) if not dataset_loader: continue title = dataset_loader.title if title in DATASET_LOADERS: warning('"{0}" key is already defined in the dataset loaders!'. format(title)) DATASET_LOADERS[title] = DATASET_LOADERS[key] _HAS_TITLE_KEYS = True return DATASET_LOADERS[six.text_type(dataset)]().content
def lightness_Wyszecki1963(Y): """ Returns the *Lightness* :math:`W` of given *luminance* :math:`Y` using *Wyszecki (1963)* method. Parameters ---------- Y : numeric or array_like *luminance* :math:`Y`. Returns ------- numeric or array_like *Lightness* :math:`W`. Notes ----- - Input *luminance* :math:`Y` is in domain [0, 100]. - Output *Lightness* :math:`W` is in range [0, 100]. References ---------- - :cite:`Wyszecki1963b` Examples -------- >>> lightness_Wyszecki1963(10.08) # doctest: +ELLIPSIS 37.0041149... """ Y = np.asarray(Y) if np.any(Y < 1) or np.any(Y > 98): warning(('"W*" Lightness computation is only applicable for ' '1% < "Y" < 98%, unpredictable results may occur!')) W = 25 * (Y**(1 / 3)) - 17 return W
def domain(self, value): """ Setter for the **self.domain** property. """ if value is not None: if not np.all(np.isfinite(value)): warning('"domain" variable is not finite, ' 'unpredictable results may occur!\n{0}'.format(value)) value = np.copy(value).astype(self.dtype) if self._range is not None: if value.size != self._range.size: warning( '"domain" and "range" variables have different size, ' '"range" variable will be resized to ' '"domain" variable shape!') self._range = np.resize(self._range, value.shape) self._domain = value self._create_function()
def eotf_BT709(V): """ Defines *Recommendation ITU-R BT.709-6* electro-optical transfer function (EOTF / EOCF). Parameters ---------- V : numeric or array_like Electrical signal :math:`V`. Returns ------- numeric or ndarray Corresponding *luminance* :math:`L` of the image. Warning ------- *Recommendation ITU-R BT.709-6* doesn't specify an electro-optical transfer function. This definition is used for symmetry in unit tests and other computations but should not be used as an *EOTF*. Examples -------- >>> eotf_BT709(0.409007728864150) # doctest: +ELLIPSIS 0.1... """ warning(('*Recommendation ITU-R BT.709-6* doesn\'t specify an ' 'electro-optical transfer function. This definition is used ' 'for symmetry in unit tests and others computations but should ' 'not be used as an *EOTF*!')) V = np.asarray(V) return as_numeric( np.where(V < oetf_BT709(0.018), V / 4.5, ((V + 0.099) / 1.099)**(1 / 0.45)))
def eotf_BT709(V): """ Defines *Recommendation ITU-R BT.709-6* electro-optical transfer function (EOTF / EOCF). Parameters ---------- V : numeric or array_like Electrical signal :math:`V`. Returns ------- numeric or ndarray Corresponding *luminance* :math:`L` of the image. Warning ------- *Recommendation ITU-R BT.709-6* doesn't specify an electro-optical transfer function. This definition is used for symmetry in unit tests and other computations but should not be used as an *EOTF*. Examples -------- >>> eotf_BT709(0.409007728864150) # doctest: +ELLIPSIS 0.1... """ warning(('*Recommendation ITU-R BT.709-6* doesn\'t specify an ' 'electro-optical transfer function. This definition is used ' 'for symmetry in unit tests and others computations but should ' 'not be used as an *EOTF*!')) V = np.asarray(V) return as_numeric(np.where(V < oetf_BT709(0.018), V / 4.5, ((V + 0.099) / 1.099) ** (1 / 0.45)))
def CCT_to_xy_Kang2002(CCT): """ Returns the *CIE XYZ* tristimulus values *xy* chromaticity coordinates from given correlated colour temperature :math:`T_{cp}` using Kang et al. (2002) method. Parameters ---------- CCT : numeric or array_like Correlated colour temperature :math:`T_{cp}`. Returns ------- ndarray *xy* chromaticity coordinates. Raises ------ ValueError If the correlated colour temperature is not in appropriate domain. References ---------- .. [11] Kang, B., Moon, O., Hong, C., Lee, H., Cho, B., & Kim, Y. (2002). Design of advanced color: Temperature control system for HDTV applications. Journal of the Korean …, 41(6), 865–871. Retrieved from http://cat.inist.fr/?aModele=afficheN&cpsidt=14448733 Examples -------- >>> CCT_to_xy_Kang2002(6504.38938305) # doctest: +ELLIPSIS array([ 0.313426..., 0.3235959...]) """ CCT = np.asarray(CCT) if np.any(CCT[np.asarray(np.logical_or(CCT < 1667, CCT > 25000))]): warning(('Correlated colour temperature must be in domain ' '[1667, 25000], unpredictable results may occur!')) x = np.where(CCT <= 4000, -0.2661239 * 10 ** 9 / CCT ** 3 - 0.2343589 * 10 ** 6 / CCT ** 2 + 0.8776956 * 10 ** 3 / CCT + 0.179910, -3.0258469 * 10 ** 9 / CCT ** 3 + 2.1070379 * 10 ** 6 / CCT ** 2 + 0.2226347 * 10 ** 3 / CCT + 0.24039) y = np.select([CCT <= 2222, np.logical_and(CCT > 2222, CCT <= 4000), CCT > 4000], [-1.1063814 * x ** 3 - 1.34811020 * x ** 2 + 2.18555832 * x - 0.20219683, -0.9549476 * x ** 3 - 1.37418593 * x ** 2 + 2.09137015 * x - 0.16748867, 3.0817580 * x ** 3 - 5.8733867 * x ** 2 + 3.75112997 * x - 0.37001483]) xy = tstack((x, y)) return xy
""" Interpolates a 1-D function using cubic spline interpolation. Notes ----- This class is a wrapper around *scipy.interpolate.interp1d* class. """ def __init__(self, *args, **kwargs): # TODO: Implements proper wrapper to ensure return values # consistency and avoid having to cast to numeric in # :meth:`SpectralPowerDistribution.interpolate` method. super(SplineInterpolator, self).__init__( kind='cubic', *args, **kwargs) else: warning(('"scipy.interpolate.interp1d" interpolator is unavailable, using ' '"LinearInterpolator1d" instead!')) SplineInterpolator = LinearInterpolator1d class SpragueInterpolator(object): """ Constructs a fifth-order polynomial that passes through :math:`y` dependent variable. Sprague (1880) method is recommended by the *CIE* for interpolating functions having a uniformly spaced independent variable. Parameters ---------- x : array_like
def main(): """ Starts the application. Return ------ bool Definition success. """ arguments = command_line_arguments() if arguments.version: print('{0} - {1}'.format(__application_name__, __version__)) return True settings = json.load(open(SETTINGS_FILE)) if arguments.settings_file is not None: assert os.path.exists( arguments.settings_file), ('"{0}" file doesn\'t exists!'.format( arguments.settings_file)) settings.update(json.load(open(arguments.settings_file))) input_linearity = arguments.input_linearity.lower() if input_linearity == 'linear': input_linear = True elif input_linearity == 'oecf': input_linear = False else: input_extension = os.path.splitext(arguments.input_image)[1].lower() if input_extension in LINEAR_IMAGE_FORMATS: input_linear = True else: input_linear = False if arguments.input_image is not None: assert os.path.exists(arguments.input_image), ( '"{0}" input image doesn\'t exists!'.format(arguments.input_image)) image_path = arguments.input_image else: image_path = DEFAULT_IMAGE_PATH try: image = read_image(str(image_path)) if not input_linear: colourspace = RGB_COLOURSPACES[arguments.input_oecf] image = colourspace.decoding_cctf(image) # Keeping RGB channels only. image = image[..., 0:3] image = image[::int(arguments.input_resample), ::int( arguments.input_resample)] except ImportError: warning( '"OpenImageIO" is not available, image reading is not supported, ' 'falling back to some random noise!') image = None if not arguments.enable_warnings: warnings.filterwarnings("ignore") ColourAnalysis(image, arguments.input_image, arguments.input_colourspace, arguments.input_oecf, input_linear, arguments.reference_colourspace, arguments.correlate_colourspace, settings, arguments.layout) return run()
def uv_to_CCT_Ohno2013(uv, cmfs=STANDARD_OBSERVERS_CMFS.get( 'CIE 1931 2 Degree Standard Observer'), start=CCT_MINIMAL, end=CCT_MAXIMAL, count=CCT_SAMPLES, iterations=CCT_CALCULATION_ITERATIONS): """ Returns the correlated colour temperature :math:`T_{cp}` and :math:`\Delta_{uv}` from given *CIE UCS* colourspace *uv* chromaticity coordinates, colour matching functions and temperature range using Ohno (2013) method. The iterations parameter defines the calculations precision: The higher its value, the more planckian tables will be generated through cascade expansion in order to converge to the exact solution. Parameters ---------- uv : array_like *CIE UCS* colourspace *uv* chromaticity coordinates. cmfs : XYZ_ColourMatchingFunctions, optional Standard observer colour matching functions. start : numeric, optional Temperature range start in kelvins. end : numeric, optional Temperature range end in kelvins. count : int, optional Temperatures count in the planckian tables. iterations : int, optional Number of planckian tables to generate. Returns ------- ndarray Correlated colour temperature :math:`T_{cp}`, :math:`\Delta_{uv}`. References ---------- .. [3] Ohno, Y. (2014). Practical Use and Calculation of CCT and Duv. LEUKOS, 10(1), 47–55. doi:10.1080/15502724.2014.839020 Examples -------- >>> from colour import STANDARD_OBSERVERS_CMFS >>> cmfs = 'CIE 1931 2 Degree Standard Observer' >>> cmfs = STANDARD_OBSERVERS_CMFS.get(cmfs) >>> uv = np.array([0.1978, 0.3122]) >>> uv_to_CCT_Ohno2013(uv, cmfs) # doctest: +ELLIPSIS array([ 6.5075470...e+03, 3.2236908...e-03]) """ # Ensuring we do at least one iteration to initialise variables. if iterations <= 0: iterations = 1 # Planckian table creation through cascade expansion. for _i in range(iterations): table = planckian_table(uv, cmfs, start, end, count) index = planckian_table_minimal_distance_index(table) if index == 0: warning( ('Minimal distance index is on lowest planckian table bound, ' 'unpredictable results may occur!')) index += 1 elif index == len(table) - 1: warning( ('Minimal distance index is on highest planckian table bound, ' 'unpredictable results may occur!')) index -= 1 start = table[index - 1].Ti end = table[index + 1].Ti _ux, vx = uv Tuvdip, Tuvdi, Tuvdin = (table[index - 1], table[index], table[index + 1]) Tip, uip, vip, dip = Tuvdip.Ti, Tuvdip.ui, Tuvdip.vi, Tuvdip.di Ti, di = Tuvdi.Ti, Tuvdi.di Tin, uin, vin, din = Tuvdin.Ti, Tuvdin.ui, Tuvdin.vi, Tuvdin.di # Triangular solution. l = np.sqrt((uin - uip) ** 2 + (vin - vip) ** 2) x = (dip ** 2 - din ** 2 + l ** 2) / (2 * l) T = Tip + (Tin - Tip) * (x / l) vtx = vip + (vin - vip) * (x / l) sign = 1 if vx - vtx >= 0 else -1 D_uv = (dip ** 2 - x ** 2) ** (1 / 2) * sign # Parabolic solution. if D_uv < 0.002: X = (Tin - Ti) * (Tip - Tin) * (Ti - Tip) a = (Tip * (din - di) + Ti * (dip - din) + Tin * (di - dip)) * X ** -1 b = (-(Tip ** 2 * (din - di) + Ti ** 2 * (dip - din) + Tin ** 2 * (di - dip)) * X ** -1) c = (-(dip * (Tin - Ti) * Ti * Tin + di * (Tip - Tin) * Tip * Tin + din * (Ti - Tip) * Tip * Ti) * X ** -1) T = -b / (2 * a) D_uv = sign * (a * T ** 2 + b * T + c) return np.array([T, D_uv])
def XYZ_to_Hunt(XYZ, XYZ_w, XYZ_b, L_A, surround=HUNT_VIEWING_CONDITIONS.get('Normal Scenes'), L_AS=None, CCT_w=None, XYZ_p=None, p=None, S=None, S_W=None, helson_judd_effect=False, discount_illuminant=True): """ Computes the *Hunt* colour appearance model correlates. Parameters ---------- XYZ : array_like, (3,) *CIE XYZ* colourspace matrix of test sample / stimulus in domain [0, 100]. XYZ_w : array_like, (3,) *CIE XYZ* colourspace matrix of reference white in domain [0, 100]. XYZ_b : array_like, (3,) *CIE XYZ* colourspace matrix of background in domain [0, 100]. L_A : numeric Adapting field *luminance* :math:`L_A` in :math:`cd/m^2`. surround : Hunt_InductionFactors, optional Surround viewing conditions induction factors. L_AS : numeric, optional Scotopic luminance :math:`L_{AS}` of the illuminant, approximated if not specified. CCT_w : numeric, optional Correlated color temperature :math:`T_{cp}`: of the illuminant, needed to approximate :math:`L_{AS}`. XYZ_p : array_like, (3,), optional *CIE XYZ* colourspace matrix of proximal field in domain [0, 100], assumed to be equal to background if not specified. p : numeric, optional Simultaneous contrast / assimilation factor :math:`p` with value in domain [-1, 0] when simultaneous contrast occurs and domain [0, 1] when assimilation occurs. S : numeric, optional Scotopic response :math:`S` to the stimulus, approximated using tristimulus values :math:`Y` of the stimulus if not specified. S_w : numeric, optional Scotopic response :math:`S_w` for the reference white, approximated using the tristimulus values :math:`Y_w` of the reference white if not specified. helson_judd_effect : bool, optional Truth value indicating whether the *Helson-Judd* effect should be accounted for. discount_illuminant : bool, optional Truth value indicating if the illuminant should be discounted. Warning ------- The input domain of that definition is non standard! Notes ----- - Input *CIE XYZ* colourspace matrix is in domain [0, 100]. - Input *CIE XYZ_b* colourspace matrix is in domain [0, 100]. - Input *CIE XYZ_w* colourspace matrix is in domain [0, 100]. - Input *CIE XYZ_p* colourspace matrix is in domain [0, 100]. Returns ------- Hunt_Specification *Hunt* colour appearance model specification. Raises ------ ValueError If an illegal arguments combination is specified. Examples -------- >>> XYZ = np.array([19.01, 20.00, 21.78]) >>> XYZ_w = np.array([95.05, 100.00, 108.88]) >>> XYZ_b = np.array([95.05, 100.00, 108.88]) >>> L_A = 318.31 >>> surround = HUNT_VIEWING_CONDITIONS['Normal Scenes'] >>> CCT_w = 6504.0 >>> XYZ_to_Hunt(XYZ, XYZ_w, XYZ_b, L_A, surround, CCT_w=CCT_w) # noqa # doctest: +ELLIPSIS Hunt_Specification(J=30.0462678..., C=0.1210508..., h=269.2737594..., s=0.0199093..., Q=22.2097654..., M=0.1238964..., H=None, HC=None) """ X, Y, Z = np.ravel(XYZ) X_b, Y_b, Z_b = np.ravel(XYZ_b) X_w, Y_w, Z_w = np.ravel(XYZ_w) # Arguments handling. if XYZ_p is not None: X_p, Y_p, Z_p = np.ravel(XYZ_p) else: X_p = X_b Y_p = Y_b Z_p = Y_b warning('Unspecified proximal field "XYZ_p" argument, using ' 'background "XYZ_b" as approximation!') if surround.N_cb is None: N_cb = 0.725 * (Y_w / Y_b) ** 0.2 warning('Unspecified "N_cb" argument, using approximation: ' '"{0}"'.format(N_cb)) if surround.N_bb is None: N_bb = 0.725 * (Y_w / Y_b) ** 0.2 warning('Unspecified "N_bb" argument, using approximation: ' '"{0}"'.format(N_bb)) if L_AS is None and CCT_w is None: raise ValueError('Either the scotopic luminance "L_AS" of the ' 'illuminant or its correlated colour temperature ' '"CCT_w" must be specified!') if L_AS is None: L_AS = illuminant_scotopic_luminance(L_A, CCT_w) warning('Unspecified "L_AS" argument, using approximation from "CCT": ' '"{0}"'.format(L_AS)) if S is None != S_W is None: raise ValueError('Either both stimulus scotopic response "S" and ' 'reference white scotopic response "S_w" arguments ' 'need to be specified or none of them!') elif S is None and S_W is None: S = Y S_W = Y_w warning('Unspecified stimulus scotopic response "S" and reference ' 'white scotopic response "S_w" arguments, using ' 'approximation: "{0}", "{1}"'.format(S, S_W)) if p is None: warning('Unspecified simultaneous contrast / assimilation "p" ' 'argument, model will not account for simultaneous chromatic ' 'contrast!') XYZ_p = np.array([X_p, Y_p, Z_p]) # Computing luminance level adaptation factor :math:`F_L`. F_L = luminance_level_adaptation_factor(L_A) # Computing test sample chromatic adaptation. rgb_a = chromatic_adaptation(XYZ, XYZ_w, XYZ_b, L_A, F_L, XYZ_p, p, helson_judd_effect, discount_illuminant) # Computing reference white chromatic adaptation. rgb_aw = chromatic_adaptation(XYZ_w, XYZ_w, XYZ_b, L_A, F_L, XYZ_p, p, helson_judd_effect, discount_illuminant) # Computing opponent colour dimensions. # Computing achromatic post adaptation signals. A_a = achromatic_post_adaptation_signal(rgb_a) A_aw = achromatic_post_adaptation_signal(rgb_aw) # Computing colour difference signals. C = colour_difference_signals(rgb_a) C_w = colour_difference_signals(rgb_aw) # ------------------------------------------------------------------------- # Computing the *hue* angle :math:`h_s`. # ------------------------------------------------------------------------- h = hue_angle(C) hue_w = hue_angle(C_w) # TODO: Implement hue quadrature & composition computation. # ------------------------------------------------------------------------- # Computing the correlate of *saturation* :math:`s`. # ------------------------------------------------------------------------- # Computing eccentricity factors. e_s = eccentricity_factor(h) e_s_w = eccentricity_factor(hue_w) # Computing low luminance tritanopia factor :math:`F_t`. F_t = low_luminance_tritanopia_factor(L_A) M_yb = yellowness_blueness_response(C, e_s, surround.N_c, N_cb, F_t) M_rg = redness_greenness_response(C, e_s, surround.N_c, N_cb) M_yb_w = yellowness_blueness_response(C_w, e_s, surround.N_c, N_cb, F_t) M_rg_w = redness_greenness_response(C_w, e_s, surround.N_c, N_cb) # Computing overall chromatic response. M = overall_chromatic_response(M_yb, M_rg) M_w = overall_chromatic_response(M_yb_w, M_rg_w) s = saturation_correlate(M, rgb_a) # ------------------------------------------------------------------------- # Computing the correlate of *brightness* :math:`Q`. # ------------------------------------------------------------------------- # Computing achromatic signal :math:`A`. A = achromatic_signal(L_AS, S, S_W, N_bb, A_a) A_w = achromatic_signal(L_AS, S_W, S_W, N_bb, A_aw) Q = brightness_correlate(A, A_w, M, surround.N_b) brightness_w = brightness_correlate(A_w, A_w, M_w, surround.N_b) # TODO: Implement whiteness-blackness :math:`Q_{wb}` computation. # ------------------------------------------------------------------------- # Computing the correlate of *Lightness* :math:`J`. # ------------------------------------------------------------------------- J = lightness_correlate(Y_b, Y_w, Q, brightness_w) # ------------------------------------------------------------------------- # Computing the correlate of *chroma* :math:`C_{94}`. # ------------------------------------------------------------------------- C_94 = chroma_correlate(s, Y_b, Y_w, Q, brightness_w) # ------------------------------------------------------------------------- # Computing the correlate of *colourfulness* :math:`M_{94}`. # ------------------------------------------------------------------------- M_94 = colourfulness_correlate(F_L, C_94) return Hunt_Specification(J, C_94, h, s, Q, M_94, None, None)
""" Property for **self.__y** private attribute. Returns ------- array_like self.__y """ return self.__y @y.setter def y(self, value): """ Setter for **self.__y** private attribute. Parameters ---------- value : array_like Attribute value. """ raise AttributeError('"{0}" attribute is read only!'.format('y')) else: warning(('"scipy.interpolate.PchipInterpolator" and ' '"scipy.interpolate.interp1d" interpolators are not available, ' 'using "LinearInterpolator" instead!')) PchipInterpolator = CubicSplineInterpolator = LinearInterpolator
def colour_quality_bars_plot(specifications, labels=True, hatching=None, hatching_repeat=1, **kwargs): """ Plots the colour quality data of given illuminants or light sources colour quality specifications. Parameters ---------- specifications : array_like Array of illuminants or light sources colour quality specifications. labels : bool, optional Add labels above bars. hatching : bool or None, optional Use hatching for the bars. hatching_repeat : int, optional Hatching pattern repeat. \**kwargs : dict, optional Keywords arguments. Returns ------- bool Definition success. Examples -------- >>> from colour import ( ... ILLUMINANTS_RELATIVE_SPDS, ... LIGHT_SOURCES_RELATIVE_SPDS) >>> illuminant = ILLUMINANTS_RELATIVE_SPDS.get('F2') >>> light_source = LIGHT_SOURCES_RELATIVE_SPDS.get('Kinoton 75P') >>> cqs_i = colour_quality_scale(illuminant, additional_data=True) >>> cqs_l = colour_quality_scale(light_source, additional_data=True) >>> colour_quality_bars_plot([cqs_i, cqs_l]) # doctest: +SKIP True """ settings = {'figure_size': (DEFAULT_FIGURE_WIDTH, DEFAULT_FIGURE_WIDTH)} settings.update(kwargs) canvas(**settings) bar_width = 0.5 y_ticks_steps = 10 count_s, count_Q_as = len(specifications), 0 patterns = cycle(DEFAULT_HATCH_PATTERNS) if hatching is None: hatching = False if count_s == 1 else True for i, specification in enumerate(specifications): Q_a, Q_as, colorimetry_data = (specification.Q_a, specification.Q_as, specification.colorimetry_data) count_Q_as = len(Q_as) colours = ([[1] * 3] + [np.clip(XYZ_to_sRGB(x.XYZ), 0, 1) for x in colorimetry_data[0]]) x = (i + np.arange(0, (count_Q_as + 1) * (count_s + 1), (count_s + 1), dtype=np.float)) * bar_width y = [s[1].Q_a for s in sorted(Q_as.items(), key=lambda s: s[0])] y = np.array([Q_a] + list(y)) if np.sign(np.min(y)) < 0: warning( ('"{0}" spectral distribution has negative "Q_a" value(s), ' 'using absolute value(s) ' 'for plotting purpose!'.format(specification.name))) y = np.abs(y) bars = pylab.bar(x, y, color=colours, width=bar_width, hatch=(next(patterns) * hatching_repeat if hatching else None), label=specification.name) if labels: label_rectangles( bars, rotation='horizontal' if count_s == 1 else 'vertical', offset=(0 if count_s == 1 else 3 / 100 * count_s + 65 / 1000, 0.025), text_size=-5 / 7 * count_s + 12.5) pylab.axhline(y=100, color='black', linestyle='--') pylab.xticks((np.arange(0, (count_Q_as + 1) * (count_s + 1), (count_s + 1), dtype=np.float) * bar_width + (count_s * bar_width / 2)), ['Qa'] + ['Q{0}'.format(index + 1) for index in range(0, count_Q_as + 1, 1)]) pylab.yticks(range(0, 100 + y_ticks_steps, y_ticks_steps)) settings.update({ 'title': 'Colour Quality', 'legend': hatching, 'x_tighten': True, 'y_tighten': True, 'limits': (-bar_width, ((count_Q_as + 1) * (count_s + 1)) / 2, 0, 120), 'aspect': 1 / (120 / (bar_width + len(Q_as) + bar_width * 2))}) settings.update(kwargs) boundaries(**settings) decorate(**settings) return display(**settings)
class TestRGBLuminance(unittest.TestCase): """ Defines :func:`colour.models.rgb.derivation.RGB_luminance` definition unit tests methods. """ def test_RGB_luminance(self): """ Tests:func:`colour.models.rgb.derivation.RGB_luminance` definition. """ self.assertAlmostEqual(RGB_luminance( np.array([50.0, 50.0, 50.0]), np.array([0.73470, 0.26530, 0.00000, 1.00000, 0.00010, -0.07700]), np.array([0.32168, 0.33767])), 50.00000000, places=7) self.assertAlmostEqual(RGB_luminance( np.array([74.6, 16.1, 100.0]), np.array([0.73470, 0.26530, 0.00000, 1.00000, 0.00010, -0.07700]), np.array([0.32168, 0.33767])), 30.17011667, places=7) self.assertAlmostEqual(RGB_luminance( np.array([40.6, 4.2, 67.4]), np.array([0.73470, 0.26530, 0.00000, 1.00000, 0.00010, -0.07700]), np.array([0.32168, 0.33767])), 12.16160184, places=7) def test_n_dimensional_RGB_luminance(self): """ Tests:func:`colour.models.rgb.derivation.RGB_luminance` definition n_dimensional arrays support. """ RGB = np.array([50.0, 50.0, 50.0]), P = np.array([0.73470, 0.26530, 0.00000, 1.00000, 0.00010, -0.07700]), W = np.array([0.32168, 0.33767]) Y = 50 np.testing.assert_almost_equal(RGB_luminance(RGB, P, W), Y) RGB = np.tile(RGB, (6, 1)) Y = np.tile(Y, 6) np.testing.assert_almost_equal(RGB_luminance(RGB, P, W), Y) RGB = np.reshape(RGB, (2, 3, 3)) Y = np.reshape(Y, (2, 3)) np.testing.assert_almost_equal(RGB_luminance(RGB, P, W), Y) @ignore_numpy_errors def test_nan_RGB_luminance(self): """ Tests :func:`colour.models.rgb.derivation.RGB_luminance` 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: RGB = np.array(case) P = np.array(np.vstack((case[0:2], case[0:2], case[0:2]))) W = np.array(case[0:2]) try: RGB_luminance(RGB, P, W) except np.linalg.linalg.LinAlgError: import traceback from colour.utilities import warning warning(traceback.format_exc())
RAW_D_CONVERSION_ARGUMENTS.__doc__ = """ Arguments for the command line raw conversion application for demosaiced linear *tiff* file format output. RAW_D_CONVERSION_ARGUMENTS : unicode """ if _IS_MACOS_PLATFORM: DNG_CONVERTER = DocstringText( '/Applications/Adobe DNG Converter.app/Contents/' 'MacOS/Adobe DNG Converter') elif _IS_WINDOWS_PLATFORM: DNG_CONVERTER = DocstringText('Adobe DNG Converter') else: DNG_CONVERTER = None warning('"Adobe DNG Converter" is not available on your platform!') if DNG_CONVERTER is not None: DNG_CONVERTER.__doc__ = """ Command line *DNG* conversion application, usually *Adobe DNG Converter*. DNG_CONVERTER : unicode """ DNG_CONVERSION_ARGUMENTS = DocstringText('-cr7.1 -l -d "{0}" "{1}"') if _IS_WINDOWS_PLATFORM: DNG_CONVERSION_ARGUMENTS = DocstringText( DNG_CONVERSION_ARGUMENTS.replace('"', '')) DNG_CONVERSION_ARGUMENTS.__doc__ = """ Arguments for the command line *dng* conversion application.
def colour_quality_bars_plot(specifications, labels=True, hatching=None, hatching_repeat=1, **kwargs): """ Plots the colour quality data of given illuminants or light sources colour quality specifications. Parameters ---------- specifications : array_like Array of illuminants or light sources colour quality specifications. labels : bool, optional Add labels above bars. hatching : bool or None, optional Use hatching for the bars. hatching_repeat : int, optional Hatching pattern repeat. 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 -------- >>> from colour import ( ... ILLUMINANTS_RELATIVE_SPDS, ... LIGHT_SOURCES_RELATIVE_SPDS, ... SpectralShape) >>> illuminant = ILLUMINANTS_RELATIVE_SPDS['F2'] >>> light_source = LIGHT_SOURCES_RELATIVE_SPDS['Kinoton 75P'] >>> light_source = light_source.clone().align(SpectralShape(360, 830, 1)) >>> cqs_i = colour_quality_scale(illuminant, additional_data=True) >>> cqs_l = colour_quality_scale(light_source, additional_data=True) >>> colour_quality_bars_plot([cqs_i, cqs_l]) # doctest: +SKIP """ settings = {'figure_size': (DEFAULT_FIGURE_WIDTH, DEFAULT_FIGURE_WIDTH)} settings.update(kwargs) canvas(**settings) bar_width = 0.5 y_ticks_interval = 10 count_s, count_Q_as = len(specifications), 0 patterns = cycle(DEFAULT_HATCH_PATTERNS) if hatching is None: hatching = False if count_s == 1 else True for i, specification in enumerate(specifications): Q_a, Q_as, colorimetry_data = (specification.Q_a, specification.Q_as, specification.colorimetry_data) count_Q_as = len(Q_as) colours = ( [[1] * 3] + [np.clip(XYZ_to_sRGB(x.XYZ), 0, 1) for x in colorimetry_data[0]]) x = (i + np.arange(0, (count_Q_as + 1) * (count_s + 1), (count_s + 1), dtype=np.float_)) * bar_width y = [s[1].Q_a for s in sorted(Q_as.items(), key=lambda s: s[0])] y = np.array([Q_a] + list(y)) if np.sign(np.min(y)) < 0: warning( ('"{0}" spectral distribution has negative "Q_a" value(s), ' 'using absolute value(s) ' 'for plotting purpose!'.format(specification.name))) y = np.abs(y) bars = pylab.bar(x, y, color=colours, width=bar_width, hatch=(next(patterns) * hatching_repeat if hatching else None), label=specification.name) if labels: label_rectangles( bars, rotation='horizontal' if count_s == 1 else 'vertical', offset=(0 if count_s == 1 else 3 / 100 * count_s + 65 / 1000, 0.025), text_size=-5 / 7 * count_s + 12.5) pylab.axhline(y=100, color='black', linestyle='--') pylab.xticks( (np.arange(0, (count_Q_as + 1) * (count_s + 1), (count_s + 1), dtype=np.float_) * bar_width + (count_s * bar_width / 2)), ['Qa'] + ['Q{0}'.format(index + 1) for index in range(0, count_Q_as + 1, 1)]) pylab.yticks(range(0, 100 + y_ticks_interval, y_ticks_interval)) settings.update({ 'title': 'Colour Quality', 'legend': hatching, 'x_tighten': True, 'y_tighten': True, 'limits': (-bar_width, ((count_Q_as + 1) * (count_s + 1)) / 2, 0, 120), 'aspect': 1 / (120 / (bar_width + len(Q_as) + bar_width * 2)) }) settings.update(kwargs) boundaries(**settings) decorate(**settings) return display(**settings)
def XYZ_to_Nayatani95(XYZ, XYZ_n, Y_o, E_o, E_or, n=1): """ Computes the Nayatani (1995) colour appearance model correlates. Parameters ---------- XYZ : array_like, (3,) *CIE XYZ* colourspace matrix of test sample / stimulus in domain [0, 100]. XYZ_n : array_like, (3,) *CIE XYZ* colourspace matrix of reference white in domain [0, 100]. Y_o : numeric Luminance factor :math:`Y_o` of achromatic background as percentage in domain [0.18, 1.0] E_o : numeric Illuminance :math:`E_o` of the viewing field in lux. E_or : numeric Normalising illuminance :math:`E_{or}` in lux usually in domain [1000, 3000] n : numeric, optional Noise term used in the non linear chromatic adaptation model. Returns ------- Nayatani95_Specification Nayatani (1995) colour appearance model specification. Warning ------- The input domain of that definition is non standard! Notes ----- - Input *CIE XYZ* colourspace matrix is in domain [0, 100]. - Input *CIE XYZ_n* colourspace matrix is in domain [0, 100]. Examples -------- >>> XYZ = np.array([19.01, 20, 21.78]) >>> XYZ_n = np.array([95.05, 100, 108.88]) >>> Y_o = 20.0 >>> E_o = 5000.0 >>> E_or = 1000.0 >>> XYZ_to_Nayatani95(XYZ, XYZ_n, Y_o, E_o, E_or) # doctest: +ELLIPSIS Nayatani95_Specification(Lstar_P=49.9998829..., C=0.0133550..., h=257.5232268..., s=0.0133550..., Q=62.6266734..., M=0.0167262..., H=None, HC=None, Lstar_N=50.0039154...) """ if not 18 <= Y_o <= 100: warning(('"Y_o" luminance factor must be in [18, 100] domain, ' 'unpredictable results may occur!')) X, Y, Z = np.ravel(XYZ) # Computing adapting luminance :math:`L_o` and normalising luminance # :math:`L_{or}` in in :math:`cd/m^2`. # L_o = illuminance_to_luminance(E_o, Y_o) L_or = illuminance_to_luminance(E_or, Y_o) # Computing :math:`\xi`, :math:`\eta`, :math:`\zeta` values. xi, eta, zeta = xez = intermediate_values(XYZ_to_xy(XYZ_n)) # Computing adapting field cone responses. RGB_o = ((Y_o * E_o) / (100 * np.pi)) * xez # Computing stimulus cone responses. R, G, B = RGB = XYZ_to_RGB_Nayatani95(np.array([X, Y, Z])) # Computing exponential factors of the chromatic adaptation. bRGB_o = exponential_factors(RGB_o) bL_or = beta_1(L_or) # Computing scaling coefficients :math:`e(R)` and :math:`e(G)` eR = scaling_coefficient(R, xi) eG = scaling_coefficient(G, eta) # Computing opponent colour dimensions. # Computing achromatic response :math:`Q`: Q_response = achromatic_response(RGB, bRGB_o, xez, bL_or, eR, eG, n) # Computing tritanopic response :math:`t`: t_response = tritanopic_response(RGB, bRGB_o, xez, n) # Computing protanopic response :math:`p`: p_response = protanopic_response(RGB, bRGB_o, xez, n) # ------------------------------------------------------------------------- # Computing the correlate of *brightness* :math:`B_r`. # ------------------------------------------------------------------------- B_r = brightness_correlate(bRGB_o, bL_or, Q_response) # Computing *brightness* :math:`B_{rw}` of ideal white. brightness_ideal_white = ideal_white_brightness_correlate(bRGB_o, xez, bL_or, n) # ------------------------------------------------------------------------- # Computing the correlate of achromatic *Lightness* :math:`L_p^\star`. # ------------------------------------------------------------------------- Lstar_P = ( achromatic_lightness_correlate(Q_response)) # ------------------------------------------------------------------------- # Computing the correlate of normalised achromatic *Lightness* # :math:`L_n^\star`. # ------------------------------------------------------------------------- Lstar_N = ( normalised_achromatic_lightness_correlate(B_r, brightness_ideal_white)) # ------------------------------------------------------------------------- # Computing the *hue* angle :math:`\\theta`. # ------------------------------------------------------------------------- theta = hue_angle(p_response, t_response) # TODO: Implement hue quadrature & composition computation. # ------------------------------------------------------------------------- # Computing the correlate of *saturation* :math:`S`. # ------------------------------------------------------------------------- S_RG, S_YB = saturation_components(theta, bL_or, t_response, p_response) S = saturation_correlate(S_RG, S_YB) # ------------------------------------------------------------------------- # Computing the correlate of *chroma* :math:`C`. # ------------------------------------------------------------------------- C_RG, C_YB = chroma_components(Lstar_P, S_RG, S_YB) C = chroma_correlate(Lstar_P, S) # ------------------------------------------------------------------------- # Computing the correlate of *colourfulness* :math:`M`. # ------------------------------------------------------------------------- # TODO: Investigate components usage. # M_RG, M_YB = colourfulness_components(C_RG, C_YB, # brightness_ideal_white) M = colourfulness_correlate(C, brightness_ideal_white) return Nayatani95_Specification(Lstar_P, C, theta, S, B_r, M, None, None, Lstar_N)
Property for **self.__y** private attribute. Returns ------- array_like self.__y """ return self.__y @y.setter def y(self, value): """ Setter for **self.__y** private attribute. Parameters ---------- value : array_like Attribute value. """ raise AttributeError('"{0}" attribute is read only!'.format('y')) else: warning(('"scipy.interpolate.PchipInterpolator" and ' '"scipy.interpolate.interp1d" interpolators are not available, ' 'using "LinearInterpolator" instead!')) PchipInterpolator = CubicSplineInterpolator = LinearInterpolator
def chromatic_adaptation_CIE1994(XYZ_1, xy_o1, xy_o2, Y_o, E_o1, E_o2, n=1): """ Adapts given stimulus *CIE XYZ_1* tristimulus values from test viewing conditions to reference viewing conditions using CIE 1994 chromatic adaptation model. Parameters ---------- XYZ : array_like *CIE XYZ* tristimulus values of test sample / stimulus in domain [0, 100]. xy_o1 : array_like Chromaticity coordinates :math:`x_{o1}` and :math:`y_{o1}` of test illuminant and background. xy_o2 : array_like Chromaticity coordinates :math:`x_{o2}` and :math:`y_{o2}` of reference illuminant and background. Y_o : numeric Luminance factor :math:`Y_o` of achromatic background as percentage in domain [18, 100]. E_o1 : numeric Test illuminance :math:`E_{o1}` in :math:`cd/m^2`. E_o2 : numeric Reference illuminance :math:`E_{o2}` in :math:`cd/m^2`. n : numeric, optional Noise component in fundamental primary system. Returns ------- ndarray Adapted *CIE XYZ_2* tristimulus values of test stimulus. Warning ------- The input domain of that definition is non standard! Notes ----- - Input *CIE XYZ_1* tristimulus values are in domain [0, 100]. - Output *CIE XYZ_2* tristimulus values are in domain [0, 100]. Examples -------- >>> XYZ_1 = np.array([28.00, 21.26, 5.27]) >>> xy_o1 = np.array([0.4476, 0.4074]) >>> xy_o2 = np.array([0.3127, 0.3290]) >>> Y_o = 20 >>> E_o1 = 1000 >>> E_o2 = 1000 >>> chromatic_adaptation_CIE1994(XYZ_1, xy_o1, xy_o2, Y_o, E_o1, E_o2) # noqa # doctest: +ELLIPSIS array([ 24.0337952..., 21.1562121..., 17.6430119...]) """ Y_o = np.asarray(Y_o) E_o1 = np.asarray(E_o1) E_o2 = np.asarray(E_o2) if np.any(Y_o < 18) or np.any(Y_o > 100): warning(('"Y_o" luminance factor must be in [18, 100] domain, ' 'unpredictable results may occur!')) RGB_1 = XYZ_to_RGB_cie1994(XYZ_1) xez_1 = intermediate_values(xy_o1) xez_2 = intermediate_values(xy_o2) RGB_o1 = effective_adapting_responses(xez_1, Y_o, E_o1) RGB_o2 = effective_adapting_responses(xez_2, Y_o, E_o2) bRGB_o1 = exponential_factors(RGB_o1) bRGB_o2 = exponential_factors(RGB_o2) K = K_coefficient(xez_1, xez_2, bRGB_o1, bRGB_o2, Y_o, n) RGB_2 = corresponding_colour( RGB_1, xez_1, xez_2, bRGB_o1, bRGB_o2, Y_o, K, n) XYZ_2 = RGB_to_XYZ_cie1994(RGB_2) return XYZ_2