# -*- coding: utf-8 -*-
"""
Cylindrical & Spherical Colour Models
=====================================
Defines various cylindrical and spherical colour models:
- :func:`colour.RGB_to_HSV`
- :func:`colour.HSV_to_RGB`
- :func:`colour.RGB_to_HSL`
- :func:`colour.HSL_to_RGB`
These colour models trade off perceptual relevance for computation speed.
They should not be used in the colour science domain although they are useful
for image analysis and provide end user software colour selection tools.
They are provided for convenience and completeness.
References
----------
- :cite:`EasyRGBj` : EasyRGB. (n.d.). RGB --> HSV. Retrieved May 18, 2014,
from http://www.easyrgb.com/index.php?X=MATH&H=20#text20
- :cite:`EasyRGBk` : EasyRGB. (n.d.). HSL --> RGB. Retrieved May 18, 2014,
from http://www.easyrgb.com/index.php?X=MATH&H=19#text19
- :cite:`EasyRGBl` : EasyRGB. (n.d.). RGB --> HSL. Retrieved May 18, 2014,
from http://www.easyrgb.com/index.php?X=MATH&H=18#text18
- :cite:`EasyRGBn` : EasyRGB. (n.d.). HSV --> RGB. Retrieved May 18, 2014,
from http://www.easyrgb.com/index.php?X=MATH&H=21#text21
- :cite:`Smith1978b` : Smith, A. R. (1978). Color gamut transform pairs.
Proceedings of the 5th Annual Conference on Computer Graphics and
Interactive Techniques - SIGGRAPH "78, 12-19. doi:10.1145/800248.807361
- :cite:`Wikipedia2003` : Wikipedia. (2003). HSL and HSV. Retrieved
September 10, 2014, from http://en.wikipedia.org/wiki/HSL_and_HSV
"""
from __future__ import division, unicode_literals
import numpy as np
from colour.utilities import (as_float_array, from_range_1, to_domain_1,
tsplit, tstack)
__author__ = 'Colour Developers'
__copyright__ = 'Copyright (C) 2013-2020 - Colour Developers'
__license__ = 'New BSD License - https://opensource.org/licenses/BSD-3-Clause'
__maintainer__ = 'Colour Developers'
__email__ = 'colour-developers@colour-science.org'
__status__ = 'Production'
__all__ = ['RGB_to_HSV', 'HSV_to_RGB', 'RGB_to_HSL', 'HSL_to_RGB']
[docs]def RGB_to_HSV(RGB):
"""
Converts from *RGB* colourspace to *HSV* colourspace.
Parameters
----------
RGB : array_like
*RGB* colourspace array.
Returns
-------
ndarray
*HSV* array.
Notes
-----
+------------+-----------------------+---------------+
| **Domain** | **Scale - Reference** | **Scale - 1** |
+============+=======================+===============+
| ``RGB`` | [0, 1] | [0, 1] |
+------------+-----------------------+---------------+
+------------+-----------------------+---------------+
| **Range** | **Scale - Reference** | **Scale - 1** |
+============+=======================+===============+
| ``HSV`` | [0, 1] | [0, 1] |
+------------+-----------------------+---------------+
References
----------
:cite:`EasyRGBj`, :cite:`Smith1978b`, :cite:`Wikipedia2003`
Examples
--------
>>> RGB = np.array([0.45620519, 0.03081071, 0.04091952])
>>> RGB_to_HSV(RGB) # doctest: +ELLIPSIS
array([ 0.9960394..., 0.9324630..., 0.4562051...])
"""
RGB = to_domain_1(RGB)
maximum = np.amax(RGB, -1)
delta = np.ptp(RGB, -1)
V = maximum
R, G, B = tsplit(RGB)
S = as_float_array(delta / maximum)
S[np.asarray(delta == 0)] = 0
delta_R = (((maximum - R) / 6) + (delta / 2)) / delta
delta_G = (((maximum - G) / 6) + (delta / 2)) / delta
delta_B = (((maximum - B) / 6) + (delta / 2)) / delta
H = delta_B - delta_G
H = np.where(G == maximum, (1 / 3) + delta_R - delta_B, H)
H = np.where(B == maximum, (2 / 3) + delta_G - delta_R, H)
H[np.asarray(H < 0)] += 1
H[np.asarray(H > 1)] -= 1
H[np.asarray(delta == 0)] = 0
HSV = tstack([H, S, V])
return from_range_1(HSV)
[docs]def HSV_to_RGB(HSV):
"""
Converts from *HSV* colourspace to *RGB* colourspace.
Parameters
----------
HSV : array_like
*HSV* colourspace array.
Returns
-------
ndarray
*RGB* colourspace array.
Notes
-----
+------------+-----------------------+---------------+
| **Domain** | **Scale - Reference** | **Scale - 1** |
+============+=======================+===============+
| ``HSV`` | [0, 1] | [0, 1] |
+------------+-----------------------+---------------+
+------------+-----------------------+---------------+
| **Range** | **Scale - Reference** | **Scale - 1** |
+============+=======================+===============+
| ``RGB`` | [0, 1] | [0, 1] |
+------------+-----------------------+---------------+
References
----------
:cite:`EasyRGBn`, :cite:`Smith1978b`, :cite:`Wikipedia2003`
Examples
--------
>>> HSV = np.array([0.99603944, 0.93246304, 0.45620519])
>>> HSV_to_RGB(HSV) # doctest: +ELLIPSIS
array([ 0.4562051..., 0.0308107..., 0.0409195...])
"""
H, S, V = tsplit(to_domain_1(HSV))
h = as_float_array(H * 6)
h[np.asarray(h == 6)] = 0
i = np.floor(h)
j = V * (1 - S)
k = V * (1 - S * (h - i))
l = V * (1 - S * (1 - (h - i))) # noqa
i = tstack([i, i, i]).astype(np.uint8)
RGB = np.choose(
i, [
tstack([V, l, j]),
tstack([k, V, j]),
tstack([j, V, l]),
tstack([j, k, V]),
tstack([l, j, V]),
tstack([V, j, k]),
],
mode='clip')
return from_range_1(RGB)
[docs]def RGB_to_HSL(RGB):
"""
Converts from *RGB* colourspace to *HSL* colourspace.
Parameters
----------
RGB : array_like
*RGB* colourspace array.
Returns
-------
ndarray
*HSL* array.
Notes
-----
+------------+-----------------------+---------------+
| **Domain** | **Scale - Reference** | **Scale - 1** |
+============+=======================+===============+
| ``RGB`` | [0, 1] | [0, 1] |
+------------+-----------------------+---------------+
+------------+-----------------------+---------------+
| **Range** | **Scale - Reference** | **Scale - 1** |
+============+=======================+===============+
| ``HSL`` | [0, 1] | [0, 1] |
+------------+-----------------------+---------------+
References
----------
:cite:`EasyRGBl`, :cite:`Smith1978b`, :cite:`Wikipedia2003`
Examples
--------
>>> RGB = np.array([0.45620519, 0.03081071, 0.04091952])
>>> RGB_to_HSL(RGB) # doctest: +ELLIPSIS
array([ 0.9960394..., 0.8734714..., 0.2435079...])
"""
RGB = to_domain_1(RGB)
minimum = np.amin(RGB, -1)
maximum = np.amax(RGB, -1)
delta = np.ptp(RGB, -1)
R, G, B = tsplit(RGB)
L = (maximum + minimum) / 2
S = np.where(
L < 0.5,
delta / (maximum + minimum),
delta / (2 - maximum - minimum),
)
S[np.asarray(delta == 0)] = 0
delta_R = (((maximum - R) / 6) + (delta / 2)) / delta
delta_G = (((maximum - G) / 6) + (delta / 2)) / delta
delta_B = (((maximum - B) / 6) + (delta / 2)) / delta
H = delta_B - delta_G
H = np.where(G == maximum, (1 / 3) + delta_R - delta_B, H)
H = np.where(B == maximum, (2 / 3) + delta_G - delta_R, H)
H[np.asarray(H < 0)] += 1
H[np.asarray(H > 1)] -= 1
H[np.asarray(delta == 0)] = 0
HSL = tstack([H, S, L])
return from_range_1(HSL)
[docs]def HSL_to_RGB(HSL):
"""
Converts from *HSL* colourspace to *RGB* colourspace.
Parameters
----------
HSL : array_like
*HSL* colourspace array.
Returns
-------
ndarray
*RGB* colourspace array.
Notes
-----
+------------+-----------------------+---------------+
| **Domain** | **Scale - Reference** | **Scale - 1** |
+============+=======================+===============+
| ``HSL`` | [0, 1] | [0, 1] |
+------------+-----------------------+---------------+
+------------+-----------------------+---------------+
| **Range** | **Scale - Reference** | **Scale - 1** |
+============+=======================+===============+
| ``RGB`` | [0, 1] | [0, 1] |
+------------+-----------------------+---------------+
References
----------
:cite:`EasyRGBk`, :cite:`Smith1978b`, :cite:`Wikipedia2003`
Examples
--------
>>> HSL = np.array([0.99603944, 0.87347144, 0.24350795])
>>> HSL_to_RGB(HSL) # doctest: +ELLIPSIS
array([ 0.4562051..., 0.0308107..., 0.0409195...])
"""
H, S, L = tsplit(to_domain_1(HSL))
def H_to_RGB(vi, vj, vH):
"""
Converts *hue* value to *RGB* colourspace.
"""
vH = as_float_array(vH)
vH[np.asarray(vH < 0)] += 1
vH[np.asarray(vH > 1)] -= 1
v = np.where(
6 * vH < 1,
vi + (vj - vi) * 6 * vH,
np.nan,
)
v = np.where(np.logical_and(2 * vH < 1, np.isnan(v)), vj, v)
v = np.where(
np.logical_and(3 * vH < 2, np.isnan(v)),
vi + (vj - vi) * ((2 / 3) - vH) * 6,
v,
)
v = np.where(np.isnan(v), vi, v)
return v
j = np.where(L < 0.5, L * (1 + S), (L + S) - (S * L))
i = 2 * L - j
R = H_to_RGB(i, j, H + (1 / 3))
G = H_to_RGB(i, j, H)
B = H_to_RGB(i, j, H - (1 / 3))
R = np.where(S == 0, L, R)
G = np.where(S == 0, L, G)
B = np.where(S == 0, L, B)
RGB = tstack([R, G, B])
return from_range_1(RGB)