import numpy as np
from typing import Union, Sequence
[docs]
def hsv2rgb(hue: Union[float, Sequence, np.ndarray] = None,
saturation: Union[float, Sequence, np.ndarray] = None,
value: Union[float, Sequence, np.ndarray] = None,
alpha: Union[float, Sequence, np.ndarray] = None,
axis: int = -1) -> np.ndarray:
"""
Converts a hue-saturation-intensity value image to a red-green-blue image.
:param hue: A 2D numpy.array with the hue.
If the saturation is not provided, the first argument will be interpreted as a 3D numpy.array with the HSV image,
the channel must be in the final right-hand dimension.
:param saturation: (optional) a 2D numpy.array with the saturation.
:param value: (optional) a 2D numpy.array with the intensity value.
:param alpha: (optional) a 2D numpy.array with the opaqueness alpha value.
:param axis: (default: -1) The channel axis of the output array, and also the input array if neither saturation, nor
value are provided.
:return: rgb_image: a 3D numpy.array with the RGB image, the channel in the final right-hand dimension, or axis if provided.
"""
if saturation is None and value is None:
hsv_image = np.moveaxis(hue, axis, 0)
if alpha is None and hsv_image.shape[0] > 3:
alpha = hsv_image[3]
value = hsv_image[2]
saturation = hsv_image[1]
hue = hsv_image[0]
else:
hue = np.asarray(hue)
saturation = np.asarray(saturation)
value = np.asarray(value)
hue = 6.0 * hue
i = hue.astype(np.int8) # integer level
f = hue - i # fractional level
i %= 6
p = value * (1.0 - saturation)
q = value * (1.0 - saturation * f)
t = value * (1.0 - (saturation * (1.0 - f)))
r = ((i == 0) | (i == 5)) * value + (i == 1) * q + ((i == 2) | (i == 3)) * p + (i == 4) * t
g = (i == 0) * t + ((i == 1) | (i == 2)) * value + (i == 3) * q + (i >= 4) * p
b = (i <= 1) * p + (i == 2) * t + ((i == 3) | (i == 4)) * value + (i == 5) * q
if alpha is None:
rgb_image = np.stack(np.broadcast_arrays(r, g, b))
else:
rgb_image = np.stack(np.broadcast_arrays(r, g, b, alpha))
return np.moveaxis(rgb_image, 0, axis)
[docs]
def rgb2hsv(red: Union[float, Sequence, np.ndarray],
green: Union[float, Sequence, np.ndarray] = None,
blue: Union[float, Sequence, np.ndarray] = None,
alpha: Union[float, Sequence, np.ndarray] = None,
axis=-1) -> np.ndarray:
"""
Converts a red-green-blue value image to a hue-saturation-intensity image.
:param red: An 2D numpy.array with the red channel.
If neither green and blue are provided, then this will be interpreted as a stack of the red, green, and blue channels.
:param green: (optional) a 2D numpy.array with the green channel.
:param blue: (optional) a 2D numpy.array with the blue channel.
:param alpha: (optional) a 2D numpy.array with the opaqueness alpha value.
:param axis: (default: -1) The channel axis of the output array, and also the input array if neither saturation, nor
value are provided.
:return: hsv_image: a 3D numpy.array with the HSV image
"""
if green is None and blue is None:
rgb_image = np.moveaxis(red, axis, 0)
elif alpha is None:
rgb_image = np.stack(np.broadcast_arrays(red, green, blue))
else:
rgb_image = np.stack(np.broadcast_arrays(red, green, blue, alpha))
v = np.amax(rgb_image, axis=0)
s_x_v = v - np.amin(rgb_image, axis=0)
def per_pixel_select(arr, idx):
selection = np.zeros(arr.shape[1:], dtype=arr.dtype)
for channel in range(rgb_image.shape[0]):
selection += arr[channel] * (idx == channel)
return selection
ch1 = (rgb_image[0] < rgb_image[1]) * (rgb_image[0] <= rgb_image[2])
ch2 = (rgb_image[1] < rgb_image[2]) * (rgb_image[1] <= rgb_image[0])
channel = np.array(ch1 + 2 * ch2) # dominant channel
dominant = per_pixel_select(rgb_image, channel) # mix from channel,
after = per_pixel_select(rgb_image, np.mod(channel + 1, 3)) # to channel,
before = per_pixel_select(rgb_image, np.mod(channel - 1, 3)) # and the other channel
i = 2 * channel + (dominant <= after) * (1 - np.isclose(after, before)) # interval index in range(6)
f = np.mod((dominant - after) / (s_x_v + np.isclose(s_x_v, 0)), 1) # fraction in interval
# 1 0 0 0/6 2
# 1 1 0 1/6 2
# 0 1 0 2/6 0
# 0 1 1 3/6 0
# 0 0 1 4/6 1
# 1 0 1 5/6 1
# 1 0 0 6/6
h = (i + f) / 6
s = s_x_v / (v + np.isclose(v, 0))
if alpha is None:
hsv_image = np.stack((h, s, v))
else:
hsv_image = np.stack((h, s, v, alpha))
return np.moveaxis(hsv_image, 0, axis)