Add colorspaces: YUV, YIQ, YPbPr, YCbCr (#2060)

Add YUV, YIQ, YPbPr, YCbCr colorspaces
This commit is contained in:
Egor Panfilov
2016-05-08 20:03:49 +03:00
4 changed files with 286 additions and 2 deletions
+3
View File
@@ -224,3 +224,6 @@
- Evgeni Burovski
Adaptation of ImageJ 3D skeletonization algorithm.
- Alex Izvorski
Color spaces for YUV and related spaces
+16
View File
@@ -22,6 +22,14 @@ from .colorconv import (convert_colorspace,
hed2rgb,
lab2lch,
lch2lab,
rgb2yuv,
yuv2rgb,
rgb2yiq,
yiq2rgb,
rgb2ypbpr,
ypbpr2rgb,
rgb2ycbcr,
ycbcr2rgb,
separate_stains,
combine_stains,
rgb_from_hed,
@@ -75,6 +83,14 @@ __all__ = ['convert_colorspace',
'hed2rgb',
'lab2lch',
'lch2lab',
'rgb2yuv',
'yuv2rgb',
'rgb2yiq',
'yiq2rgb',
'rgb2ypbpr',
'ypbpr2rgb',
'rgb2ycbcr',
'ycbcr2rgb',
'separate_stains',
'combine_stains',
'rgb_from_hed',
+236 -2
View File
@@ -40,6 +40,7 @@ Supported color spaces
:author: Ralf Gommers (hsv2rgb)
:author: Travis Oliphant (XYZ and RGB CIE functions)
:author: Matt Terry (lab2lch)
:author: Alex Izvorski (yuv2rgb, rgb2yuv and related)
:license: modified BSD
@@ -124,9 +125,11 @@ def convert_colorspace(arr, fromspace, tospace):
>>> img_hsv = convert_colorspace(img, 'RGB', 'HSV')
"""
fromdict = {'RGB': lambda im: im, 'HSV': hsv2rgb, 'RGB CIE': rgbcie2rgb,
'XYZ': xyz2rgb}
'XYZ': xyz2rgb, 'YUV': yuv2rgb, 'YIQ': yiq2rgb,
'YPbPr': ypbpr2rgb, 'YCbCr': ycbcr2rgb }
todict = {'RGB': lambda im: im, 'HSV': rgb2hsv, 'RGB CIE': rgb2rgbcie,
'XYZ': rgb2xyz}
'XYZ': rgb2xyz, 'YUV': rgb2yuv, 'YIQ': rgb2yiq,
'YPbPr': rgb2ypbpr, 'YCbCr': rgb2ycbcr }
fromspace = fromspace.upper()
tospace = tospace.upper()
@@ -322,6 +325,30 @@ gray_from_rgb = np.array([[0.2125, 0.7154, 0.0721],
[0, 0, 0],
[0, 0, 0]])
yuv_from_rgb = np.array([[ 0.299 , 0.587 , 0.114 ],
[-0.14714119, -0.28886916, 0.43601035 ],
[ 0.61497538, -0.51496512, -0.10001026 ]])
rgb_from_yuv = linalg.inv(yuv_from_rgb)
yiq_from_rgb = np.array([[0.299 , 0.587 , 0.114 ],
[0.59590059, -0.27455667, -0.32134392],
[0.21153661, -0.52273617, 0.31119955]])
rgb_from_yiq = linalg.inv(yiq_from_rgb)
ypbpr_from_rgb = np.array([[ 0.299 , 0.587 , 0.114 ],
[-0.168736,-0.331264, 0.5 ],
[ 0.5 ,-0.418688,-0.081312]])
rgb_from_ypbpr = linalg.inv(ypbpr_from_rgb)
ycbcr_from_rgb = np.array([[ 65.481, 128.553, 24.966],
[ -37.797, -74.203, 112.0 ],
[ 112.0 , -93.786, -18.214]])
rgb_from_ycbcr = linalg.inv(ycbcr_from_rgb)
# CIE LAB constants for Observer=2A, Illuminant=D65
# NOTE: this is actually the XYZ values for the illuminant above.
lab_ref_white = np.array([0.95047, 1., 1.08883])
@@ -1450,3 +1477,210 @@ def _prepare_lab_array(arr):
if shape[-1] < 3:
raise ValueError('Input array has less than 3 color channels')
return dtype.img_as_float(arr, force_copy=True)
def rgb2yuv(rgb):
"""RGB to YUV color space conversion.
Parameters
----------
rgb : array_like
The image in RGB format, in a 3- or 4-D array of shape
``(M, N, [P,] 3)``.
Returns
-------
out : ndarray
The image in YUV format, in a 3- or 4-D array of shape
``(M, N, [P,] 3)``.
Raises
------
ValueError
If `rgb` is not a 3- or 4-D array of shape ``(M, N, [P,] 3)``.
Notes
-----
Y is between 0 and 1. Use YCbCr instead of YUV for the color space which
is commonly used by video codecs (where Y ranges from 16 to 235)
"""
return _convert(yuv_from_rgb, rgb)
def rgb2yiq(rgb):
"""RGB to YIQ color space conversion.
Parameters
----------
rgb : array_like
The image in RGB format, in a 3- or 4-D array of shape
``(M, N, [P,] 3)``.
Returns
-------
out : ndarray
The image in YIQ format, in a 3- or 4-D array of shape
``(M, N, [P,] 3)``.
Raises
------
ValueError
If `rgb` is not a 3- or 4-D array of shape ``(M, N, [P,] 3)``.
"""
return _convert(yiq_from_rgb, rgb)
def rgb2ypbpr(rgb):
"""RGB to YIQ color space conversion.
Parameters
----------
rgb : array_like
The image in RGB format, in a 3- or 4-D array of shape
``(M, N, [P,] 3)``.
Returns
-------
out : ndarray
The image in YIQ format, in a 3- or 4-D array of shape
``(M, N, [P,] 3)``.
Raises
------
ValueError
If `rgb` is not a 3- or 4-D array of shape ``(M, N, [P,] 3)``.
"""
return _convert(ypbpr_from_rgb, rgb)
def rgb2ycbcr(rgb):
"""RGB to YCbCr color space conversion.
Parameters
----------
rgb : array_like
The image in RGB format, in a 3- or 4-D array of shape
``(M, N, [P,] 3)``.
Returns
-------
out : ndarray
The image in YCbCr format, in a 3- or 4-D array of shape
``(M, N, [P,] 3)``.
Raises
------
ValueError
If `rgb` is not a 3- or 4-D array of shape ``(M, N, [P,] 3)``.
Notes
-----
Y is between 16 and 235. This is the color space which is commonly used
by video codecs, it is sometimes incorrectly called "YUV"
"""
arr = _convert(ycbcr_from_rgb, rgb)
arr[..., 0] += 16
arr[..., 1] += 128
arr[..., 2] += 128
return arr
def yuv2rgb(yuv):
"""RGB to YIQ color space conversion.
Parameters
----------
rgb : array_like
The image in RGB format, in a 3- or 4-D array of shape
``(M, N, [P,] 3)``.
Returns
-------
out : ndarray
The image in YIQ format, in a 3- or 4-D array of shape
``(M, N, [P,] 3)``.
Raises
------
ValueError
If `rgb` is not a 3- or 4-D array of shape ``(M, N, [P,] 3)``.
"""
return _convert(rgb_from_yuv, yuv)
def yiq2rgb(yiq):
"""YIQ to RGB color space conversion.
Parameters
----------
yiq : array_like
The image in YIQ format, in a 3- or 4-D array of shape
``(M, N, [P,] 3)``.
Returns
-------
out : ndarray
The image in RGB format, in a 3- or 4-D array of shape
``(M, N, [P,] 3)``.
Raises
------
ValueError
If `yiq` is not a 3- or 4-D array of shape ``(M, N, [P,] 3)``.
"""
return _convert(rgb_from_yiq, yiq)
def ypbpr2rgb(ypbpr):
"""YPbPr to RGB color space conversion.
Parameters
----------
ypbpr : array_like
The image in YPbPr format, in a 3- or 4-D array of shape
``(M, N, [P,] 3)``.
Returns
-------
out : ndarray
The image in RGB format, in a 3- or 4-D array of shape
``(M, N, [P,] 3)``.
Raises
------
ValueError
If `ypbpr` is not a 3- or 4-D array of shape ``(M, N, [P,] 3)``.
"""
return _convert(rgb_from_ypbpr, ypbpr)
def ycbcr2rgb(ycbcr):
"""YCbCr to RGB color space conversion.
Parameters
----------
ycbcr : array_like
The image in YCbCr format, in a 3- or 4-D array of shape
``(M, N, [P,] 3)``.
Returns
-------
out : ndarray
The image in RGB format, in a 3- or 4-D array of shape
``(M, N, [P,] 3)``.
Raises
------
ValueError
If `ycbcr` is not a 3- or 4-D array of shape ``(M, N, [P,] 3)``.
Notes
-----
Y is between 16 and 235. This is the color space which is commonly used
by video codecs, it is sometimes incorrectly called "YUV"
"""
arr = ycbcr.copy()
arr[..., 0] -= 16
arr[..., 1] -= 128
arr[..., 2] -= 128
return _convert(rgb_from_ycbcr, arr)
+31
View File
@@ -37,6 +37,10 @@ from skimage.color import (rgb2hsv, hsv2rgb,
xyz2luv, luv2xyz,
luv2rgb, rgb2luv,
lab2lch, lch2lab,
rgb2yuv, yuv2rgb,
rgb2yiq, yiq2rgb,
rgb2ypbpr, ypbpr2rgb,
rgb2ycbcr, ycbcr2rgb,
guess_spatial_dimensions
)
@@ -438,6 +442,33 @@ class TestColorconv(TestCase):
rgb = img_as_float(self.img_rgb[:1, :1, :])
return rgb2lab(rgb)[0, 0, :]
def test_yuv(self):
rgb = np.array([[[1.0, 1.0, 1.0]]])
assert_array_almost_equal(rgb2yuv(rgb), np.array([[[1, 0, 0]]]))
assert_array_almost_equal(rgb2yiq(rgb), np.array([[[1, 0, 0]]]))
assert_array_almost_equal(rgb2ypbpr(rgb), np.array([[[1, 0, 0]]]))
assert_array_almost_equal(rgb2ycbcr(rgb), np.array([[[235, 128, 128]]]))
rgb = np.array([[[0.0, 1.0, 0.0]]])
assert_array_almost_equal(rgb2yuv(rgb), np.array([[[0.587, -0.28886916, -0.51496512]]]))
assert_array_almost_equal(rgb2yiq(rgb), np.array([[[0.587, -0.27455667, -0.52273617]]]))
assert_array_almost_equal(rgb2ypbpr(rgb), np.array([[[0.587, -0.331264, -0.418688]]]))
assert_array_almost_equal(rgb2ycbcr(rgb), np.array([[[144.553, 53.797, 34.214]]]))
def test_yuv_roundtrip(self):
img_rgb = img_as_float(self.img_rgb)[::16, ::16]
assert_array_almost_equal(yuv2rgb(rgb2yuv(img_rgb)), img_rgb)
assert_array_almost_equal(yiq2rgb(rgb2yiq(img_rgb)), img_rgb)
assert_array_almost_equal(ypbpr2rgb(rgb2ypbpr(img_rgb)), img_rgb)
assert_array_almost_equal(ycbcr2rgb(rgb2ycbcr(img_rgb)), img_rgb)
def test_rgb2yiq_conversion(self):
rgb = img_as_float(self.img_rgb)[::16, ::16]
yiq = rgb2yiq(rgb).reshape(-1, 3)
gt = np.array([colorsys.rgb_to_yiq(pt[0], pt[1], pt[2])
for pt in rgb.reshape(-1, 3)]
)
assert_almost_equal(yiq, gt, decimal=2)
def test_gray2rgb():
x = np.array([0, 0.5, 1])