diff --git a/.github/workflows/deploy.yaml b/.github/workflows/deploy.yaml index 1cce42d4..f02821be 100644 --- a/.github/workflows/deploy.yaml +++ b/.github/workflows/deploy.yaml @@ -40,7 +40,7 @@ jobs: - name: Build wheels uses: pypa/cibuildwheel@v2.15.0 env: - CIBW_SKIP: "cp36-* cp37-* cp38-* pp*-win* *-manylinux_i686 *-musllinux_i686 *-win32" + CIBW_SKIP: "cp36-* cp37-* cp38-* pp* *-manylinux_i686 *-musllinux_i686 *-win32" - uses: actions/upload-artifact@v3 with: diff --git a/trollimage/_colorspaces.pyx b/trollimage/_colorspaces.pyx index 4f668f0a..b54c843b 100644 --- a/trollimage/_colorspaces.pyx +++ b/trollimage/_colorspaces.pyx @@ -18,31 +18,46 @@ np.import_array() # bint npy_isnan(np.float32_t x) nogil # Function pointer type to allow for generic high-level functions -ctypedef void (*CONVERT_FUNC)(floating[:] comp1, floating[:] comp2, floating[:] comp3, floating[:, ::1] out) nogil +ctypedef void (*CONVERT_FUNC)(floating[:, ::1] in_out_arr) nogil -cdef: - np.float32_t bintercept = 4.0 / 29 # 0.137931 - np.float32_t delta = 6.0 / 29 # 0.206896 - np.float32_t t0 = delta ** 3 # 0.008856 - np.float32_t alpha = (delta ** -2) / 3 # 7.787037 - np.float32_t third = 1.0 / 3 - np.float32_t kappa = (29.0 / 3) ** 3 # 903.3 - np.float32_t gamma = 2.2 - np.float32_t xn = 0.95047 - np.float32_t yn = 1.0 - np.float32_t zn = 1.08883 - np.float32_t denom_n = xn + (15 * yn) + (3 * zn) - np.float32_t uprime_n = (4 * xn) / denom_n - np.float32_t vprime_n = (9 * yn) / denom_n - - - # Compile time option to use - # sRGB companding (default, True) or simplified gamma (False) - # sRGB companding is slightly slower but is more accurate at - # the extreme ends of scale - # Unit tests tuned to sRGB companding, change with caution - bint SRGB_COMPAND = True +cdef extern from *: + """ + #define bintercept (4.0 / 29.0) + #define delta (6.0 / 29.0) + #define t0 pow(delta, 3.0) + #define alpha (pow(delta, -2.0) / 3) + #define third (1.0 / 3.0) + #define kappa pow(29.0 / 3.0, 3.0) + #define gamma 2.2 + #define xn 0.95047 + #define yn 1.0 + #define zn 1.08883 + #define denom_n (xn + (15 * yn) + (3 * zn)) + #define uprime_n ((4 * xn) / denom_n) + #define vprime_n ((9 * yn) / denom_n) + + // Compile time option to use + // sRGB companding (default, 1 - True) or simplified gamma (False) + // sRGB companding is slightly slower but is more accurate at + // the extreme ends of scale + // Unit tests tuned to sRGB companding, change with caution + #define SRGB_COMPAND 1 + """ + np.float32_t bintercept + np.float32_t delta + np.float32_t t0 + np.float32_t alpha + np.float32_t third + np.float32_t kappa + np.float32_t gamma + np.float32_t xn + np.float32_t yn + np.float32_t zn + np.float32_t denom_n + np.float32_t uprime_n + np.float32_t vprime_n + bint SRGB_COMPAND def rgb2lch( @@ -152,7 +167,6 @@ def convert_colors(object input_colors, str in_space, str out_space): cdef np.ndarray[floating, ndim=2] _call_convert_func( floating[:, :] in_colors, str in_space, str out_space, ): - cdef floating[:] in1_view, in2_view, in3_view cdef CONVERT_FUNC conv_func = NULL if in_space == "rgb": if out_space == "lch": @@ -209,91 +223,87 @@ cdef np.ndarray[floating, ndim=2] _call_convert_func( else: dtype = np.float64 - in1_view = in_colors[:, 0] - in2_view = in_colors[:, 1] - in3_view = in_colors[:, 2] - cdef np.ndarray[floating, ndim=2] out_colors = np.empty((in_colors.shape[0], 3), dtype=dtype) - cdef floating[:, ::1] out_view = out_colors + cdef floating[:, ::1] in_out_view = in_colors.copy() with nogil: - conv_func(in1_view, in2_view, in3_view, out_view) - return out_colors + conv_func(in_out_view) + return np.asarray(in_out_view) -cdef void _rgb_to_lab(floating[:] r_arr, floating[:] g_arr, floating[:] b_arr, floating[:, ::1] lab_arr) noexcept nogil: - _rgb_to_xyz[floating](r_arr, g_arr, b_arr, lab_arr) - _xyz_to_lab[floating](lab_arr[:, 0], lab_arr[:, 1], lab_arr[:, 2], lab_arr) +cdef void _rgb_to_lab(floating[:, ::1] rgb_to_lab_arr) noexcept nogil: + _rgb_to_xyz[floating](rgb_to_lab_arr) + _xyz_to_lab[floating](rgb_to_lab_arr) -cdef void _rgb_to_lch(floating[:] r_arr, floating[:] g_arr, floating[:] b_arr, floating[:, ::1] lch_arr) noexcept nogil: - _rgb_to_xyz(r_arr, g_arr, b_arr, lch_arr) - _xyz_to_lab[floating](lch_arr[:, 0], lch_arr[:, 1], lch_arr[:, 2], lch_arr) - _lab_to_lch[floating](lch_arr[:, 0], lch_arr[:, 1], lch_arr[:, 2], lch_arr) +cdef void _rgb_to_lch(floating[:, ::1] rgb_to_lch_arr) noexcept nogil: + _rgb_to_xyz(rgb_to_lch_arr) + _xyz_to_lab[floating](rgb_to_lch_arr) + _lab_to_lch[floating](rgb_to_lch_arr) -cdef void _rgb_to_luv(floating[:] r_arr, floating[:] g_arr, floating[:] b_arr, floating[:, ::1] luv_arr) noexcept nogil: - _rgb_to_xyz(r_arr, g_arr, b_arr, luv_arr) - _xyz_to_luv[floating](luv_arr[:, 0], luv_arr[:, 1], luv_arr[:, 2], luv_arr) +cdef void _rgb_to_luv(floating[:, ::1] rgb_to_luv_arr) noexcept nogil: + _rgb_to_xyz(rgb_to_luv_arr) + _xyz_to_luv[floating](rgb_to_luv_arr) -cdef void _xyz_to_lch(floating[:] x_arr, floating[:] y_arr, floating[:] z_arr, floating[:, ::1] lch_arr) noexcept nogil: - _xyz_to_lab(x_arr, y_arr, z_arr, lch_arr) - _lab_to_lch[floating](lch_arr[:, 0], lch_arr[:, 1], lch_arr[:, 2], lch_arr) +cdef void _xyz_to_lch(floating[:, ::1] xyz_to_lch_arr) noexcept nogil: + _xyz_to_lab(xyz_to_lch_arr) + _lab_to_lch[floating](xyz_to_lch_arr) -cdef void _lab_to_rgb(floating[:] l_arr, floating[:] a_arr, floating[:] b_arr, floating[:, ::1] rgb_arr) noexcept nogil: - _lab_to_xyz(l_arr, a_arr, b_arr, rgb_arr) - _xyz_to_rgb[floating](rgb_arr[:, 0], rgb_arr[:, 1], rgb_arr[:, 2], rgb_arr) +cdef void _lab_to_rgb(floating[:, ::1] lab_to_rgb_arr) noexcept nogil: + _lab_to_xyz(lab_to_rgb_arr) + _xyz_to_rgb[floating](lab_to_rgb_arr) -cdef void _lab_to_luv(floating[:] l_arr, floating[:] a_arr, floating[:] b_arr, floating[:, ::1] luv_arr) noexcept nogil: - _lab_to_xyz(l_arr, a_arr, b_arr, luv_arr) - _xyz_to_luv[floating](luv_arr[:, 0], luv_arr[:, 1], luv_arr[:, 2], luv_arr) +cdef void _lab_to_luv(floating[:, ::1] lab_to_luv_arr) noexcept nogil: + _lab_to_xyz(lab_to_luv_arr) + _xyz_to_luv[floating](lab_to_luv_arr) -cdef void _lch_to_xyz(floating[:] l_arr, floating[:] c_arr, floating[:] h_arr, floating[:, ::1] xyz_arr) noexcept nogil: - _lch_to_lab(l_arr, c_arr, h_arr, xyz_arr) - _lab_to_xyz[floating](xyz_arr[:, 0], xyz_arr[:, 1], xyz_arr[:, 2], xyz_arr) +cdef void _lch_to_xyz(floating[:, ::1] lch_to_xyz_arr) noexcept nogil: + _lch_to_lab(lch_to_xyz_arr) + _lab_to_xyz[floating](lch_to_xyz_arr) -cdef void _lch_to_rgb(floating[:] l_arr, floating[:] c_arr, floating[:] h_arr, floating[:, ::1] rgb_arr) noexcept nogil: - _lch_to_lab(l_arr, c_arr, h_arr, rgb_arr) - _lab_to_xyz[floating](rgb_arr[:, 0], rgb_arr[:, 1], rgb_arr[:, 2], rgb_arr) - _xyz_to_rgb[floating](rgb_arr[:, 0], rgb_arr[:, 1], rgb_arr[:, 2], rgb_arr) +cdef void _lch_to_rgb(floating[:, ::1] lch_to_rgb_arr) noexcept nogil: + _lch_to_lab(lch_to_rgb_arr) + _lab_to_xyz[floating](lch_to_rgb_arr) + _xyz_to_rgb[floating](lch_to_rgb_arr) -cdef void _lch_to_luv(floating[:] l_arr, floating[:] c_arr, floating[:] h_arr, floating[:, ::1] luv_arr) noexcept nogil: - _lch_to_lab(l_arr, c_arr, h_arr, luv_arr) - _lab_to_xyz[floating](luv_arr[:, 0], luv_arr[:, 1], luv_arr[:, 2], luv_arr) - _xyz_to_rgb[floating](luv_arr[:, 0], luv_arr[:, 1], luv_arr[:, 2], luv_arr) +cdef void _lch_to_luv(floating[:, ::1] lch_to_luv_arr) noexcept nogil: + _lch_to_lab(lch_to_luv_arr) + _lab_to_xyz[floating](lch_to_luv_arr) + _xyz_to_rgb[floating](lch_to_luv_arr) -cdef void _luv_to_lab(floating[:] l_arr, floating[:] u_arr, floating[:] v_arr, floating[:, ::1] lab_arr) noexcept nogil: - _luv_to_xyz(l_arr, u_arr, v_arr, lab_arr) - _xyz_to_lab[floating](lab_arr[:, 0], lab_arr[:, 1], lab_arr[:, 2], lab_arr) +cdef void _luv_to_lab(floating[:, ::1] luv_to_lab_arr) noexcept nogil: + _luv_to_xyz(luv_to_lab_arr) + _xyz_to_lab[floating](luv_to_lab_arr) -cdef void _luv_to_rgb(floating[:] l_arr, floating[:] u_arr, floating[:] v_arr, floating[:, ::1] rgb_arr) noexcept nogil: - _luv_to_xyz(l_arr, u_arr, v_arr, rgb_arr) - _xyz_to_rgb[floating](rgb_arr[:, 0], rgb_arr[:, 1], rgb_arr[:, 2], rgb_arr) +cdef void _luv_to_rgb(floating[:, ::1] luv_to_rgb_arr) noexcept nogil: + _luv_to_xyz(luv_to_rgb_arr) + _xyz_to_rgb[floating](luv_to_rgb_arr) -cdef void _luv_to_lch(floating[:] l_arr, floating[:] u_arr, floating[:] v_arr, floating[:, ::1] lch_arr) noexcept nogil: - _luv_to_xyz(l_arr, u_arr, v_arr, lch_arr) - _xyz_to_lab[floating](lch_arr[:, 0], lch_arr[:, 1], lch_arr[:, 2], lch_arr) - _lab_to_lch[floating](lch_arr[:, 0], lch_arr[:, 1], lch_arr[:, 2], lch_arr) +cdef void _luv_to_lch(floating[:, ::1] luv_to_lch_arr) noexcept nogil: + _luv_to_xyz(luv_to_lch_arr) + _xyz_to_lab[floating](luv_to_lch_arr) + _lab_to_lch[floating](luv_to_lch_arr) # Direct colorspace conversions -cdef void _rgb_to_xyz(floating[:] red_arr, floating[:] green_arr, floating[:] blue_arr, floating[:, ::1] xyz_arr) noexcept nogil: +cdef void _rgb_to_xyz(floating[:, ::1] rgb_to_xyz_arr) noexcept nogil: cdef floating r, g, b, rl, gl, bl, x, y, z cdef Py_ssize_t idx - for idx in range(red_arr.shape[0]): - r = red_arr[idx] - g = green_arr[idx] - b = blue_arr[idx] + for idx in range(rgb_to_xyz_arr.shape[0]): + r = rgb_to_xyz_arr[idx, 0] + g = rgb_to_xyz_arr[idx, 1] + b = rgb_to_xyz_arr[idx, 2] # convert RGB to linear scale rl = _to_linear_rgb(r) @@ -306,9 +316,9 @@ cdef void _rgb_to_xyz(floating[:] red_arr, floating[:] green_arr, floating[:] bl y = ((rl * 0.2126729) + (gl * 0.7151522) + (bl * 0.0721750)) z = ((rl * 0.0193339) + (gl * 0.1191920) + (bl * 0.9503041)) / zn - xyz_arr[idx, 0] = x - xyz_arr[idx, 1] = y - xyz_arr[idx, 2] = z + rgb_to_xyz_arr[idx, 0] = x + rgb_to_xyz_arr[idx, 1] = y + rgb_to_xyz_arr[idx, 2] = z cdef inline floating _to_linear_rgb(floating rgb_component) noexcept nogil: @@ -324,15 +334,15 @@ cdef inline floating _to_linear_srgb_expand(floating rgb_component) noexcept nog return ((rgb_component + 0.055) / 1.055) ** 2.4 -cdef void _xyz_to_lab(floating[:] x_arr, floating[:] y_arr, floating[:] z_arr, floating[:, ::1] lab) noexcept nogil: +cdef void _xyz_to_lab(floating[:, ::1] xyz_to_lab_arr) noexcept nogil: cdef floating x, y, z, fx, fy, fz cdef floating L, a, b cdef Py_ssize_t idx - for idx in range(x_arr.shape[0]): - x = x_arr[idx] - y = y_arr[idx] - z = z_arr[idx] + for idx in range(xyz_to_lab_arr.shape[0]): + x = xyz_to_lab_arr[idx, 0] + y = xyz_to_lab_arr[idx, 1] + z = xyz_to_lab_arr[idx, 2] # convert XYZ to LAB colorspace if x > t0: @@ -354,45 +364,45 @@ cdef void _xyz_to_lab(floating[:] x_arr, floating[:] y_arr, floating[:] z_arr, f a = 500 * (fx - fy) b = 200 * (fy - fz) - lab[idx, 0] = L - lab[idx, 1] = a - lab[idx, 2] = b + xyz_to_lab_arr[idx, 0] = L + xyz_to_lab_arr[idx, 1] = a + xyz_to_lab_arr[idx, 2] = b -cdef void _lab_to_lch(floating[:] L_arr, floating[:] a_arr, floating[:] b_arr, floating[:, ::1] lch) noexcept nogil: +cdef void _lab_to_lch(floating[:, ::1] lab_to_lch_arr) noexcept nogil: cdef Py_ssize_t idx cdef floating c, h - for idx in range(L_arr.shape[0]): - lch[idx, 0] = L_arr[idx] + for idx in range(lab_to_lch_arr.shape[0]): + # lab_to_lch_arr[idx, 0] = lab_to_lch_arr[idx, 0] # store temporary results then write output to avoid corruption # if the output array is the same as the input arrays - c = ((a_arr[idx] * a_arr[idx]) + (b_arr[idx] * b_arr[idx])) ** 0.5 - h = atan2(b_arr[idx], a_arr[idx]) - lch[idx, 1] = c - lch[idx, 2] = h + c = ((lab_to_lch_arr[idx, 1] * lab_to_lch_arr[idx, 1]) + (lab_to_lch_arr[idx, 2] * lab_to_lch_arr[idx, 2])) ** 0.5 + h = atan2(lab_to_lch_arr[idx, 2], lab_to_lch_arr[idx, 1]) + lab_to_lch_arr[idx, 1] = c + lab_to_lch_arr[idx, 2] = h -cdef void _lch_to_lab(floating[:] l_arr, floating[:] c_arr, floating[:] h_arr, floating[:, ::1] lab_arr) noexcept nogil: +cdef void _lch_to_lab(floating[:, ::1] lch_to_lab_arr) noexcept nogil: cdef floating a, b cdef Py_ssize_t idx - for idx in range(l_arr.shape[0]): - a = c_arr[idx] * cos(h_arr[idx]) - b = c_arr[idx] * sin(h_arr[idx]) - lab_arr[idx, 0] = l_arr[idx] - lab_arr[idx, 1] = a - lab_arr[idx, 2] = b + for idx in range(lch_to_lab_arr.shape[0]): + a = lch_to_lab_arr[idx, 1] * cos(lch_to_lab_arr[idx, 2]) + b = lch_to_lab_arr[idx, 1] * sin(lch_to_lab_arr[idx, 2]) + lch_to_lab_arr[idx, 0] = lch_to_lab_arr[idx, 0] + lch_to_lab_arr[idx, 1] = a + lch_to_lab_arr[idx, 2] = b -cdef void _lab_to_xyz(floating[:] l_arr, floating[:] a_arr, floating[:] b_arr, floating[:, ::1] xyz_arr) noexcept nogil: +cdef void _lab_to_xyz(floating[:, ::1] lab_to_xyz_arr) noexcept nogil: cdef floating x, y, z, L, a, b, tx, ty, tz cdef Py_ssize_t idx - for idx in range(l_arr.shape[0]): - L = l_arr[idx] - a = a_arr[idx] - b = b_arr[idx] + for idx in range(lab_to_xyz_arr.shape[0]): + L = lab_to_xyz_arr[idx, 0] + a = lab_to_xyz_arr[idx, 1] + b = lab_to_xyz_arr[idx, 2] tx = ((L + 16) / 116.0) + (a / 500.0) if tx > delta: @@ -412,19 +422,19 @@ cdef void _lab_to_xyz(floating[:] l_arr, floating[:] a_arr, floating[:] b_arr, f else: z = 3 * delta * delta * (tz - bintercept) - xyz_arr[idx, 0] = x - xyz_arr[idx, 1] = y - xyz_arr[idx, 2] = z + lab_to_xyz_arr[idx, 0] = x + lab_to_xyz_arr[idx, 1] = y + lab_to_xyz_arr[idx, 2] = z -cdef void _xyz_to_rgb(floating[:] x_arr, floating[:] y_arr, floating[:] z_arr, floating[:, ::1] rgb_arr) noexcept nogil: +cdef void _xyz_to_rgb(floating[:, ::1] xyz_to_rgb_arr) noexcept nogil: cdef floating rlin, glin, blin, r, g, b, x, y, z cdef Py_ssize_t idx - for idx in range(x_arr.shape[0]): - x = x_arr[idx] - y = y_arr[idx] - z = z_arr[idx] + for idx in range(xyz_to_rgb_arr.shape[0]): + x = xyz_to_rgb_arr[idx, 0] + y = xyz_to_rgb_arr[idx, 1] + z = xyz_to_rgb_arr[idx, 2] # uses reference white d65 x = x * xn @@ -441,22 +451,18 @@ cdef void _xyz_to_rgb(floating[:] x_arr, floating[:] y_arr, floating[:] z_arr, f b = _to_nonlinear_rgb(blin) # constrain to 0..1 to deal with any float drift - if r > 1.0: - r = 1.0 - elif r < 0.0: - r = 0.0 - if g > 1.0: - g = 1.0 - elif g < 0.0: - g = 0.0 - if b > 1.0: - b = 1.0 - elif b < 0.0: - b = 0.0 - - rgb_arr[idx, 0] = r - rgb_arr[idx, 1] = g - rgb_arr[idx, 2] = b + r = _clamp_0_1(r) + g = _clamp_0_1(g) + b = _clamp_0_1(b) + + xyz_to_rgb_arr[idx, 0] = r + xyz_to_rgb_arr[idx, 1] = g + xyz_to_rgb_arr[idx, 2] = b + + +cdef inline floating _clamp_0_1(floating val) noexcept nogil: + val = 0.0 if val < 0.0 else val + return 1.0 if val > 1.0 else val cdef inline floating _to_nonlinear_rgb(floating rgb_component) noexcept nogil: @@ -472,14 +478,14 @@ cdef inline floating _to_nonlinear_srgb_compand(floating rgb_component) noexcept return (1.055 * (rgb_component ** (1 / 2.4))) - 0.055 -cdef void _xyz_to_luv(floating[:] x_arr, floating[:] y_arr, floating[:] z_arr, floating[:, ::1] luv_arr) noexcept nogil: +cdef void _xyz_to_luv(floating[:, ::1] xyz_to_luv_arr) noexcept nogil: cdef floating L, u, v, uprime, vprime, denom, x, y, z cdef Py_ssize_t idx - for idx in range(x_arr.shape[0]): - x = x_arr[idx] - y = y_arr[idx] - z = z_arr[idx] + for idx in range(xyz_to_luv_arr.shape[0]): + x = xyz_to_luv_arr[idx, 0] + y = xyz_to_luv_arr[idx, 1] + z = xyz_to_luv_arr[idx, 2] denom = x + (15 * y) + (3 * z) uprime = (4 * x) / denom @@ -495,24 +501,24 @@ cdef void _xyz_to_luv(floating[:] x_arr, floating[:] y_arr, floating[:] z_arr, f u = 13 * L * (uprime - uprime_n) v = 13 * L * (vprime - vprime_n) - luv_arr[idx, 0] = L - luv_arr[idx, 1] = u - luv_arr[idx, 2] = v + xyz_to_luv_arr[idx, 0] = L + xyz_to_luv_arr[idx, 1] = u + xyz_to_luv_arr[idx, 2] = v -cdef void _luv_to_xyz(floating[:] l_arr, floating[:] u_arr, floating[:] v_arr, floating[:, ::1] xyz_arr) noexcept nogil: +cdef void _luv_to_xyz(floating[:, ::1] luv_to_xyz_arr) noexcept nogil: cdef floating x, y, z, uprime, vprime, L, u, v cdef Py_ssize_t idx - for idx in range(l_arr.shape[0]): - L = l_arr[idx] - u = u_arr[idx] - v = v_arr[idx] + for idx in range(luv_to_xyz_arr.shape[0]): + L = luv_to_xyz_arr[idx, 0] + u = luv_to_xyz_arr[idx, 1] + v = luv_to_xyz_arr[idx, 2] if L == 0.0: - xyz_arr[idx, 0] = 0.0 - xyz_arr[idx, 1] = 0.0 - xyz_arr[idx, 2] = 0.0 + luv_to_xyz_arr[idx, 0] = 0.0 + luv_to_xyz_arr[idx, 1] = 0.0 + luv_to_xyz_arr[idx, 2] = 0.0 continue uprime = (u / (13 * L)) + uprime_n @@ -526,6 +532,6 @@ cdef void _luv_to_xyz(floating[:] l_arr, floating[:] u_arr, floating[:] v_arr, f x = y * ((9 * uprime) / (4 * vprime)) z = y * ((12 - (3 * uprime) - (20 * vprime)) / (4 * vprime)) - xyz_arr[idx, 0] = x - xyz_arr[idx, 1] = y - xyz_arr[idx, 2] = z + luv_to_xyz_arr[idx, 0] = x + luv_to_xyz_arr[idx, 1] = y + luv_to_xyz_arr[idx, 2] = z diff --git a/trollimage/colormap.py b/trollimage/colormap.py index 358039c1..ae743a94 100644 --- a/trollimage/colormap.py +++ b/trollimage/colormap.py @@ -86,7 +86,7 @@ def _colorize_dask(dask_array, colors, values): The channels are stacked on the first dimension. """ - return dask_array.map_blocks(_colorize, colors, values, dtype=colors.dtype, new_axis=0, + return dask_array.map_blocks(_colorize, colors, values, dtype=dask_array.dtype, new_axis=0, chunks=[colors.shape[1]] + list(dask_array.chunks)) @@ -100,14 +100,18 @@ def _colorize(arr, colors, values): def _interpolate_rgb_colors(arr, colors, values): - interp_xp_coords = np.array(values) + if not np.issubdtype(arr.dtype, np.floating): + # force data to floating point for Cython code + arr = arr.astype(np.float32) + interp_xp_coords = np.asarray(values, dtype=arr.dtype) + colors = np.asarray(colors, dtype=arr.dtype) interp_y_coords = rgb2lch(colors) if values[0] > values[-1]: # monotonically decreasing interp_xp_coords = interp_xp_coords[::-1] interp_y_coords = interp_y_coords[::-1] # Make sure hue (radians) are consistently increasing or decreasing - interp_lch = np.zeros(arr.shape + (3,), dtype=interp_y_coords.dtype) + interp_lch = np.empty(arr.shape + (3,), dtype=arr.dtype) interp_lch[..., 0] = np.interp(arr, interp_xp_coords, interp_y_coords[..., 0]) interp_lch[..., 1] = np.interp(arr, interp_xp_coords, interp_y_coords[..., 1]) interp_y_coords[..., 2] = np.unwrap(interp_y_coords[..., 2]) diff --git a/trollimage/tests/test_colormap.py b/trollimage/tests/test_colormap.py index f3646659..0ad995f9 100644 --- a/trollimage/tests/test_colormap.py +++ b/trollimage/tests/test_colormap.py @@ -410,13 +410,13 @@ def test_colorize_no_interpolation(self, input_cmap_func, expected_result): (_mono_inc_colormap, np.array([ [0.43301, 1.0, 0.639861], - [0.738804, 1.0, 0.926142], + [0.730304, 1.0, 0.937484], [0.466327, 0.466327, 0.466327], [0.0, 0.0, 0.0]])), (_mono_dec_colormap, np.array([ [0.466327, 0.466327, 0.466327], - [0.738804, 1.0, 0.926142], + [0.730304, 1.0, 0.937484], [0.43301, 1.0, 0.639861], [1.0, 1.0, 0.0]])), ] @@ -433,22 +433,27 @@ def test_colorize_with_interpolation(self, input_cmap_func, expected_result): np.testing.assert_allclose(output_colors[2], expected_result[2], atol=0.001) np.testing.assert_allclose(output_colors[3], expected_result[3], atol=0.001) - def test_colorize_dask_with_interpolation(self): + @pytest.mark.parametrize("dtype", [np.float32, np.float64]) + def test_colorize_dask_with_interpolation(self, dtype): """Test colorize dask arrays.""" import dask.array as da data = da.from_array(np.array([[1.5, 2.5, 3.5, 4], [1.5, 2.5, 3.5, 4], - [1.5, 2.5, 3.5, 4]]), chunks=-1) + [1.5, 2.5, 3.5, 4]], dtype=dtype), chunks=-1) - expected_channels = [np.array([[0.43301012, 0.73880362, 0.46632665, 0.], - [0.43301012, 0.73880362, 0.46632665, 0.], - [0.43301012, 0.73880362, 0.46632665, 0.]]), + expected_channels = [np.array([[0.43301012, 0.730304, 0.46632665, 0.], + [0.43301012, 0.730304, 0.46632665, 0.], + [0.43301012, 0.730304, 0.46632665, 0.]], dtype=dtype), np.array([[1., 1., 0.46632662, 0.], [1., 1., 0.46632662, 0.], - [1., 1., 0.46632662, 0.]]), - np.array([[0.63986057, 0.92614193, 0.46632658, 0.], - [0.63986057, 0.92614193, 0.46632658, 0.], - [0.63986057, 0.92614193, 0.46632658, 0.]])] + [1., 1., 0.46632662, 0.]], dtype=dtype), + np.array([[0.639861, 0.937484, 0.466327, 0.], + [0.639861, 0.937484, 0.466327, 0.], + [0.639861, 0.937484, 0.466327, 0.]], dtype=dtype)] + if dtype is np.float32: + expected_channels[0][:, 1] = 0.875065 + expected_channels[1][:, 1] = 0.949392 + expected_channels[2][:, 1] = 1.0 cm = _mono_inc_colormap() import dask @@ -456,6 +461,8 @@ def test_colorize_dask_with_interpolation(self): channels = cm.colorize(data) assert isinstance(channels, da.Array) channels_np = channels.compute() + assert channels_np.dtype == channels.dtype + assert channels_np.dtype == dtype np.testing.assert_allclose(channels_np[0], expected_channels[0], atol=0.001) np.testing.assert_allclose(channels_np[1], expected_channels[1], atol=0.001) np.testing.assert_allclose(channels_np[2], expected_channels[2], atol=0.001) diff --git a/trollimage/tests/test_image.py b/trollimage/tests/test_image.py index be83bfa6..bf9bda24 100644 --- a/trollimage/tests/test_image.py +++ b/trollimage/tests/test_image.py @@ -2025,66 +2025,66 @@ class TestXRImageColorize: """Test the colorize method of the XRImage class.""" _expected = np.array([[ - [3.29411723e-01, 3.57655082e-01, 3.86434110e-01, 4.15693606e-01, - 4.45354600e-01, 4.75400861e-01, 5.05821366e-01, 5.36605929e-01, - 5.65154978e-01, 5.92088497e-01, 6.19067971e-01, 6.46087246e-01, - 6.73140324e-01, 7.00221360e-01, 7.27324645e-01], - [7.52329770e-01, 7.68885184e-01, 7.85480717e-01, 8.02165033e-01, - 8.18991652e-01, 8.36019205e-01, 8.53311577e-01, 8.70937939e-01, - 8.84215464e-01, 8.96340860e-01, 9.08470028e-01, 9.20615990e-01, - 9.32792728e-01, 9.45015153e-01, 9.57299069e-01], - [9.57098327e-01, 9.40960114e-01, 9.29584947e-01, 9.23677290e-01, - 9.23753761e-01, 9.30068208e-01, 9.42551542e-01, 9.60784273e-01, - 9.41109213e-01, 9.19647970e-01, 8.96571901e-01, 8.72056790e-01, - 8.46282229e-01, 8.19431622e-01, 7.91692975e-01], - [7.58448192e-01, 7.21741664e-01, 6.84822731e-01, 6.47626513e-01, - 6.10070647e-01, 5.72048960e-01, 5.33421992e-01, 4.94570855e-01, - 4.57464094e-01, 4.20002632e-01, 3.82018455e-01, 3.43266518e-01, - 3.03372572e-01, 2.61727458e-01, 2.17242854e-01], - [1.89905753e-01, 1.67063022e-01, 1.43524406e-01, 1.18889110e-01, - 9.24115112e-02, 6.24348956e-02, 2.53761173e-02, 4.08181032e-03, - 4.27986478e-03, 4.17929690e-03, 3.78662146e-03, 3.12692318e-03, - 2.24023940e-03, 1.17807264e-03, 3.21825881e-08]], - [[1.88235327e-01, 2.05148705e-01, 2.22246533e-01, 2.39526068e-01, - 2.56989487e-01, 2.74629823e-01, 2.92439994e-01, 3.10413422e-01, - 3.32343814e-01, 3.57065419e-01, 3.82068278e-01, 4.07348961e-01, - 4.32903760e-01, 4.58728817e-01, 4.84820203e-01], - [5.12920806e-01, 5.47946930e-01, 5.82732545e-01, 6.17314757e-01, - 6.51719365e-01, 6.85963748e-01, 7.20058900e-01, 7.54010901e-01, - 7.76938576e-01, 7.97119666e-01, 8.17286145e-01, 8.37436050e-01, - 8.57567245e-01, 8.77677444e-01, 8.97764221e-01], - [9.14974807e-01, 9.26634684e-01, 9.36490370e-01, 9.44572058e-01, - 9.50936890e-01, 9.55671974e-01, 9.58899694e-01, 9.60784377e-01, - 9.54533524e-01, 9.48563492e-01, 9.42789228e-01, 9.37126769e-01, - 9.31494725e-01, 9.25815456e-01, 9.20015949e-01], - [9.08501736e-01, 8.93232137e-01, 8.77927046e-01, 8.62584949e-01, - 8.47204357e-01, 8.31783811e-01, 8.16321878e-01, 7.98071154e-01, - 7.68921238e-01, 7.39943765e-01, 7.11141597e-01, 6.82517728e-01, - 6.54075286e-01, 6.25817541e-01, 5.97747906e-01], - [5.70776450e-01, 5.44247780e-01, 5.17943011e-01, 4.91867999e-01, - 4.66028940e-01, 4.40432405e-01, 4.15085375e-01, 3.90762603e-01, - 3.67819656e-01, 3.45100714e-01, 3.22617398e-01, 3.00381887e-01, - 2.78406993e-01, 2.56706267e-01, 2.35294109e-01]], - [[1.96078164e-02, 2.42548791e-02, 2.74972980e-02, 2.96227786e-02, - 3.17156285e-02, 3.38568546e-02, 3.60498743e-02, 3.82990372e-02, - 5.17340107e-02, 7.13424499e-02, 9.00791380e-02, 1.08349520e-01, - 1.26372958e-01, 1.44280386e-01, 1.62155431e-01], - [1.84723724e-01, 2.25766583e-01, 2.66872651e-01, 3.08395883e-01, - 3.50522786e-01, 3.93349758e-01, 4.36919863e-01, 4.81242202e-01, - 5.19495725e-01, 5.56210021e-01, 5.93054317e-01, 6.30051817e-01, - 6.67218407e-01, 7.04564489e-01, 7.42096284e-01], - [7.75703258e-01, 8.04590533e-01, 8.34519408e-01, 8.64314044e-01, - 8.92841230e-01, 9.19042335e-01, 9.41963593e-01, 9.60784303e-01, - 9.45735072e-01, 9.32649658e-01, 9.21608884e-01, 9.12665692e-01, - 9.05844317e-01, 9.01139812e-01, 8.98517976e-01], - [8.86846758e-01, 8.68087757e-01, 8.49200397e-01, 8.30188000e-01, - 8.11054008e-01, 7.91801982e-01, 7.72435606e-01, 7.51368872e-01, - 7.24059052e-01, 6.97016433e-01, 6.70243011e-01, 6.43740898e-01, - 6.17512331e-01, 5.91559687e-01, 5.65885492e-01], - [5.39262087e-01, 5.12603461e-01, 4.86221750e-01, 4.60123396e-01, - 4.34315297e-01, 4.08804859e-01, 3.83600057e-01, 3.58016749e-01, - 3.31909003e-01, 3.06406088e-01, 2.81515756e-01, 2.57245695e-01, - 2.33603632e-01, 2.10597439e-01, 1.88235281e-01]]]) + [3.29411737e-01, 3.57655096e-01, 3.86434124e-01, 4.15693619e-01, + 4.45354613e-01, 4.75400874e-01, 5.05821379e-01, 5.36605942e-01, + 5.65154991e-01, 5.92088509e-01, 6.19067983e-01, 6.46087257e-01, + 6.73140335e-01, 7.00221370e-01, 7.27324655e-01], + [7.52329780e-01, 7.68885192e-01, 7.85480725e-01, 8.02165039e-01, + 8.18991658e-01, 8.36019210e-01, 8.53311582e-01, 8.70937944e-01, + 8.84215466e-01, 8.96340861e-01, 9.08470028e-01, 9.20615989e-01, + 9.32792726e-01, 9.45015152e-01, 9.57299069e-01], + [9.56159426e-01, 9.37986875e-01, 9.25192897e-01, 9.18645198e-01, + 9.18939761e-01, 9.26295798e-01, 9.40479522e-01, 9.60784274e-01, + 9.40020132e-01, 9.17935908e-01, 8.94639401e-01, 8.70237106e-01, + 8.44833768e-01, 8.18532129e-01, 7.91432917e-01], + [7.58448199e-01, 7.21741672e-01, 6.84822740e-01, 6.47626523e-01, + 6.10070658e-01, 5.72048971e-01, 5.33422004e-01, 4.94570868e-01, + 4.57464108e-01, 4.20002646e-01, 3.82018470e-01, 3.43266534e-01, + 3.03372589e-01, 2.61727477e-01, 2.17242874e-01], + [1.89905775e-01, 1.67063045e-01, 1.43524430e-01, 1.18889134e-01, + 9.24115382e-02, 6.24349277e-02, 2.53761544e-02, 4.08184216e-03, + 4.27989281e-03, 4.17932136e-03, 3.78664262e-03, 3.12694131e-03, + 2.24025474e-03, 1.17808547e-03, 4.27413533e-08]], + [[1.88235338e-01, 2.05148716e-01, 2.22246545e-01, 2.39526080e-01, + 2.56989499e-01, 2.74629834e-01, 2.92440006e-01, 3.10413434e-01, + 3.32343826e-01, 3.57065431e-01, 3.82068290e-01, 4.07348972e-01, + 4.32903771e-01, 4.58728828e-01, 4.84820214e-01], + [5.12920816e-01, 5.47946941e-01, 5.82732555e-01, 6.17314767e-01, + 6.51719374e-01, 6.85963755e-01, 7.20058907e-01, 7.54010908e-01, + 7.76938582e-01, 7.97119672e-01, 8.17286151e-01, 8.37436055e-01, + 8.57567250e-01, 8.77677448e-01, 8.97764224e-01], + [9.15264356e-01, 9.27481082e-01, 9.37652197e-01, 9.45817488e-01, + 9.52058640e-01, 9.56503095e-01, 9.59331143e-01, 9.60784378e-01, + 9.54729702e-01, 9.48843339e-01, 9.43068589e-01, 9.37350480e-01, + 9.31636367e-01, 9.25876402e-01, 9.20023868e-01], + [9.08501739e-01, 8.93232140e-01, 8.77927050e-01, 8.62584953e-01, + 8.47204361e-01, 8.31783816e-01, 8.16321883e-01, 7.98071160e-01, + 7.68921244e-01, 7.39943772e-01, 7.11141605e-01, 6.82517736e-01, + 6.54075295e-01, 6.25817550e-01, 5.97747915e-01], + [5.70776460e-01, 5.44247790e-01, 5.17943022e-01, 4.91868010e-01, + 4.66028951e-01, 4.40432416e-01, 4.15085387e-01, 3.90762614e-01, + 3.67819668e-01, 3.45100725e-01, 3.22617410e-01, 3.00381898e-01, + 2.78407005e-01, 2.56706279e-01, 2.35294121e-01]], + [[1.96078107e-02, 2.42548730e-02, 2.74972914e-02, 2.96227826e-02, + 3.17156346e-02, 3.38568632e-02, 3.60498856e-02, 3.82990518e-02, + 5.17340258e-02, 7.13424642e-02, 9.00791521e-02, 1.08349534e-01, + 1.26372972e-01, 1.44280400e-01, 1.62155446e-01], + [1.84723738e-01, 2.25766596e-01, 2.66872663e-01, 3.08395895e-01, + 3.50522797e-01, 3.93349769e-01, 4.36919875e-01, 4.81242213e-01, + 5.19495736e-01, 5.56210031e-01, 5.93054327e-01, 6.30051826e-01, + 6.67218415e-01, 7.04564497e-01, 7.42096291e-01], + [7.75783700e-01, 8.05228737e-01, 8.35977793e-01, 8.66550574e-01, + 8.95532082e-01, 9.21622726e-01, 9.43684792e-01, 9.60784304e-01, + 9.46951904e-01, 9.34756333e-01, 9.24234153e-01, 9.15407055e-01, + 9.08281595e-01, 9.02849071e-01, 8.99085540e-01], + [8.86846761e-01, 8.68087760e-01, 8.49200399e-01, 8.30188003e-01, + 8.11054012e-01, 7.91801986e-01, 7.72435611e-01, 7.51368879e-01, + 7.24059059e-01, 6.97016441e-01, 6.70243019e-01, 6.43740907e-01, + 6.17512340e-01, 5.91559696e-01, 5.65885501e-01], + [5.39262097e-01, 5.12603472e-01, 4.86221761e-01, 4.60123407e-01, + 4.34315308e-01, 4.08804870e-01, 3.83600068e-01, 3.58016760e-01, + 3.31909014e-01, 3.06406099e-01, 2.81515767e-01, 2.57245707e-01, + 2.33603643e-01, 2.10597450e-01, 1.88235292e-01]]]) @pytest.mark.parametrize("colormap_tag", [None, "colormap"]) def test_colorize_geotiff_tag(self, tmp_path, colormap_tag):