From c3245b1830323991823f20580f81eee856dcc04c Mon Sep 17 00:00:00 2001 From: Thomas Mansencal Date: Sun, 24 May 2020 11:47:09 +1200 Subject: [PATCH] Ensure that most "Colour" honours float precision change. --- colour/adaptation/fairchild1990.py | 7 +- colour/algebra/geometry.py | 6 +- colour/appearance/cam16.py | 6 +- colour/appearance/ciecam02.py | 14 +- colour/appearance/hunt.py | 12 +- colour/characterisation/correction.py | 20 +-- colour/colorimetry/generation.py | 6 +- colour/colorimetry/photometry.py | 5 +- colour/colorimetry/tristimulus.py | 1 + colour/constants/common.py | 7 +- colour/continuous/multi_signals.py | 4 - colour/continuous/signal.py | 78 ++++----- colour/continuous/tests/test_multi_signal.py | 6 +- colour/continuous/tests/test_signal.py | 6 +- colour/corresponding/prediction.py | 9 +- colour/geometry/primitives.py | 43 +++-- colour/geometry/vertices.py | 8 +- colour/graph/conversion.py | 6 +- colour/io/image.py | 1 + colour/io/luts/lut.py | 6 +- colour/models/cie_luv.py | 15 +- colour/models/cie_ucs.py | 13 +- colour/models/cie_xyy.py | 9 +- colour/models/rgb/cmyk.py | 4 +- colour/models/rgb/cylindrical.py | 4 +- colour/models/rgb/derivation.py | 7 +- colour/notation/hexadecimal.py | 6 +- colour/notation/munsell.py | 30 ++-- colour/plotting/colorimetry.py | 4 +- colour/plotting/temperature.py | 4 +- colour/plotting/volume.py | 55 +++--- colour/quality/ssi.py | 3 +- colour/utilities/__init__.py | 39 ++++- colour/utilities/array.py | 175 ++++++++++++++++++- colour/utilities/tests/test_array.py | 113 +++++++++++- colour/volume/spectrum.py | 11 +- docs/colour.utilities.rst | 5 + 37 files changed, 524 insertions(+), 224 deletions(-) diff --git a/colour/adaptation/fairchild1990.py b/colour/adaptation/fairchild1990.py index c277b9ea47..9483545e05 100644 --- a/colour/adaptation/fairchild1990.py +++ b/colour/adaptation/fairchild1990.py @@ -22,7 +22,7 @@ from colour.algebra import spow from colour.adaptation import VON_KRIES_CAT -from colour.utilities import (as_float_array, dot_vector, from_range_100, +from colour.utilities import (as_float_array, dot_vector, from_range_100, ones, row_as_diagonal, to_domain_100, tsplit, tstack) __author__ = 'Colour Developers' @@ -231,14 +231,15 @@ def degrees_of_adaptation(LMS, Y_n, v=1 / 3, discount_illuminant=False): LMS = as_float_array(LMS) if discount_illuminant: - return np.ones(LMS.shape) + return ones(LMS.shape) Y_n = as_float_array(Y_n) v = as_float_array(v) L, M, S = tsplit(LMS) - LMS_E = dot_vector(VON_KRIES_CAT, np.ones(LMS.shape)) # E illuminant. + # E illuminant. + LMS_E = dot_vector(VON_KRIES_CAT, ones(LMS.shape)) L_E, M_E, S_E = tsplit(LMS_E) Ye_n = spow(Y_n, v) diff --git a/colour/algebra/geometry.py b/colour/algebra/geometry.py index 9af24e60f3..7f32407107 100644 --- a/colour/algebra/geometry.py +++ b/colour/algebra/geometry.py @@ -40,8 +40,8 @@ import numpy as np from collections import namedtuple -from colour.utilities import (CaseInsensitiveMapping, as_float_array, tsplit, - tstack) +from colour.utilities import (CaseInsensitiveMapping, as_float_array, ones, + tsplit, tstack) __author__ = 'Colour Developers' __copyright__ = 'Copyright (C) 2013-2020 - Colour Developers' @@ -492,7 +492,7 @@ def ellipse_fitting_Halir1998(a): # Quadratic part of the design matrix. D1 = tstack([x ** 2, x * y, y ** 2]) # Linear part of the design matrix. - D2 = tstack([x, y, np.ones(x.shape)]) + D2 = tstack([x, y, ones(x.shape)]) D1_T = np.transpose(D1) D2_T = np.transpose(D2) diff --git a/colour/appearance/cam16.py b/colour/appearance/cam16.py index 3465a39a60..7bbe40da40 100644 --- a/colour/appearance/cam16.py +++ b/colour/appearance/cam16.py @@ -38,7 +38,7 @@ viewing_condition_dependent_parameters) from colour.utilities import (CaseInsensitiveMapping, as_float_array, as_namedtuple, dot_vector, from_range_100, - from_range_degrees, to_domain_100, + from_range_degrees, ones, to_domain_100, to_domain_degrees, tsplit) __author__ = 'Colour Developers' @@ -244,7 +244,7 @@ def XYZ_to_CAM16(XYZ, # Computing degree of adaptation :math:`D`. D = (np.clip(degree_of_adaptation(surround.F, L_A), 0, 1) - if not discount_illuminant else np.ones(L_A.shape)) + if not discount_illuminant else ones(L_A.shape)) n, F_L, N_bb, N_cb, z = tsplit( viewing_condition_dependent_parameters(Y_b, Y_w, L_A)) @@ -419,7 +419,7 @@ def CAM16_to_XYZ(CAM16_specification, # Computing degree of adaptation :math:`D`. D = (np.clip(degree_of_adaptation(surround.F, L_A), 0, 1) - if not discount_illuminant else np.ones(L_A.shape)) + if not discount_illuminant else ones(L_A.shape)) n, F_L, N_bb, N_cb, z = tsplit( viewing_condition_dependent_parameters(Y_b, Y_w, L_A)) diff --git a/colour/appearance/ciecam02.py b/colour/appearance/ciecam02.py index 1af832c243..7748bae39d 100644 --- a/colour/appearance/ciecam02.py +++ b/colour/appearance/ciecam02.py @@ -38,8 +38,8 @@ from colour.constants import EPSILON from colour.utilities import ( CaseInsensitiveMapping, as_float_array, as_int_array, as_namedtuple, - as_float, from_range_degrees, dot_matrix, dot_vector, from_range_100, - to_domain_100, to_domain_degrees, tsplit, tstack) + as_float, from_range_degrees, dot_matrix, dot_vector, from_range_100, ones, + to_domain_100, to_domain_degrees, tsplit, tstack, zeros) __author__ = 'Colour Developers' __copyright__ = 'Copyright (C) 2013-2020 - Colour Developers' @@ -265,7 +265,7 @@ def XYZ_to_CIECAM02(XYZ, # Computing degree of adaptation :math:`D`. D = (degree_of_adaptation(surround.F, L_A) - if not discount_illuminant else np.ones(L_A.shape)) + if not discount_illuminant else ones(L_A.shape)) # Computing full chromatic adaptation. RGB_c = full_chromatic_adaptation_forward(RGB, RGB_w, Y_w, D) @@ -437,7 +437,7 @@ def CIECAM02_to_XYZ(CIECAM02_specification, # Computing degree of adaptation :math:`D`. D = (degree_of_adaptation(surround.F, L_A) - if not discount_illuminant else np.ones(L_A.shape)) + if not discount_illuminant else ones(L_A.shape)) # Computing full chromatic adaptation. RGB_wc = full_chromatic_adaptation_forward(RGB_w, RGB_w, Y_w, D) @@ -900,8 +900,8 @@ def opponent_colour_dimensions_inverse(P_n, h): P_5 = P_1 / cos_hr n = P_2 * (2 + P_3) * (460 / 1403) - a = np.zeros(hr.shape) - b = np.zeros(hr.shape) + a = zeros(hr.shape) + b = zeros(hr.shape) b = np.where( np.isfinite(P_1) * np.abs(sin_hr) >= np.abs(cos_hr), @@ -1456,7 +1456,7 @@ def P(N_c, N_cb, e_t, t, A, N_bb): P_1 = ((50000 / 13) * N_c * N_cb * e_t) / t P_2 = A / N_bb + 0.305 - P_3 = np.ones(P_1.shape) * (21 / 20) + P_3 = ones(P_1.shape) * (21 / 20) P_n = tstack([P_1, P_2, P_3]) diff --git a/colour/appearance/hunt.py b/colour/appearance/hunt.py index a7400b5041..c92928efbc 100644 --- a/colour/appearance/hunt.py +++ b/colour/appearance/hunt.py @@ -24,9 +24,9 @@ from collections import namedtuple from colour.algebra import spow -from colour.utilities import (CaseInsensitiveMapping, as_float_array, - dot_vector, from_range_degrees, to_domain_100, - tsplit, tstack, usage_warning) +from colour.utilities import ( + CaseInsensitiveMapping, as_float_array, dot_vector, from_range_degrees, + ones, to_domain_100, tsplit, tstack, usage_warning, zeros) __author__ = 'Colour Developers' __copyright__ = 'Copyright (C) 2013-2020 - Colour Developers' @@ -325,7 +325,7 @@ def XYZ_to_Hunt(XYZ, >>> XYZ_b = np.array([95.05, 100.00, 108.88]) >>> L_A = 318.31 >>> surround = HUNT_VIEWING_CONDITIONS['Normal Scenes'] - >>> CCT_w = 6504.0 + >>> CCT_w = 6504 >>> XYZ_to_Hunt(XYZ, XYZ_w, XYZ_b, L_A, surround, CCT_w=CCT_w) ... # doctest: +ELLIPSIS Hunt_Specification(J=30.0462678..., C=0.1210508..., h=269.2737594..., \ @@ -662,14 +662,14 @@ def chromatic_adaptation(XYZ, L_A_p = spow(L_A, 1 / 3) F_rgb = ((1 + L_A_p + h_rgb) / (1 + L_A_p + (1 / h_rgb))) else: - F_rgb = np.ones(h_rgb.shape) + F_rgb = ones(h_rgb.shape) # Computing Helson-Judd effect parameters. if helson_judd_effect: D_rgb = (f_n((Y_b / Y_w) * F_L * F_rgb[..., 1]) - f_n( (Y_b / Y_w) * F_L * F_rgb)) else: - D_rgb = np.zeros(F_rgb.shape) + D_rgb = zeros(F_rgb.shape) # Computing cone bleach factors. B_rgb = (10 ** 7) / ((10 ** 7) + 5 * L_A[..., np.newaxis] * (rgb_w / 100)) diff --git a/colour/characterisation/correction.py b/colour/characterisation/correction.py index 6c0bfb3841..5ed8ba802c 100644 --- a/colour/characterisation/correction.py +++ b/colour/characterisation/correction.py @@ -63,7 +63,7 @@ from colour.algebra import least_square_mapping_MoorePenrose from colour.utilities import (CaseInsensitiveMapping, as_float_array, as_int, - closest, filter_kwargs, tsplit, tstack) + closest, filter_kwargs, ones, tsplit, tstack) __author__ = 'Colour Developers' __copyright__ = 'Copyright (C) 2013-2020 - Colour Developers' @@ -119,7 +119,7 @@ def augmented_matrix_Cheung2004(RGB, terms=3): """ R, G, B = tsplit(RGB) - ones = np.ones(R.shape) + tail = ones(R.shape) existing_terms = np.array([3, 5, 7, 8, 10, 11, 14, 16, 17, 19, 20, 22]) closest_terms = as_int(closest(existing_terms, terms)) @@ -132,23 +132,23 @@ def augmented_matrix_Cheung2004(RGB, terms=3): if terms == 3: return RGB elif terms == 5: - return tstack([R, G, B, R * G * B, ones]) + return tstack([R, G, B, R * G * B, tail]) elif terms == 7: - return tstack([R, G, B, R * G, R * B, G * B, ones]) + return tstack([R, G, B, R * G, R * B, G * B, tail]) elif terms == 8: - return tstack([R, G, B, R * G, R * B, G * B, R * G * B, ones]) + return tstack([R, G, B, R * G, R * B, G * B, R * G * B, tail]) elif terms == 10: return tstack( - [R, G, B, R * G, R * B, G * B, R ** 2, G ** 2, B ** 2, ones]) + [R, G, B, R * G, R * B, G * B, R ** 2, G ** 2, B ** 2, tail]) elif terms == 11: return tstack([ R, G, B, R * G, R * B, G * B, R ** 2, G ** 2, B ** 2, R * G * B, - ones + tail ]) elif terms == 14: return tstack([ R, G, B, R * G, R * B, G * B, R ** 2, G ** 2, B ** 2, R * G * B, R - ** 3, G ** 3, B ** 3, ones + ** 3, G ** 3, B ** 3, tail ]) elif terms == 16: return tstack([ @@ -158,7 +158,7 @@ def augmented_matrix_Cheung2004(RGB, terms=3): elif terms == 17: return tstack([ R, G, B, R * G, R * B, G * B, R ** 2, G ** 2, B ** 2, R * G * B, - R ** 2 * G, G ** 2 * B, B ** 2 * R, R ** 3, G ** 3, B ** 3, ones + R ** 2 * G, G ** 2 * B, B ** 2 * R, R ** 3, G ** 3, B ** 3, tail ]) elif terms == 19: return tstack([ @@ -170,7 +170,7 @@ def augmented_matrix_Cheung2004(RGB, terms=3): return tstack([ R, G, B, R * G, R * B, G * B, R ** 2, G ** 2, B ** 2, R * G * B, R ** 2 * G, G ** 2 * B, B ** 2 * R, R ** 2 * B, G ** 2 * R, - B ** 2 * G, R ** 3, G ** 3, B ** 3, ones + B ** 2 * G, R ** 3, G ** 3, B ** 3, tail ]) elif terms == 22: return tstack([ diff --git a/colour/colorimetry/generation.py b/colour/colorimetry/generation.py index d3d5b6aaa6..ae41b7b3a5 100644 --- a/colour/colorimetry/generation.py +++ b/colour/colorimetry/generation.py @@ -36,7 +36,7 @@ from colour.constants import DEFAULT_FLOAT_DTYPE from colour.colorimetry import (DEFAULT_SPECTRAL_SHAPE, SpectralDistribution) -from colour.utilities import CaseInsensitiveMapping, as_float_array +from colour.utilities import CaseInsensitiveMapping, as_float_array, full, ones __author__ = 'Colour Developers' __copyright__ = 'Copyright (C) 2013-2020 - Colour Developers' @@ -90,7 +90,7 @@ def sd_constant(k, shape=DEFAULT_SPECTRAL_SHAPE, dtype=None): dtype = DEFAULT_FLOAT_DTYPE wavelengths = shape.range(dtype) - values = np.full(len(wavelengths), k, dtype) + values = full(len(wavelengths), k, dtype) name = '{0} Constant'.format(k) return SpectralDistribution(values, wavelengths, name=name, dtype=dtype) @@ -485,7 +485,7 @@ def sd_multi_leds_Ohno2005(peak_wavelengths, peak_wavelengths = as_float_array(peak_wavelengths) fwhm = np.resize(fwhm, peak_wavelengths.shape) if peak_power_ratios is None: - peak_power_ratios = np.ones(peak_wavelengths.shape) + peak_power_ratios = ones(peak_wavelengths.shape) else: peak_power_ratios = np.resize(peak_power_ratios, peak_wavelengths.shape) diff --git a/colour/colorimetry/photometry.py b/colour/colorimetry/photometry.py index 0598c3d645..a7cf74a77d 100644 --- a/colour/colorimetry/photometry.py +++ b/colour/colorimetry/photometry.py @@ -20,6 +20,7 @@ from colour.colorimetry import PHOTOPIC_LEFS from colour.constants import K_M +from colour.utilities import as_float __author__ = 'Colour Developers' __copyright__ = 'Copyright (C) 2013-2020 - Colour Developers' @@ -75,7 +76,7 @@ def luminous_flux(sd, flux = K_m * np.trapz(sd.values, sd.wavelengths) - return flux + return as_float(flux) def luminous_efficiency( @@ -155,4 +156,4 @@ def luminous_efficacy( efficacy = K_M * luminous_efficiency(sd, lef) - return efficacy + return as_float(efficacy) diff --git a/colour/colorimetry/tristimulus.py b/colour/colorimetry/tristimulus.py index 8284959d76..58a7a6575f 100644 --- a/colour/colorimetry/tristimulus.py +++ b/colour/colorimetry/tristimulus.py @@ -498,6 +498,7 @@ def sd_to_XYZ_integration( S = illuminant.values x_bar, y_bar, z_bar = tsplit(cmfs.values) R = sd.values + dw = cmfs.shape.interval k = 100 / (np.sum(y_bar * S) * dw) if k is None else k diff --git a/colour/constants/common.py b/colour/constants/common.py index ad1b851880..0b5405ab6c 100644 --- a/colour/constants/common.py +++ b/colour/constants/common.py @@ -8,6 +8,7 @@ from __future__ import division, unicode_literals +import os import numpy as np from colour.utilities.documentation import DocstringFloat @@ -46,14 +47,16 @@ EPSILON : numeric """ -DEFAULT_FLOAT_DTYPE = np.float_ +DEFAULT_FLOAT_DTYPE = np.sctypeDict.get( + os.environ.get('COLOUR_SCIENCE__FLOAT_PRECISION', 'float64'), 'float64') """ Default floating point number dtype. DEFAULT_FLOAT_DTYPE : type """ -DEFAULT_INT_DTYPE = np.int_ +DEFAULT_INT_DTYPE = np.sctypeDict.get( + os.environ.get('COLOUR_SCIENCE__INT_PRECISION', 'int64'), 'int64') """ Default integer number dtype. diff --git a/colour/continuous/multi_signals.py b/colour/continuous/multi_signals.py index 7c2ea90a1f..95a538c46e 100644 --- a/colour/continuous/multi_signals.py +++ b/colour/continuous/multi_signals.py @@ -1318,10 +1318,6 @@ def multi_signals_unpack_data(data=None, if dtype is None: dtype = DEFAULT_FLOAT_DTYPE - assert dtype in np.sctypes['float'], ( - '"dtype" must be one of the following types: {0}'.format( - np.sctypes['float'])) - domain_u, range_u, signals = None, None, None signals = OrderedDict() # TODO: Implement support for Signal class passing. diff --git a/colour/continuous/signal.py b/colour/continuous/signal.py index ce9cca77b2..20aefc8db4 100644 --- a/colour/continuous/signal.py +++ b/colour/continuous/signal.py @@ -30,7 +30,7 @@ from colour.algebra import Extrapolator, KernelInterpolator from colour.constants import DEFAULT_FLOAT_DTYPE from colour.continuous import AbstractContinuousFunction -from colour.utilities import (as_array, fill_nan, is_pandas_installed, +from colour.utilities import (as_array, fill_nan, full, is_pandas_installed, runtime_warning, tsplit, tstack, usage_warning) from colour.utilities.deprecation import ObjectRenamed @@ -216,7 +216,7 @@ def __init__(self, data=None, domain=None, **kwargs): self.domain, self.range = self.signal_unpack_data(data, domain) - self.dtype = kwargs.get('dtype') + self.dtype = kwargs.get('dtype', DEFAULT_FLOAT_DTYPE) self.interpolator = kwargs.get('interpolator') self.interpolator_kwargs = kwargs.get('interpolator_kwargs') @@ -250,16 +250,10 @@ def dtype(self, value): """ if value is not None: - float_dtypes = [] - for float_dtype in ['float16', 'float32', 'float64', 'float128']: - if hasattr(np, float_dtype): - float_dtypes.append(getattr(np, float_dtype)) - assert value in float_dtypes, (( - '"{0}" attribute: "{1}" type is not in "{2}"!').format( - 'dtype', value, ', '.join([ - float_dtype.__name__ for float_dtype in float_dtypes - ]))) + assert value in np.sctypes['float'], ( + '"dtype" must be one of the following types: {0}'.format( + np.sctypes['float'])) self._dtype = value @@ -295,25 +289,26 @@ def domain(self, value): """ if value is not None: - if not np.all(np.isfinite(value)): - runtime_warning( - '"{0}" new "domain" variable is not finite: {1}, ' - 'unpredictable results may occur!'.format( - self.name, value)) + if np.asarray(value).dtype != object: + if not np.all(np.isfinite(value)): + runtime_warning( + '"{0}" new "domain" variable is not finite: {1}, ' + 'unpredictable results may occur!'.format( + self.name, value)) - value = np.copy(value).astype(self.dtype) + value = np.copy(value).astype(self.dtype) - if self._range is not None: - if value.size != self._range.size: - runtime_warning( - '"{0}" new "domain" and current "range" variables ' - 'have different size, "range" variable will be ' - 'resized to "domain" variable shape!'.format( - self.name)) - self._range = np.resize(self._range, value.shape) + if self._range is not None: + if value.size != self._range.size: + runtime_warning( + '"{0}" new "domain" and current "range" variables ' + 'have different size, "range" variable will be ' + 'resized to "domain" variable shape!'.format( + self.name)) + self._range = np.resize(self._range, value.shape) - self._domain = value - self._create_function() + self._domain = value + self._create_function() @property def range(self): @@ -342,20 +337,21 @@ def range(self, value): """ if value is not None: - if not np.all(np.isfinite(value)): - runtime_warning( - '"{0}" new "range" variable is not finite: {1}, ' - 'unpredictable results may occur!'.format( - self.name, value)) + if np.asarray(value).dtype != object: + if not np.all(np.isfinite(value)): + runtime_warning( + '"{0}" new "range" variable is not finite: {1}, ' + 'unpredictable results may occur!'.format( + self.name, value)) - value = np.copy(value).astype(self.dtype) + 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!') + 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() + self._range = value + self._create_function() @property def interpolator(self): @@ -1028,7 +1024,7 @@ def arithmetical_operation(self, a, operation, in_place=False): if isinstance(a, Signal): self[self._domain] = operation(self._range, a[self._domain]) exclusive_or = np.setxor1d(self._domain, a.domain) - self[exclusive_or] = np.full(exclusive_or.shape, np.nan) + self[exclusive_or] = full(exclusive_or.shape, np.nan) else: self.range = ioperator(self.range, a) @@ -1116,10 +1112,6 @@ def signal_unpack_data(data=None, domain=None, dtype=None): if dtype is None: dtype = DEFAULT_FLOAT_DTYPE - assert dtype in np.sctypes['float'], ( - '"dtype" must be one of the following types: {0}'.format( - np.sctypes['float'])) - domain_u, range_u = None, None if isinstance(data, Signal): domain_u = data.domain diff --git a/colour/continuous/tests/test_multi_signal.py b/colour/continuous/tests/test_multi_signal.py index 72ccd97a54..ac4839e9ef 100644 --- a/colour/continuous/tests/test_multi_signal.py +++ b/colour/continuous/tests/test_multi_signal.py @@ -79,11 +79,11 @@ def test_dtype(self): property. """ - self.assertEqual(self._multi_signals.dtype, None) + self.assertEqual(self._multi_signals.dtype, DEFAULT_FLOAT_DTYPE) multi_signals = self._multi_signals.copy() - multi_signals.dtype = DEFAULT_FLOAT_DTYPE - self.assertEqual(multi_signals.dtype, DEFAULT_FLOAT_DTYPE) + multi_signals.dtype = np.float32 + self.assertEqual(multi_signals.dtype, np.float32) def test_domain(self): """ diff --git a/colour/continuous/tests/test_signal.py b/colour/continuous/tests/test_signal.py index 7413407be3..7f6e6c89a8 100644 --- a/colour/continuous/tests/test_signal.py +++ b/colour/continuous/tests/test_signal.py @@ -72,11 +72,11 @@ def test_dtype(self): Tests :func:`colour.continuous.signal.Signal.dtype` property. """ - self.assertEqual(self._signal.dtype, None) + self.assertEqual(self._signal.dtype, DEFAULT_FLOAT_DTYPE) signal = self._signal.copy() - signal.dtype = DEFAULT_FLOAT_DTYPE - self.assertEqual(signal.dtype, DEFAULT_FLOAT_DTYPE) + signal.dtype = np.float32 + self.assertEqual(signal.dtype, np.float32) def test_domain(self): """ diff --git a/colour/corresponding/prediction.py b/colour/corresponding/prediction.py index 7b38fe4398..1822e01b8d 100644 --- a/colour/corresponding/prediction.py +++ b/colour/corresponding/prediction.py @@ -47,7 +47,7 @@ from colour.models import (Luv_to_uv, Luv_uv_to_xy, XYZ_to_Luv, XYZ_to_xy, xy_to_XYZ, xyY_to_XYZ) from colour.utilities import (CaseInsensitiveMapping, domain_range_scale, - filter_kwargs, is_numeric) + filter_kwargs, full, is_numeric) __author__ = 'Colour Developers' __copyright__ = 'Copyright (C) 2013-2020 - Colour Developers' @@ -212,10 +212,9 @@ def convert_experiment_results_Breneman1987(experiment): B_r = B_t = 0.3 XYZ_t, XYZ_r = xy_to_XYZ( - np.hstack([ - Luv_uv_to_xy(illuminant_chromaticities[1:3]), - np.full([2, 1], Y_r) - ])) / Y_r + np.hstack( + [Luv_uv_to_xy(illuminant_chromaticities[1:3]), + full([2, 1], Y_r)])) / Y_r xyY_cr, xyY_ct = [], [] for i, experiment_result in enumerate(experiment_results): diff --git a/colour/geometry/primitives.py b/colour/geometry/primitives.py index fab551965b..1bde0458d5 100644 --- a/colour/geometry/primitives.py +++ b/colour/geometry/primitives.py @@ -22,7 +22,8 @@ import numpy as np -from colour.utilities import CaseInsensitiveMapping, filter_kwargs +from colour.constants import DEFAULT_INT_DTYPE, DEFAULT_FLOAT_DTYPE +from colour.utilities import CaseInsensitiveMapping, filter_kwargs, ones, zeros __author__ = 'Colour Developers' __copyright__ = 'Copyright (C) 2013-2020 - Colour Developers' @@ -116,9 +117,9 @@ def primitive_grid(width=1, y_grid1 = y_grid + 1 # Positions, normals and uvs. - positions = np.zeros(x_grid1 * y_grid1 * 3) - normals = np.zeros(x_grid1 * y_grid1 * 3) - uvs = np.zeros(x_grid1 * y_grid1 * 2) + positions = zeros(x_grid1 * y_grid1 * 3) + normals = zeros(x_grid1 * y_grid1 * 3) + uvs = zeros(x_grid1 * y_grid1 * 2) y = np.arange(y_grid1) * height / y_grid - height / 2 x = np.arange(x_grid1) * width / x_grid - width / 2 @@ -167,15 +168,15 @@ def primitive_grid(width=1, np.interp(vertex_colours, (np.min(vertex_colours), np.max(vertex_colours)), (0, 1)), positions.shape), - np.ones((positions.shape[0], 1)) + ones([positions.shape[0], 1]) ]) vertex_colours[..., zero_axis] = 0 - vertices = np.zeros(positions.shape[0], [ - ('position', np.float32, 3), - ('uv', np.float32, 2), - ('normal', np.float32, 3), - ('colour', np.float32, 4), + vertices = zeros(positions.shape[0], [ + ('position', DEFAULT_FLOAT_DTYPE, 3), + ('uv', DEFAULT_FLOAT_DTYPE, 2), + ('normal', DEFAULT_FLOAT_DTYPE, 3), + ('colour', DEFAULT_FLOAT_DTYPE, 4), ]) vertices['position'] = positions @@ -321,12 +322,12 @@ def primitive_cube(width=1, planes_m.append(list(primitive_grid(depth, height, d_s, h_s, '+x'))) planes_m[-1][0]['position'][..., 0] += width / 2 - positions = np.zeros((0, 3), dtype=np.float32) - uvs = np.zeros((0, 2), dtype=np.float32) - normals = np.zeros((0, 3), dtype=np.float32) + positions = zeros([0, 3]) + uvs = zeros([0, 2]) + normals = zeros([0, 3]) - faces = np.zeros((0, 3), dtype=np.uint32) - outline = np.zeros((0, 2), dtype=np.uint32) + faces = zeros([0, 3], dtype=DEFAULT_INT_DTYPE) + outline = zeros([0, 2], dtype=DEFAULT_INT_DTYPE) offset = 0 for vertices_p, faces_p, outline_p in planes_m: @@ -338,12 +339,10 @@ def primitive_cube(width=1, outline = np.vstack([outline, outline_p + offset]) offset += vertices_p['position'].shape[0] - vertices = np.zeros(positions.shape[0], [ - ('position', np.float32, 3), - ('uv', np.float32, 2), - ('normal', np.float32, 3), - ('colour', np.float32, 4), - ]) + vertices = zeros(positions.shape[0], [('position', DEFAULT_FLOAT_DTYPE, 3), + ('uv', DEFAULT_FLOAT_DTYPE, 2), + ('normal', DEFAULT_FLOAT_DTYPE, 3), + ('colour', DEFAULT_FLOAT_DTYPE, 4)]) vertex_colours = np.ravel(positions) vertex_colours = np.hstack([ @@ -351,7 +350,7 @@ def primitive_cube(width=1, np.interp(vertex_colours, (np.min(vertex_colours), np.max(vertex_colours)), (0, 1)), positions.shape), - np.ones([positions.shape[0], 1]) + ones([positions.shape[0], 1]) ]) vertices['position'] = positions diff --git a/colour/geometry/vertices.py b/colour/geometry/vertices.py index 075cc31dce..2b2d02461d 100644 --- a/colour/geometry/vertices.py +++ b/colour/geometry/vertices.py @@ -20,7 +20,7 @@ from colour.algebra import spherical_to_cartesian from colour.geometry import PLANE_TO_AXIS_MAPPING from colour.utilities import (CaseInsensitiveMapping, as_float_array, - filter_kwargs, tsplit, tstack) + filter_kwargs, full, ones, tsplit, tstack, zeros) __author__ = 'Colour Developers' __copyright__ = 'Copyright (C) 2013-2020 - Colour Developers' @@ -359,16 +359,16 @@ def primitive_vertices_sphere(radius=0.5, np.radians(np.linspace(0, 180, segments * 2 + 1)[1::2][1:-1]), (segments + 1, 1)) theta = np.hstack([ - np.zeros([segments + 1, 1]), + zeros([segments + 1, 1]), theta, - np.full([segments + 1, 1], np.pi), + full([segments + 1, 1], np.pi), ]) phi = np.transpose( np.tile( np.radians(np.linspace(-180, 180, segments + 1)) + np.radians( 360 / segments / 2), (segments, 1))) - rho = np.ones(phi.shape) * radius + rho = ones(phi.shape) * radius rho_theta_phi = tstack([rho, theta, phi]) vertices = spherical_to_cartesian(rho_theta_phi) diff --git a/colour/graph/conversion.py b/colour/graph/conversion.py index 258453f8b0..004cd1b6e2 100644 --- a/colour/graph/conversion.py +++ b/colour/graph/conversion.py @@ -460,7 +460,8 @@ def RGB_luminance_to_RGB(Y): XYZ_to_Hunt, XYZ_w=_DEFAULT_ILLUMINANT_XYZ, XYZ_b=_DEFAULT_ILLUMINANT_XYZ, - L_A=80 * 0.2)), + L_A=80 * 0.2, + CCT_w=6504)), ('CIE XYZ', 'ATD95', partial( XYZ_to_ATD95, @@ -505,7 +506,8 @@ def RGB_luminance_to_RGB(Y): Y_o=0.2, E_o=1000, E_or=1000)), - ('CIE XYZ', 'RLAB', XYZ_to_RLAB), + ('CIE XYZ', 'RLAB', + partial(XYZ_to_RLAB, XYZ_n=_DEFAULT_ILLUMINANT_XYZ, Y_n=20)), ('CIECAM02 JMh', 'CAM02LCD', JMh_CIECAM02_to_CAM02LCD), ('CAM02LCD', 'CIECAM02 JMh', CAM02LCD_to_JMh_CIECAM02), ('CIECAM02 JMh', 'CAM02SCD', JMh_CIECAM02_to_CAM02SCD), diff --git a/colour/io/image.py b/colour/io/image.py index a7d61680dc..f94adf8118 100644 --- a/colour/io/image.py +++ b/colour/io/image.py @@ -62,6 +62,7 @@ def __new__(cls, name, value, type_=None): cls, name, value, type_) +# TODO: Overhaul by using "np.sctypeDict". if is_openimageio_installed(): # pragma: no cover from OpenImageIO import UINT8, UINT16, HALF, FLOAT diff --git a/colour/io/luts/lut.py b/colour/io/luts/lut.py index 518f6829a0..788c609705 100644 --- a/colour/io/luts/lut.py +++ b/colour/io/luts/lut.py @@ -38,8 +38,8 @@ from colour.algebra import LinearInterpolator, table_interpolation_trilinear from colour.constants import DEFAULT_INT_DTYPE from colour.utilities import (as_float_array, is_numeric, is_iterable, - is_string, linear_conversion, runtime_warning, - tsplit, tstack, usage_warning) + is_string, full, linear_conversion, + runtime_warning, tsplit, tstack, usage_warning) from colour.utilities.deprecation import handle_arguments_deprecation __author__ = 'Colour Developers' @@ -2037,7 +2037,7 @@ def LUT_to_LUT(LUT, cls, force_conversion=False, **kwargs): del kwargs['size'] channel_weights = as_float_array( - kwargs.get('channel_weights', np.full(3, 1 / 3))) + kwargs.get('channel_weights', full(3, 1 / 3))) if 'channel_weights' in kwargs: del kwargs['channel_weights'] diff --git a/colour/models/cie_luv.py b/colour/models/cie_luv.py index 526c7f29c1..fdbb946a55 100644 --- a/colour/models/cie_luv.py +++ b/colour/models/cie_luv.py @@ -31,14 +31,11 @@ from __future__ import division, unicode_literals -import numpy as np - from colour.colorimetry import (ILLUMINANTS, lightness_CIE1976, luminance_CIE1976) -from colour.constants import DEFAULT_FLOAT_DTYPE from colour.models import xy_to_xyY, xyY_to_XYZ, Jab_to_JCh, JCh_to_Jab from colour.utilities import (domain_range_scale, from_range_1, from_range_100, - to_domain_1, to_domain_100, tsplit, tstack) + full, to_domain_1, to_domain_100, tsplit, tstack) __author__ = 'Colour Developers' __copyright__ = 'Copyright (C) 2013-2020 - Colour Developers' @@ -100,6 +97,7 @@ def XYZ_to_Luv( Examples -------- + >>> import numpy as np >>> XYZ = np.array([0.20654008, 0.12197225, 0.05136952]) >>> XYZ_to_Luv(XYZ) # doctest: +ELLIPSIS array([ 41.5278752..., 96.8362605..., 17.7521014...]) @@ -169,6 +167,7 @@ def Luv_to_XYZ( Examples -------- + >>> import numpy as np >>> Luv = np.array([41.52787529, 96.83626054, 17.75210149]) >>> Luv_to_XYZ(Luv) # doctest: +ELLIPSIS array([ 0.2065400..., 0.1219722..., 0.0513695...]) @@ -237,6 +236,7 @@ def Luv_to_uv( Examples -------- + >>> import numpy as np >>> Luv = np.array([41.52787529, 96.83626054, 17.75210149]) >>> Luv_to_uv(Luv) # doctest: +ELLIPSIS array([ 0.3772021..., 0.5012026...]) @@ -298,6 +298,7 @@ def uv_to_Luv( Examples -------- + >>> import numpy as np >>> uv = np.array([0.37720213, 0.50120264]) >>> uv_to_Luv(uv) # doctest: +ELLIPSIS array([ 100. , 233.1837603..., 42.7474385...]) @@ -308,7 +309,7 @@ def uv_to_Luv( X = 9 * u / (4 * v) Z = (-5 * Y * v - 3 * u / 4 + 3) / v - Y = np.full(u.shape, Y, DEFAULT_FLOAT_DTYPE) + Y = full(u.shape, Y) return XYZ_to_Luv(from_range_1(tstack([X, Y, Z])), illuminant) @@ -334,6 +335,7 @@ def Luv_uv_to_xy(uv): Examples -------- + >>> import numpy as np >>> uv = np.array([0.37720213, 0.50120264]) >>> Luv_uv_to_xy(uv) # doctest: +ELLIPSIS array([ 0.5436955..., 0.3210794...]) @@ -368,6 +370,7 @@ def xy_to_Luv_uv(xy): Examples -------- + >>> import numpy as np >>> xy = np.array([0.54369558, 0.32107944]) >>> xy_to_Luv_uv(xy) # doctest: +ELLIPSIS array([ 0.3772021..., 0.5012026...]) @@ -425,6 +428,7 @@ def Luv_to_LCHuv(Luv): Examples -------- + >>> import numpy as np >>> Luv = np.array([41.52787529, 96.83626054, 17.75210149]) >>> Luv_to_LCHuv(Luv) # doctest: +ELLIPSIS array([ 41.5278752..., 98.4499795..., 10.3881634...]) @@ -477,6 +481,7 @@ def LCHuv_to_Luv(LCHuv): Examples -------- + >>> import numpy as np >>> LCHuv = np.array([41.52787529, 98.44997950, 10.38816348]) >>> LCHuv_to_Luv(LCHuv) # doctest: +ELLIPSIS array([ 41.5278752..., 96.8362605..., 17.7521014...]) diff --git a/colour/models/cie_ucs.py b/colour/models/cie_ucs.py index f0b886a435..fb43f52ae5 100644 --- a/colour/models/cie_ucs.py +++ b/colour/models/cie_ucs.py @@ -23,10 +23,7 @@ from __future__ import division, unicode_literals -import numpy as np - -from colour.constants import DEFAULT_FLOAT_DTYPE -from colour.utilities import from_range_1, to_domain_1, tsplit, tstack +from colour.utilities import (from_range_1, full, to_domain_1, tsplit, tstack) __author__ = 'Colour Developers' __copyright__ = 'Copyright (C) 2013-2020 - Colour Developers' @@ -76,6 +73,7 @@ def XYZ_to_UCS(XYZ): Examples -------- + >>> import numpy as np >>> XYZ = np.array([0.20654008, 0.12197225, 0.05136952]) >>> XYZ_to_UCS(XYZ) # doctest: +ELLIPSIS array([ 0.1376933..., 0.1219722..., 0.1053731...]) @@ -123,6 +121,7 @@ def UCS_to_XYZ(UVW): Examples -------- + >>> import numpy as np >>> UVW = np.array([0.13769339, 0.12197225, 0.10537310]) >>> UCS_to_XYZ(UVW) # doctest: +ELLIPSIS array([ 0.2065400..., 0.1219722..., 0.0513695...]) @@ -165,6 +164,7 @@ def UCS_to_uv(UVW): Examples -------- + >>> import numpy as np >>> UVW = np.array([0.13769339, 0.12197225, 0.10537310]) >>> UCS_to_uv(UVW) # doctest: +ELLIPSIS array([ 0.3772021..., 0.3341350...]) @@ -202,13 +202,14 @@ def uv_to_UCS(uv, V=1): Examples -------- + >>> import numpy as np >>> uv = np.array([0.37720213, 0.33413508]) >>> uv_to_UCS(uv) # doctest: +ELLIPSIS array([ 1.1288911..., 1. , 0.8639104...]) """ u, v = tsplit(uv) - V = np.full(u.shape, V, DEFAULT_FLOAT_DTYPE) + V = full(u.shape, V) U = V * u / v W = -V * (u + v - 1) / v @@ -239,6 +240,7 @@ def UCS_uv_to_xy(uv): Examples -------- + >>> import numpy as np >>> uv = np.array([0.37720213, 0.33413508]) >>> UCS_uv_to_xy(uv) # doctest: +ELLIPSIS array([ 0.5436955..., 0.3210794...]) @@ -273,6 +275,7 @@ def xy_to_UCS_uv(xy): Examples -------- + >>> import numpy as np >>> xy = np.array([0.54369555, 0.32107941]) >>> xy_to_UCS_uv(xy) # doctest: +ELLIPSIS array([ 0.3772021..., 0.3341350...]) diff --git a/colour/models/cie_xyy.py b/colour/models/cie_xyy.py index 143fbe2a80..cfbfb40221 100644 --- a/colour/models/cie_xyy.py +++ b/colour/models/cie_xyy.py @@ -27,9 +27,8 @@ import numpy as np from colour.colorimetry import ILLUMINANTS -from colour.constants import DEFAULT_FLOAT_DTYPE -from colour.utilities import (as_float_array, from_range_1, to_domain_1, - tsplit, tstack) +from colour.utilities import (as_float_array, from_range_1, full, to_domain_1, + tsplit, tstack, zeros) __author__ = 'Colour Developers' __copyright__ = 'Copyright (C) 2013-2020 - Colour Developers' @@ -93,7 +92,7 @@ def XYZ_to_xyY( X, Y, Z = tsplit(XYZ) xy_w = as_float_array(illuminant) - XYZ_n = np.zeros(XYZ.shape) + XYZ_n = zeros(XYZ.shape) XYZ_n[..., 0:2] = xy_w xyY = np.where( @@ -281,7 +280,7 @@ def xy_to_xyY(xy, Y=1): x, y = tsplit(xy) - Y = np.full(x.shape, from_range_1(Y), DEFAULT_FLOAT_DTYPE) + Y = full(x.shape, from_range_1(Y)) xyY = tstack([x, y, Y]) return xyY diff --git a/colour/models/rgb/cmyk.py b/colour/models/rgb/cmyk.py index 7965fb55f9..7bc9ebc65d 100644 --- a/colour/models/rgb/cmyk.py +++ b/colour/models/rgb/cmyk.py @@ -26,7 +26,7 @@ import numpy as np -from colour.utilities import (as_float_array, from_range_1, to_domain_1, +from colour.utilities import (as_float_array, from_range_1, ones, to_domain_1, tsplit, tstack) __author__ = 'Colour Developers' @@ -171,7 +171,7 @@ def CMY_to_CMYK(CMY): C, M, Y = tsplit(to_domain_1(CMY)) - K = np.ones(C.shape) + K = ones(C.shape) K = np.where(C < K, C, K) K = np.where(M < K, M, K) K = np.where(Y < K, Y, K) diff --git a/colour/models/rgb/cylindrical.py b/colour/models/rgb/cylindrical.py index 4486c418ff..ae15d56ba4 100644 --- a/colour/models/rgb/cylindrical.py +++ b/colour/models/rgb/cylindrical.py @@ -37,7 +37,7 @@ import numpy as np -from colour.utilities import (as_float_array, from_range_1, to_domain_1, +from colour.utilities import (as_float_array, from_range_1, full, to_domain_1, tsplit, tstack) __author__ = 'Colour Developers' @@ -309,7 +309,7 @@ def H_to_RGB(vi, vj, vH): vH[np.asarray(vH < 0)] += 1 vH[np.asarray(vH > 1)] -= 1 - v = np.full(vi.shape, np.nan) + v = full(vi.shape, np.nan) v = np.where( np.logical_and(6 * vH < 1, np.isnan(v)), diff --git a/colour/models/rgb/derivation.py b/colour/models/rgb/derivation.py index 5c2efb4126..d9a31d602f 100644 --- a/colour/models/rgb/derivation.py +++ b/colour/models/rgb/derivation.py @@ -30,7 +30,7 @@ from colour.adaptation import chromatic_adaptation_VonKries from colour.models import XYZ_to_xy, XYZ_to_xyY, xy_to_XYZ -from colour.utilities import tsplit +from colour.utilities import as_numeric, ones, tsplit __author__ = 'Colour Developers' __copyright__ = 'Copyright (C) 2013-2020 - Colour Developers' @@ -206,8 +206,7 @@ def primaries_whitepoint(npm): 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)))))) + 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. @@ -274,4 +273,4 @@ def RGB_luminance(RGB, primaries, whitepoint): Y = np.sum( normalised_primary_matrix(primaries, whitepoint)[1] * RGB, axis=-1) - return Y + return as_numeric(Y) diff --git a/colour/notation/hexadecimal.py b/colour/notation/hexadecimal.py index 48a86e4c5c..0faf8949c2 100644 --- a/colour/notation/hexadecimal.py +++ b/colour/notation/hexadecimal.py @@ -14,8 +14,8 @@ import numpy as np from colour.models import eotf_inverse_sRGB, eotf_sRGB -from colour.utilities import (from_range_1, normalise_maximum, to_domain_1, - usage_warning) +from colour.utilities import (as_float_array, from_range_1, normalise_maximum, + to_domain_1, usage_warning) __author__ = 'Colour Developers' __copyright__ = 'Copyright (C) 2013-2020 - Colour Developers' @@ -124,6 +124,6 @@ def to_RGB(x): to_RGB_v = np.vectorize(to_RGB, otypes=[np.ndarray]) - RGB = np.asarray(to_RGB_v(HEX).tolist()) / 255 + RGB = as_float_array(to_RGB_v(HEX).tolist()) / 255 return from_range_1(RGB) diff --git a/colour/notation/munsell.py b/colour/notation/munsell.py index 8d23394a72..09e31e4158 100644 --- a/colour/notation/munsell.py +++ b/colour/notation/munsell.py @@ -791,8 +791,10 @@ def _munsell_specification_to_xyY(specification): Y_minus = luminance_ASTMD1535(value_minus) Y_plus = luminance_ASTMD1535(value_plus) - x = LinearInterpolator((Y_minus, Y_plus), (x_minus, x_plus))(Y) - y = LinearInterpolator((Y_minus, Y_plus), (y_minus, y_plus))(Y) + x = LinearInterpolator( + np.ravel([Y_minus, Y_plus]), np.ravel([x_minus, x_plus]))(Y) + y = LinearInterpolator( + np.ravel([Y_minus, Y_plus]), np.ravel([y_minus, y_plus]))(Y) return np.array([x, y, from_range_1(Y / 100)]) @@ -2165,19 +2167,23 @@ def xy_from_renotation_ovoid(specification): ['Linear', 'radial']))) if interpolation_method == 'linear': - x = LinearInterpolator((lower_hue_angle, upper_hue_angle), - (x_minus, x_plus))(hue_angle) - y = LinearInterpolator((lower_hue_angle, upper_hue_angle), - (y_minus, y_plus))(hue_angle) + x = LinearInterpolator( + np.ravel([lower_hue_angle, upper_hue_angle]), + np.ravel([x_minus, x_plus]))(hue_angle) + y = LinearInterpolator( + np.ravel([lower_hue_angle, upper_hue_angle]), + np.ravel([y_minus, y_plus]))(hue_angle) elif interpolation_method == 'radial': - theta = LinearInterpolator((lower_hue_angle, upper_hue_angle), - (phi_minus, phi_plus))(hue_angle) - rho = LinearInterpolator((lower_hue_angle, upper_hue_angle), - (rho_minus, rho_plus))(hue_angle) + theta = LinearInterpolator( + np.ravel([lower_hue_angle, upper_hue_angle]), + np.ravel([phi_minus, phi_plus]))(hue_angle) + rho = LinearInterpolator( + np.ravel([lower_hue_angle, upper_hue_angle]), + np.ravel([rho_minus, rho_plus]))(hue_angle) x, y = tsplit( - polar_to_cartesian((rho, np.radians(theta))) + - as_float_array((x_grey, y_grey))) + polar_to_cartesian(np.ravel([rho, np.radians(theta)])) + + as_float_array([x_grey, y_grey])) return as_float_array([x, y]) diff --git a/colour/plotting/colorimetry.py b/colour/plotting/colorimetry.py index dc2ebbe619..ce4e74cdf7 100644 --- a/colour/plotting/colorimetry.py +++ b/colour/plotting/colorimetry.py @@ -42,7 +42,7 @@ filter_passthrough, filter_cmfs, filter_illuminants, override_style, render, plot_single_colour_swatch, plot_multi_functions) from colour.utilities import (domain_range_scale, first_item, - normalise_maximum, tstack) + normalise_maximum, ones, tstack) __author__ = 'Colour Developers' __copyright__ = 'Copyright (C) 2013-2020 - Colour Developers' @@ -157,7 +157,7 @@ def plot_single_sd(sd, colours = COLOUR_STYLE_CONSTANTS.colour.colourspace.cctf_encoding(colours) if equalize_sd_amplitude: - values = np.ones(values.shape) + values = ones(values.shape) margin = 0 if equalize_sd_amplitude else 0.05 diff --git a/colour/plotting/temperature.py b/colour/plotting/temperature.py index 03772167f6..0d7107b696 100644 --- a/colour/plotting/temperature.py +++ b/colour/plotting/temperature.py @@ -24,7 +24,7 @@ plot_chromaticity_diagram_CIE1960UCS, filter_passthrough, override_style, render) from colour.plotting.diagrams import plot_chromaticity_diagram -from colour.utilities import tstack +from colour.utilities import tstack, zeros from colour.utilities.deprecation import handle_arguments_deprecation __author__ = 'Colour Developers' @@ -116,7 +116,7 @@ def uv_to_ij(uv): start, end = 1667, 100000 CCT = np.arange(start, end + 250, 250) - CCT_D_uv = tstack([CCT, np.zeros(CCT.shape)]) + CCT_D_uv = tstack([CCT, zeros(CCT.shape)]) ij = uv_to_ij(CCT_to_uv(CCT_D_uv, 'Robertson 1968')) axes.plot(ij[..., 0], ij[..., 1], color=planckian_locus_colours) diff --git a/colour/plotting/volume.py b/colour/plotting/volume.py index 0170e05977..df689fa1c5 100644 --- a/colour/plotting/volume.py +++ b/colour/plotting/volume.py @@ -25,7 +25,7 @@ COLOUR_STYLE_CONSTANTS, common_colourspace_model_axis_reorder, filter_RGB_colourspaces, filter_cmfs, override_style, render) from colour.utilities import (Structure, as_float_array, as_int_array, - first_item) + first_item, full, ones, zeros) __author__ = 'Colour Developers' __copyright__ = 'Copyright (C) 2013-2020 - Colour Developers' @@ -177,19 +177,13 @@ def nadir_grid(limits=None, segments=10, labels=None, axes=None, **kwargs): height_segments=segments, width_segments=segments) - RGB_g = np.ones((quads_g.shape[0], quads_g.shape[-1])) + RGB_g = ones([quads_g.shape[0], quads_g.shape[-1]]) RGB_gf = RGB_g * settings.grid_face_colours - RGB_gf = np.hstack([ - RGB_gf, - np.full((RGB_gf.shape[0], 1), settings.grid_face_alpha, - DEFAULT_FLOAT_DTYPE) - ]) + RGB_gf = np.hstack( + [RGB_gf, full([RGB_gf.shape[0], 1], settings.grid_face_alpha)]) RGB_ge = RGB_g * settings.grid_edge_colours - RGB_ge = np.hstack([ - RGB_ge, - np.full((RGB_ge.shape[0], 1), settings.grid_edge_alpha, - DEFAULT_FLOAT_DTYPE) - ]) + RGB_ge = np.hstack( + [RGB_ge, full([RGB_ge.shape[0], 1], settings.grid_edge_alpha)]) # Inner grid. quads_gs = primitive_vertices_grid_mpl( @@ -199,27 +193,24 @@ def nadir_grid(limits=None, segments=10, labels=None, axes=None, **kwargs): height_segments=segments * 2, width_segments=segments * 2) - RGB_gs = np.ones((quads_gs.shape[0], quads_gs.shape[-1])) + RGB_gs = ones([quads_gs.shape[0], quads_gs.shape[-1]]) RGB_gsf = RGB_gs * 0 - RGB_gsf = np.hstack( - [RGB_gsf, - np.full((RGB_gsf.shape[0], 1), 0, DEFAULT_FLOAT_DTYPE)]) + RGB_gsf = np.hstack([RGB_gsf, full([RGB_gsf.shape[0], 1], 0)]) RGB_gse = np.clip(RGB_gs * settings.grid_edge_colours * 1.5, 0, 1) - RGB_gse = np.hstack( - (RGB_gse, - np.full((RGB_gse.shape[0], 1), settings.grid_edge_alpha / 2, - DEFAULT_FLOAT_DTYPE))) + RGB_gse = np.hstack((RGB_gse, + full([RGB_gse.shape[0], 1], + settings.grid_edge_alpha / 2))) # Axis. thickness = extent / 1000 quad_x = primitive_vertices_grid_mpl( origin=(limits[0, 0], -thickness / 2), width=extent, height=thickness) - RGB_x = np.ones((quad_x.shape[0], quad_x.shape[-1] + 1)) + RGB_x = ones([quad_x.shape[0], quad_x.shape[-1] + 1]) RGB_x = RGB_x * settings.x_axis_colour quad_y = primitive_vertices_grid_mpl( origin=(-thickness / 2, limits[1, 0]), width=thickness, height=extent) - RGB_y = np.ones((quad_y.shape[0], quad_y.shape[-1] + 1)) + RGB_y = ones([quad_y.shape[0], quad_y.shape[-1] + 1]) RGB_y = RGB_y * settings.y_axis_colour if axes is not None: @@ -464,7 +455,7 @@ def plot_RGB_colourspaces_gamuts(colourspaces=None, illuminant = COLOUR_STYLE_CONSTANTS.colour.colourspace.whitepoint - points = np.zeros((4, 3)) + points = zeros([4, 3]) if show_spectral_locus: cmfs = first_item(filter_cmfs(cmfs).values()) XYZ = cmfs.values @@ -509,24 +500,18 @@ def plot_RGB_colourspaces_gamuts(colourspaces=None, ), reference_colourspace)) if settings.face_colours[i] is not None: - RGB = np.ones(RGB.shape) * settings.face_colours[i] + RGB = ones(RGB.shape) * settings.face_colours[i] RGB_f.extend( - np.hstack([ - RGB, - np.full((RGB.shape[0], 1), settings.face_alpha[i], - DEFAULT_FLOAT_DTYPE) - ])) + np.hstack([RGB, + full([RGB.shape[0], 1], settings.face_alpha[i])])) if settings.edge_colours[i] is not None: - RGB = np.ones(RGB.shape) * settings.edge_colours[i] + RGB = ones(RGB.shape) * settings.edge_colours[i] RGB_e.extend( - np.hstack([ - RGB, - np.full((RGB.shape[0], 1), settings.edge_alpha[i], - DEFAULT_FLOAT_DTYPE) - ])) + np.hstack([RGB, + full([RGB.shape[0], 1], settings.edge_alpha[i])])) quads = as_float_array(quads) quads[np.isnan(quads)] = 0 diff --git a/colour/quality/ssi.py b/colour/quality/ssi.py index 832f5ff883..8acaaa45b7 100644 --- a/colour/quality/ssi.py +++ b/colour/quality/ssi.py @@ -21,6 +21,7 @@ from colour.algebra import LinearInterpolator from colour.colorimetry import SpectralShape +from colour.utilities import zeros __author__ = 'Colour Developers' __copyright__ = 'Copyright (C) 2013-2020 - Colour Developers' @@ -76,7 +77,7 @@ def spectral_similarity_index(sd_test, sd_reference): global _INTEGRATION_MATRIX if _INTEGRATION_MATRIX is None: - _INTEGRATION_MATRIX = np.zeros([ + _INTEGRATION_MATRIX = zeros([ len(_SSI_LARGE_SPECTRAL_SHAPE.range()), len(SSI_SPECTRAL_SHAPE.range()) ]) diff --git a/colour/utilities/__init__.py b/colour/utilities/__init__.py index af37dcdb03..056b94c27b 100644 --- a/colour/utilities/__init__.py +++ b/colour/utilities/__init__.py @@ -13,12 +13,37 @@ domain_range_scale, to_domain_1, to_domain_10, to_domain_100, to_domain_degrees, to_domain_int, from_range_1, from_range_10, from_range_100, from_range_degrees, from_range_int) -from .array import (as_array, as_int_array, as_float_array, as_numeric, as_int, - as_float, set_float_precision, set_int_precision, - as_namedtuple, closest_indexes, closest, normalise_maximum, - interval, is_uniform, in_array, tstack, tsplit, - row_as_diagonal, dot_vector, dot_matrix, orient, centroid, - linear_conversion, lerp, fill_nan, ndarray_write) +from .array import ( + as_array, + as_int_array, + as_float_array, + as_numeric, + as_int, + as_float, + set_float_precision, + set_int_precision, + as_namedtuple, + closest_indexes, + closest, + normalise_maximum, + interval, + is_uniform, + in_array, + tstack, + tsplit, + row_as_diagonal, + dot_vector, + dot_matrix, + orient, + centroid, + linear_conversion, + lerp, + fill_nan, + ndarray_write, + zeros, + ones, + full, +) from .metrics import metric_mse, metric_psnr from .verbose import ( ColourWarning, ColourUsageWarning, ColourRuntimeWarning, message_box, @@ -47,7 +72,7 @@ 'closest_indexes', 'closest', 'normalise_maximum', 'interval', 'is_uniform', 'in_array', 'tstack', 'tsplit', 'row_as_diagonal', 'dot_vector', 'dot_matrix', 'orient', 'centroid', 'linear_conversion', - 'fill_nan', 'lerp', 'ndarray_write' + 'fill_nan', 'lerp', 'ndarray_write', 'zeros', 'ones', 'full' ] __all__ += ['metric_mse', 'metric_psnr'] __all__ += [ diff --git a/colour/utilities/array.py b/colour/utilities/array.py index 8577016404..6fc331508c 100644 --- a/colour/utilities/array.py +++ b/colour/utilities/array.py @@ -42,7 +42,7 @@ 'closest_indexes', 'closest', 'normalise_maximum', 'interval', 'is_uniform', 'in_array', 'tstack', 'tsplit', 'row_as_diagonal', 'dot_vector', 'dot_matrix', 'orient', 'centroid', 'linear_conversion', - 'lerp', 'fill_nan', 'ndarray_write' + 'lerp', 'fill_nan', 'ndarray_write', 'zeros', 'ones', 'full' ] @@ -55,7 +55,7 @@ def as_array(a, dtype=None): a : object Variable to convert. dtype : object - Type to use for conversion, default to the type defined by + Type to use for conversion, default to the type defined by the :attr:`colour.constant.DEFAULT_FLOAT_DTYPE` attribute. Returns @@ -86,7 +86,7 @@ def as_int_array(a, dtype=None): a : object Variable to convert. dtype : object - Type to use for conversion, default to the type defined by + Type to use for conversion, default to the type defined by the :attr:`colour.constant.DEFAULT_INT_DTYPE` attribute. Returns @@ -103,6 +103,10 @@ def as_int_array(a, dtype=None): if dtype is None: dtype = DEFAULT_INT_DTYPE + assert dtype in np.sctypes['int'], ( + '"dtype" must be one of the following types: {0}'.format( + np.sctypes['int'])) + return as_array(a, dtype) @@ -115,7 +119,7 @@ def as_float_array(a, dtype=None): a : object Variable to convert. dtype : object - Type to use for conversion, default to the type defined by + Type to use for conversion, default to the type defined by the :attr:`colour.constant.DEFAULT_FLOAT_DTYPE` attribute. Returns @@ -132,6 +136,10 @@ def as_float_array(a, dtype=None): if dtype is None: dtype = DEFAULT_FLOAT_DTYPE + assert dtype in np.sctypes['float'], ( + '"dtype" must be one of the following types: {0}'.format( + np.sctypes['float'])) + return as_array(a, dtype) @@ -145,7 +153,7 @@ def as_numeric(a, dtype=None): a : object Variable to convert. dtype : object - Type to use for conversion, default to the type defined by + Type to use for conversion, default to the type defined by the :attr:`colour.constant.DEFAULT_FLOAT_DTYPE` attribute. Returns @@ -179,7 +187,7 @@ def as_int(a, dtype=None): a : object Variable to convert. dtype : object - Type to use for conversion, default to the type defined by + Type to use for conversion, default to the type defined by the :attr:`colour.constant.DEFAULT_INT_DTYPE` attribute. In the event where :math:`a` cannot be converted, it is converted to *ndarray* using the type defined by :attr:`colour.constant.DEFAULT_INT_DTYPE` attribute. @@ -208,6 +216,9 @@ def as_int(a, dtype=None): if dtype is None: dtype = DEFAULT_INT_DTYPE + assert dtype in np.sctypes['int'], ( + '"dtype" must be one of the following types: {0}'.format( + np.sctypes['int'])) try: # TODO: Change to "DEFAULT_INT_DTYPE" when and if # https://github.com/numpy/numpy/issues/11956 is addressed. @@ -225,7 +236,7 @@ def as_float(a, dtype=None): a : object Variable to convert. dtype : object - Type to use for conversion, default to the type defined by + Type to use for conversion, default to the type defined by the :attr:`colour.constant.DEFAULT_INT_DTYPE` attribute. In the event where :math:`a` cannot be converted, it is converted to *ndarray* using the type defined by :attr:`colour.constant.DEFAULT_FLOAT_DTYPE` attribute. @@ -254,6 +265,10 @@ def as_float(a, dtype=None): if dtype is None: dtype = DEFAULT_FLOAT_DTYPE + assert dtype in np.sctypes['float'], ( + '"dtype" must be one of the following types: {0}'.format( + np.sctypes['float'])) + return dtype(a) @@ -268,6 +283,20 @@ def set_float_precision(dtype=DEFAULT_FLOAT_DTYPE): dtype : object Type to set :attr:`colour.constant.DEFAULT_FLOAT_DTYPE` with. + Warnings + -------- + Changing float precision might result in various *Colour* functionality + breaking entirely: https://github.com/numpy/numpy/issues/6860. With great + power comes great responsibility. + + Notes + ----- + - It is possible to define the float precision at import time by setting + the *COLOUR_SCIENCE__FLOAT_PRECISION* environment variable, for example + `set COLOUR_SCIENCE__FLOAT_PRECISION=float32`. + - Some definition returning a single-scalar ndarray might not honour the + given float precision: https://github.com/numpy/numpy/issues/16353 + Examples -------- >>> as_float_array(np.ones(3)).dtype @@ -301,6 +330,19 @@ def set_int_precision(dtype=DEFAULT_INT_DTYPE): dtype : object Type to set :attr:`colour.constant.DEFAULT_INT_DTYPE` with. + Notes + ----- + - It is possible to define the int precision at import time by setting + the *COLOUR_SCIENCE__INT_PRECISION* environment variable, for example + `set COLOUR_SCIENCE__INT_PRECISION=int32`. + + Warnings + -------- + This definition is mostly given for consistency purposes with + :func:`colour.utilities.set_float_precision` definition but contrary to the + latter, changing integer precision will almost certainly completely break + *Colour*. With great power comes great responsibility. + Examples -------- >>> as_int_array(np.ones(3)).dtype # doctest: +SKIP @@ -797,6 +839,9 @@ def dot_vector(m, v): [ 0.1954094..., 0.0620396..., 0.0527952...]]) """ + m = as_float_array(m) + v = as_float_array(v) + return np.einsum('...ij,...j->...i', m, v) @@ -815,6 +860,9 @@ def dot_matrix(a, b): Array of 3x3 matrices. b : array_like Array of 3x3 matrices. + dtype : object + Type to use for conversion, default to the type defined by the + :attr:`colour.constant.DEFAULT_FLOAT_DTYPE` attribute. Returns ------- @@ -855,6 +903,9 @@ def dot_matrix(a, b): [-0.0044203..., 0.0377490..., 0.9666713...]]]) """ + a = as_float_array(a) + b = as_float_array(b) + return np.einsum('...ij,...jk->...ik', a, b) @@ -1099,3 +1150,113 @@ def ndarray_write(a): yield a finally: a.setflags(write=False) + + +def zeros(shape, dtype=None, order='C'): + """ + Simple wrapper around :func:`np.zeros` definition to create arrays with + the active type defined by the:attr:`colour.constant.DEFAULT_FLOAT_DTYPE` + attribute. + + Parameters + ---------- + shape : int or array_like + Shape of the new array, e.g., ``(2, 3)`` or ``2``. + dtype : object + Type to use for conversion, default to the type defined by the + :attr:`colour.constant.DEFAULT_FLOAT_DTYPE` attribute. + order : unicode, optional + {'C', 'F'}, + Whether to store multi-dimensional data in row-major + (C-style) or column-major (Fortran-style) order in + memory. + + Returns + ------- + ndarray + Array of given shape and type, filled with zeros. + + Examples + -------- + >>> zeros(3) + array([ 0., 0., 0.]) + """ + + if dtype is None: + dtype = DEFAULT_FLOAT_DTYPE + + return np.zeros(shape, dtype, order) + + +def ones(shape, dtype=None, order='C'): + """ + Simple wrapper around :func:`np.ones` definition to create arrays with + the active type defined by the:attr:`colour.constant.DEFAULT_FLOAT_DTYPE` + attribute. + + Parameters + ---------- + shape : int or array_like + Shape of the new array, e.g., ``(2, 3)`` or ``2``. + dtype : object + Type to use for conversion, default to the type defined by the + :attr:`colour.constant.DEFAULT_FLOAT_DTYPE` attribute. + order : unicode, optional + {'C', 'F'}, + Whether to store multi-dimensional data in row-major + (C-style) or column-major (Fortran-style) order in + memory. + + Returns + ------- + ndarray + Array of given shape and type, filled with ones. + + Examples + -------- + >>> ones(3) + array([ 1., 1., 1.]) + """ + + if dtype is None: + dtype = DEFAULT_FLOAT_DTYPE + + return np.ones(shape, dtype, order) + + +def full(shape, fill_value, dtype=None, order='C'): + """ + Simple wrapper around :func:`np.full` definition to create arrays with + the active type defined by the:attr:`colour.constant.DEFAULT_FLOAT_DTYPE` + attribute. + + Parameters + ---------- + shape : int or array_like + Shape of the new array, e.g., ``(2, 3)`` or ``2``. + fill_value : numeric + Fill value. + dtype : object + Type to use for conversion, default to the type defined by the + :attr:`colour.constant.DEFAULT_FLOAT_DTYPE` attribute. + order : unicode, optional + {'C', 'F'}, + Whether to store multi-dimensional data in row-major + (C-style) or column-major (Fortran-style) order in + memory. + + Returns + ------- + ndarray + Array of given shape and type, filled with given value. + + Examples + -------- + >>> ones(3) + array([ 1., 1., 1.]) + """ + + if dtype is None: + dtype = DEFAULT_FLOAT_DTYPE + + return np.full(shape, fill_value, dtype, order) diff --git a/colour/utilities/tests/test_array.py b/colour/utilities/tests/test_array.py index 17f994e8c8..149bc82f6c 100644 --- a/colour/utilities/tests/test_array.py +++ b/colour/utilities/tests/test_array.py @@ -15,7 +15,8 @@ set_float_precision, set_int_precision, as_namedtuple, closest_indexes, closest, normalise_maximum, interval, is_uniform, in_array, tstack, tsplit, row_as_diagonal, dot_vector, dot_matrix, orient, centroid, - linear_conversion, lerp, fill_nan, ndarray_write) + linear_conversion, lerp, fill_nan, ndarray_write, zeros, ones, full) +from colour.utilities import is_networkx_installed __author__ = 'Colour Developers' __copyright__ = 'Copyright (C) 2013-2020 - Colour Developers' @@ -31,7 +32,8 @@ 'TestNormaliseMaximum', 'TestInterval', 'TestIsUniform', 'TestInArray', 'TestTstack', 'TestTsplit', 'TestRowAsDiagonal', 'TestDotVector', 'TestDotMatrix', 'TestOrient', 'TestCentroid', 'TestLinearConversion', - 'TestLerp', 'TestFillNan', 'TestNdarrayWrite' + 'TestLerp', 'TestFillNan', 'TestNdarrayWrite', 'TestZeros', 'TestOnes', + 'TestFull' ] @@ -185,6 +187,71 @@ def test_set_float_precision(self): self.assertEqual(as_float_array(np.ones(3)).dtype, np.float64) + def test_set_float_precision_enforcement(self): + """ + Tests whether :func:`colour.utilities.array.set_float_precision` effect + is applied through most of *Colour* public API. + """ + + if not is_networkx_installed(): + return + + from colour.appearance import (CAM16_Specification, + CIECAM02_Specification) + from colour.graph.conversion import (CONVERSION_SPECIFICATIONS_DATA, + convert) + + dtype = np.float32 + set_float_precision(dtype) + + for source, target, _callable in CONVERSION_SPECIFICATIONS_DATA: + if target in ('Hexadecimal', 'Munsell Colour'): + continue + + # Spectral distributions are instantiated with float64 data and + # spectral up-sampling optimization fails. + if ('Spectral Distribution' in (source, target) or + target == 'Complementary Wavelength' or + target == 'Dominant Wavelength'): + continue + + a = np.array([(0.25, 0.5, 0.25), (0.25, 0.5, 0.25)]) + + if source == 'CAM16': + a = CAM16_Specification(J=0.25, M=0.5, h=0.25) + + if source == 'CIECAM02': + a = CIECAM02_Specification(J=0.25, M=0.5, h=0.25) + + if source == 'CMYK': + a = np.array([(0.25, 0.5, 0.25, 0.5), (0.25, 0.5, 0.25, 0.5)]) + + if source == 'Hexadecimal': + a = np.array(['#FFFFFF', '#FFFFFF']) + + if source == 'Munsell Colour': + a = ['4.2YR 8.1/5.3', '4.2YR 8.1/5.3'] + + if source == 'Wavelength': + a = 555 + + if source.endswith(' xy') or source.endswith(' uv'): + a = np.array([(0.25, 0.5), (0.25, 0.5)]) + + def dtype_getter(x): + """ + dtype getter callable. + """ + + for specification in ('ATD95', 'CIECAM02', 'CAM16', 'Hunt', + 'LLAB', 'Nayatani95', 'RLAB'): + if target.endswith(specification): + return x[0].dtype + + return x.dtype + + self.assertEqual(dtype_getter(convert(a, source, target)), dtype) + def tearDown(self): """ After tests actions. @@ -885,5 +952,47 @@ def test_ndarray_write(self): a += 1 +class TestZeros(unittest.TestCase): + """ + Defines :func:`colour.utilities.array.zeros` definition unit tests + methods. + """ + + def test_zeros(self): + """ + Tests :func:`colour.utilities.array.zeros` definition. + """ + + np.testing.assert_equal(zeros(3), np.zeros(3)) + + +class TestOnes(unittest.TestCase): + """ + Defines :func:`colour.utilities.array.ones` definition unit tests + methods. + """ + + def test_ones(self): + """ + Tests :func:`colour.utilities.array.ones` definition. + """ + + np.testing.assert_equal(ones(3), np.ones(3)) + + +class TestFull(unittest.TestCase): + """ + Defines :func:`colour.utilities.array.full` definition unit tests + methods. + """ + + def test_full(self): + """ + Tests :func:`colour.utilities.array.full` definition. + """ + + np.testing.assert_equal(full(3, 0.5), np.full(3, 0.5)) + + if __name__ == '__main__': unittest.main() diff --git a/colour/volume/spectrum.py b/colour/volume/spectrum.py index 20fb845e0c..e65f823d02 100644 --- a/colour/volume/spectrum.py +++ b/colour/volume/spectrum.py @@ -22,7 +22,9 @@ from colour.colorimetry import (STANDARD_OBSERVER_CMFS, multi_sds_to_XYZ, SpectralShape, sd_ones) +from colour.constants import DEFAULT_FLOAT_DTYPE from colour.volume import is_within_mesh_volume +from colour.utilities import zeros __author__ = 'Colour Developers' __copyright__ = 'Copyright (C) 2013-2020 - Colour Developers' @@ -121,12 +123,17 @@ def generate_pulse_waves(bins): """ square_waves = [] - square_waves_basis = np.tril(np.ones((bins, bins)))[0:-1, :] + square_waves_basis = np.tril( + np.ones((bins, bins), dtype=DEFAULT_FLOAT_DTYPE))[0:-1, :] for square_wave_basis in square_waves_basis: for i in range(bins): square_waves.append(np.roll(square_wave_basis, i)) - return np.vstack([np.zeros(bins), np.vstack(square_waves), np.ones(bins)]) + return np.vstack([ + zeros(bins), + np.vstack(square_waves), + np.ones(bins, dtype=DEFAULT_FLOAT_DTYPE) + ]) def XYZ_outer_surface( diff --git a/docs/colour.utilities.rst b/docs/colour.utilities.rst index e6bb681392..081b8333c5 100644 --- a/docs/colour.utilities.rst +++ b/docs/colour.utilities.rst @@ -73,6 +73,8 @@ Array as_numeric as_int as_float + set_float_precision + set_int_precision as_namedtuple closest_indexes closest @@ -91,6 +93,9 @@ Array lerp fill_nan ndarray_write + zeros + ones + full Metrics -------