CofeehousePy/deps/scikit-image/skimage/color/colorconv.py

1899 lines
56 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""Functions for converting between color spaces.
The "central" color space in this module is RGB, more specifically the linear
sRGB color space using D65 as a white-point [1]_. This represents a
standard monitor (w/o gamma correction). For a good FAQ on color spaces see
[2]_.
The API consists of functions to convert to and from RGB as defined above, as
well as a generic function to convert to and from any supported color space
(which is done through RGB in most cases).
Supported color spaces
----------------------
* RGB : Red Green Blue.
Here the sRGB standard [1]_.
* HSV : Hue, Saturation, Value.
Uniquely defined when related to sRGB [3]_.
* RGB CIE : Red Green Blue.
The original RGB CIE standard from 1931 [4]_. Primary colors are 700 nm
(red), 546.1 nm (blue) and 435.8 nm (green).
* XYZ CIE : XYZ
Derived from the RGB CIE color space. Chosen such that
``x == y == z == 1/3`` at the whitepoint, and all color matching
functions are greater than zero everywhere.
* LAB CIE : Lightness, a, b
Colorspace derived from XYZ CIE that is intended to be more
perceptually uniform
* LUV CIE : Lightness, u, v
Colorspace derived from XYZ CIE that is intended to be more
perceptually uniform
* LCH CIE : Lightness, Chroma, Hue
Defined in terms of LAB CIE. C and H are the polar representation of
a and b. The polar angle C is defined to be on ``(0, 2*pi)``
:author: Nicolas Pinto (rgb2hsv)
: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
References
----------
.. [1] Official specification of sRGB, IEC 61966-2-1:1999.
.. [2] http://www.poynton.com/ColorFAQ.html
.. [3] https://en.wikipedia.org/wiki/HSL_and_HSV
.. [4] https://en.wikipedia.org/wiki/CIE_1931_color_space
"""
import functools
import numpy as np
from warnings import warn
from scipy import linalg
from ..util import dtype, dtype_limits
def convert_colorspace(arr, fromspace, tospace):
"""Convert an image array to a new color space.
Valid color spaces are:
'RGB', 'HSV', 'RGB CIE', 'XYZ', 'YUV', 'YIQ', 'YPbPr', 'YCbCr', 'YDbDr'
Parameters
----------
arr : (..., 3) array_like
The image to convert. Final dimension denotes channels.
fromspace : str
The color space to convert from. Can be specified in lower case.
tospace : str
The color space to convert to. Can be specified in lower case.
Returns
-------
out : (..., 3) ndarray
The converted image. Same dimensions as input.
Raises
------
ValueError
If fromspace is not a valid color space
ValueError
If tospace is not a valid color space
Notes
-----
Conversion is performed through the "central" RGB color space,
i.e. conversion from XYZ to HSV is implemented as ``XYZ -> RGB -> HSV``
instead of directly.
Examples
--------
>>> from skimage import data
>>> img = data.astronaut()
>>> img_hsv = convert_colorspace(img, 'RGB', 'HSV')
"""
fromdict = {'rgb': lambda im: im, 'hsv': hsv2rgb, 'rgb cie': rgbcie2rgb,
'xyz': xyz2rgb, 'yuv': yuv2rgb, 'yiq': yiq2rgb,
'ypbpr': ypbpr2rgb, 'ycbcr': ycbcr2rgb, 'ydbdr': ydbdr2rgb}
todict = {'rgb': lambda im: im, 'hsv': rgb2hsv, 'rgb cie': rgb2rgbcie,
'xyz': rgb2xyz, 'yuv': rgb2yuv, 'yiq': rgb2yiq,
'ypbpr': rgb2ypbpr, 'ycbcr': rgb2ycbcr, 'ydbdr': rgb2ydbdr}
fromspace = fromspace.lower()
tospace = tospace.lower()
if fromspace not in fromdict:
msg = '`fromspace` has to be one of {}'.format(fromdict.keys())
raise ValueError(msg)
if tospace not in todict:
msg = '`tospace` has to be one of {}'.format(todict.keys())
raise ValueError(msg)
return todict[tospace](fromdict[fromspace](arr))
def _prepare_colorarray(arr, force_copy=False):
"""Check the shape of the array and convert it to
floating point representation.
"""
arr = np.asanyarray(arr)
if arr.shape[-1] != 3:
raise ValueError("Input array must have a shape == (..., 3)), "
f"got {arr.shape}")
return dtype.img_as_float(arr, force_copy=force_copy)
def rgba2rgb(rgba, background=(1, 1, 1)):
"""RGBA to RGB conversion using alpha blending [1]_.
Parameters
----------
rgba : (..., 4) array_like
The image in RGBA format. Final dimension denotes channels.
background : array_like
The color of the background to blend the image with (3 floats
between 0 to 1 - the RGB value of the background).
Returns
-------
out : (..., 3) ndarray
The image in RGB format. Same dimensions as input.
Raises
------
ValueError
If `rgba` is not at least 2-D with shape (..., 4).
References
----------
.. [1] https://en.wikipedia.org/wiki/Alpha_compositing#Alpha_blending
Examples
--------
>>> from skimage import color
>>> from skimage import data
>>> img_rgba = data.logo()
>>> img_rgb = color.rgba2rgb(img_rgba)
"""
arr = np.asanyarray(rgba)
if arr.shape[-1] != 4:
msg = ("the input array must have shape == (..., 4)), "
f"got {arr.shape}")
raise ValueError(msg)
arr = dtype.img_as_float(arr)
background = np.ravel(background).astype(arr.dtype)
if len(background) != 3:
raise ValueError('background must be an array-like containing 3 RGB '
f'values. Got {len(background)} items')
if np.any(background < 0) or np.any(background > 1):
raise ValueError('background RGB values must be floats between '
'0 and 1.')
background = background[np.newaxis, ...]
alpha = arr[..., -1, np.newaxis]
channels = arr[np.newaxis, ..., :-1]
out = np.squeeze(np.clip((1 - alpha) * background + alpha * channels,
a_min=0, a_max=1),
axis=0)
return out
def rgb2hsv(rgb):
"""RGB to HSV color space conversion.
Parameters
----------
rgb : (..., 3) array_like
The image in RGB format. Final dimension denotes channels.
Returns
-------
out : (..., 3) ndarray
The image in HSV format. Same dimensions as input.
Raises
------
ValueError
If `rgb` is not at least 2-D with shape (..., 3).
Notes
-----
Conversion between RGB and HSV color spaces results in some loss of
precision, due to integer arithmetic and rounding [1]_.
References
----------
.. [1] https://en.wikipedia.org/wiki/HSL_and_HSV
Examples
--------
>>> from skimage import color
>>> from skimage import data
>>> img = data.astronaut()
>>> img_hsv = color.rgb2hsv(img)
"""
input_is_one_pixel = rgb.ndim == 1
if input_is_one_pixel:
rgb = rgb[np.newaxis, ...]
arr = _prepare_colorarray(rgb)
out = np.empty_like(arr)
# -- V channel
out_v = arr.max(-1)
# -- S channel
delta = arr.ptp(-1)
# Ignore warning for zero divided by zero
old_settings = np.seterr(invalid='ignore')
out_s = delta / out_v
out_s[delta == 0.] = 0.
# -- H channel
# red is max
idx = (arr[..., 0] == out_v)
out[idx, 0] = (arr[idx, 1] - arr[idx, 2]) / delta[idx]
# green is max
idx = (arr[..., 1] == out_v)
out[idx, 0] = 2. + (arr[idx, 2] - arr[idx, 0]) / delta[idx]
# blue is max
idx = (arr[..., 2] == out_v)
out[idx, 0] = 4. + (arr[idx, 0] - arr[idx, 1]) / delta[idx]
out_h = (out[..., 0] / 6.) % 1.
out_h[delta == 0.] = 0.
np.seterr(**old_settings)
# -- output
out[..., 0] = out_h
out[..., 1] = out_s
out[..., 2] = out_v
# # remove NaN
out[np.isnan(out)] = 0
if input_is_one_pixel:
out = np.squeeze(out, axis=0)
return out
def hsv2rgb(hsv):
"""HSV to RGB color space conversion.
Parameters
----------
hsv : (..., 3) array_like
The image in HSV format. Final dimension denotes channels.
Returns
-------
out : (..., 3) ndarray
The image in RGB format. Same dimensions as input.
Raises
------
ValueError
If `hsv` is not at least 2-D with shape (..., 3).
Notes
-----
Conversion between RGB and HSV color spaces results in some loss of
precision, due to integer arithmetic and rounding [1]_.
References
----------
.. [1] https://en.wikipedia.org/wiki/HSL_and_HSV
Examples
--------
>>> from skimage import data
>>> img = data.astronaut()
>>> img_hsv = rgb2hsv(img)
>>> img_rgb = hsv2rgb(img_hsv)
"""
arr = _prepare_colorarray(hsv)
hi = np.floor(arr[..., 0] * 6)
f = arr[..., 0] * 6 - hi
p = arr[..., 2] * (1 - arr[..., 1])
q = arr[..., 2] * (1 - f * arr[..., 1])
t = arr[..., 2] * (1 - (1 - f) * arr[..., 1])
v = arr[..., 2]
hi = np.stack([hi, hi, hi], axis=-1).astype(np.uint8) % 6
out = np.choose(
hi, np.stack([np.stack((v, t, p), axis=-1),
np.stack((q, v, p), axis=-1),
np.stack((p, v, t), axis=-1),
np.stack((p, q, v), axis=-1),
np.stack((t, p, v), axis=-1),
np.stack((v, p, q), axis=-1)]))
return out
# ---------------------------------------------------------------
# Primaries for the coordinate systems
# ---------------------------------------------------------------
cie_primaries = np.array([700, 546.1, 435.8])
sb_primaries = np.array([1. / 155, 1. / 190, 1. / 225]) * 1e5
# ---------------------------------------------------------------
# Matrices that define conversion between different color spaces
# ---------------------------------------------------------------
# From sRGB specification
xyz_from_rgb = np.array([[0.412453, 0.357580, 0.180423],
[0.212671, 0.715160, 0.072169],
[0.019334, 0.119193, 0.950227]])
rgb_from_xyz = linalg.inv(xyz_from_rgb)
# From https://en.wikipedia.org/wiki/CIE_1931_color_space
# Note: Travis's code did not have the divide by 0.17697
xyz_from_rgbcie = np.array([[0.49, 0.31, 0.20],
[0.17697, 0.81240, 0.01063],
[0.00, 0.01, 0.99]]) / 0.17697
rgbcie_from_xyz = linalg.inv(xyz_from_rgbcie)
# construct matrices to and from rgb:
rgbcie_from_rgb = rgbcie_from_xyz @ xyz_from_rgb
rgb_from_rgbcie = rgb_from_xyz @ xyz_from_rgbcie
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)
ydbdr_from_rgb = np.array([[ 0.299, 0.587, 0.114],
[ -0.45 , -0.883, 1.333],
[ -1.333, 1.116, 0.217]])
rgb_from_ydbdr = linalg.inv(ydbdr_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])
# XYZ coordinates of the illuminants, scaled to [0, 1]. For each illuminant I
# we have:
#
# illuminant[I][0] corresponds to the XYZ coordinates for the 2 degree
# field of view.
#
# illuminant[I][1] corresponds to the XYZ coordinates for the 10 degree
# field of view.
#
# The XYZ coordinates are calculated from [1], using the formula:
#
# X = x * ( Y / y )
# Y = Y
# Z = ( 1 - x - y ) * ( Y / y )
#
# where Y = 1. The only exception is the illuminant "D65" with aperture angle
# 2, whose coordinates are copied from 'lab_ref_white' for
# backward-compatibility reasons.
#
# References
# ----------
# .. [1] https://en.wikipedia.org/wiki/Standard_illuminant
illuminants = \
{"A": {'2': (1.098466069456375, 1, 0.3558228003436005),
'10': (1.111420406956693, 1, 0.3519978321919493)},
"D50": {'2': (0.9642119944211994, 1, 0.8251882845188288),
'10': (0.9672062750333777, 1, 0.8142801513128616)},
"D55": {'2': (0.956797052643698, 1, 0.9214805860173273),
'10': (0.9579665682254781, 1, 0.9092525159847462)},
"D65": {'2': (0.95047, 1., 1.08883), # This was: `lab_ref_white`
'10': (0.94809667673716, 1, 1.0730513595166162)},
"D75": {'2': (0.9497220898840717, 1, 1.226393520724154),
'10': (0.9441713925645873, 1, 1.2064272211720228)},
"E": {'2': (1.0, 1.0, 1.0),
'10': (1.0, 1.0, 1.0)}}
def get_xyz_coords(illuminant, observer, dtype=float):
"""Get the XYZ coordinates of the given illuminant and observer [1]_.
Parameters
----------
illuminant : {"A", "D50", "D55", "D65", "D75", "E"}, optional
The name of the illuminant (the function is NOT case sensitive).
observer : {"2", "10"}, optional
The aperture angle of the observer.
dtype: dtype, optional
Output data type.
Returns
-------
out : array
Array with 3 elements containing the XYZ coordinates of the given
illuminant.
Raises
------
ValueError
If either the illuminant or the observer angle are not supported or
unknown.
References
----------
.. [1] https://en.wikipedia.org/wiki/Standard_illuminant
"""
illuminant = illuminant.upper()
try:
return np.asarray(illuminants[illuminant][observer], dtype=dtype)
except KeyError:
raise ValueError("Unknown illuminant/observer combination\
(\'{0}\', \'{1}\')".format(illuminant, observer))
# Haematoxylin-Eosin-DAB colorspace
# From original Ruifrok's paper: A. C. Ruifrok and D. A. Johnston,
# "Quantification of histochemical staining by color deconvolution.,"
# Analytical and quantitative cytology and histology / the International
# Academy of Cytology [and] American Society of Cytology, vol. 23, no. 4,
# pp. 291-9, Aug. 2001.
rgb_from_hed = np.array([[0.65, 0.70, 0.29],
[0.07, 0.99, 0.11],
[0.27, 0.57, 0.78]])
hed_from_rgb = linalg.inv(rgb_from_hed)
# Following matrices are adapted form the Java code written by G.Landini.
# The original code is available at:
# https://web.archive.org/web/20160624145052/http://www.mecourse.com/landinig/software/cdeconv/cdeconv.html
# Hematoxylin + DAB
rgb_from_hdx = np.array([[0.650, 0.704, 0.286],
[0.268, 0.570, 0.776],
[0.0, 0.0, 0.0]])
rgb_from_hdx[2, :] = np.cross(rgb_from_hdx[0, :], rgb_from_hdx[1, :])
hdx_from_rgb = linalg.inv(rgb_from_hdx)
# Feulgen + Light Green
rgb_from_fgx = np.array([[0.46420921, 0.83008335, 0.30827187],
[0.94705542, 0.25373821, 0.19650764],
[0.0, 0.0, 0.0]])
rgb_from_fgx[2, :] = np.cross(rgb_from_fgx[0, :], rgb_from_fgx[1, :])
fgx_from_rgb = linalg.inv(rgb_from_fgx)
# Giemsa: Methyl Blue + Eosin
rgb_from_bex = np.array([[0.834750233, 0.513556283, 0.196330403],
[0.092789, 0.954111, 0.283111],
[0.0, 0.0, 0.0]])
rgb_from_bex[2, :] = np.cross(rgb_from_bex[0, :], rgb_from_bex[1, :])
bex_from_rgb = linalg.inv(rgb_from_bex)
# FastRed + FastBlue + DAB
rgb_from_rbd = np.array([[0.21393921, 0.85112669, 0.47794022],
[0.74890292, 0.60624161, 0.26731082],
[0.268, 0.570, 0.776]])
rbd_from_rgb = linalg.inv(rgb_from_rbd)
# Methyl Green + DAB
rgb_from_gdx = np.array([[0.98003, 0.144316, 0.133146],
[0.268, 0.570, 0.776],
[0.0, 0.0, 0.0]])
rgb_from_gdx[2, :] = np.cross(rgb_from_gdx[0, :], rgb_from_gdx[1, :])
gdx_from_rgb = linalg.inv(rgb_from_gdx)
# Hematoxylin + AEC
rgb_from_hax = np.array([[0.650, 0.704, 0.286],
[0.2743, 0.6796, 0.6803],
[0.0, 0.0, 0.0]])
rgb_from_hax[2, :] = np.cross(rgb_from_hax[0, :], rgb_from_hax[1, :])
hax_from_rgb = linalg.inv(rgb_from_hax)
# Blue matrix Anilline Blue + Red matrix Azocarmine + Orange matrix Orange-G
rgb_from_bro = np.array([[0.853033, 0.508733, 0.112656],
[0.09289875, 0.8662008, 0.49098468],
[0.10732849, 0.36765403, 0.9237484]])
bro_from_rgb = linalg.inv(rgb_from_bro)
# Methyl Blue + Ponceau Fuchsin
rgb_from_bpx = np.array([[0.7995107, 0.5913521, 0.10528667],
[0.09997159, 0.73738605, 0.6680326],
[0.0, 0.0, 0.0]])
rgb_from_bpx[2, :] = np.cross(rgb_from_bpx[0, :], rgb_from_bpx[1, :])
bpx_from_rgb = linalg.inv(rgb_from_bpx)
# Alcian Blue + Hematoxylin
rgb_from_ahx = np.array([[0.874622, 0.457711, 0.158256],
[0.552556, 0.7544, 0.353744],
[0.0, 0.0, 0.0]])
rgb_from_ahx[2, :] = np.cross(rgb_from_ahx[0, :], rgb_from_ahx[1, :])
ahx_from_rgb = linalg.inv(rgb_from_ahx)
# Hematoxylin + PAS
rgb_from_hpx = np.array([[0.644211, 0.716556, 0.266844],
[0.175411, 0.972178, 0.154589],
[0.0, 0.0, 0.0]])
rgb_from_hpx[2, :] = np.cross(rgb_from_hpx[0, :], rgb_from_hpx[1, :])
hpx_from_rgb = linalg.inv(rgb_from_hpx)
# -------------------------------------------------------------
# The conversion functions that make use of the matrices above
# -------------------------------------------------------------
def _convert(matrix, arr):
"""Do the color space conversion.
Parameters
----------
matrix : array_like
The 3x3 matrix to use.
arr : (..., 3) array_like
The input array. Final dimension denotes channels.
Returns
-------
out : (..., 3) ndarray
The converted array. Same dimensions as input.
"""
arr = _prepare_colorarray(arr)
return arr @ matrix.T.astype(arr.dtype)
def xyz2rgb(xyz):
"""XYZ to RGB color space conversion.
Parameters
----------
xyz : (..., 3) array_like
The image in XYZ format. Final dimension denotes channels.
Returns
-------
out : (..., 3) ndarray
The image in RGB format. Same dimensions as input.
Raises
------
ValueError
If `xyz` is not at least 2-D with shape (..., 3).
Notes
-----
The CIE XYZ color space is derived from the CIE RGB color space. Note
however that this function converts to sRGB.
References
----------
.. [1] https://en.wikipedia.org/wiki/CIE_1931_color_space
Examples
--------
>>> from skimage import data
>>> from skimage.color import rgb2xyz, xyz2rgb
>>> img = data.astronaut()
>>> img_xyz = rgb2xyz(img)
>>> img_rgb = xyz2rgb(img_xyz)
"""
# Follow the algorithm from http://www.easyrgb.com/index.php
# except we don't multiply/divide by 100 in the conversion
arr = _convert(rgb_from_xyz, xyz)
mask = arr > 0.0031308
arr[mask] = 1.055 * np.power(arr[mask], 1 / 2.4) - 0.055
arr[~mask] *= 12.92
np.clip(arr, 0, 1, out=arr)
return arr
def rgb2xyz(rgb):
"""RGB to XYZ color space conversion.
Parameters
----------
rgb : (..., 3) array_like
The image in RGB format. Final dimension denotes channels.
Returns
-------
out : (..., 3) ndarray
The image in XYZ format. Same dimensions as input.
Raises
------
ValueError
If `rgb` is not at least 2-D with shape (..., 3).
Notes
-----
The CIE XYZ color space is derived from the CIE RGB color space. Note
however that this function converts from sRGB.
References
----------
.. [1] https://en.wikipedia.org/wiki/CIE_1931_color_space
Examples
--------
>>> from skimage import data
>>> img = data.astronaut()
>>> img_xyz = rgb2xyz(img)
"""
# Follow the algorithm from http://www.easyrgb.com/index.php
# except we don't multiply/divide by 100 in the conversion
arr = _prepare_colorarray(rgb).copy()
mask = arr > 0.04045
arr[mask] = np.power((arr[mask] + 0.055) / 1.055, 2.4)
arr[~mask] /= 12.92
return arr @ xyz_from_rgb.T.astype(arr.dtype)
def rgb2rgbcie(rgb):
"""RGB to RGB CIE color space conversion.
Parameters
----------
rgb : (..., 3) array_like
The image in RGB format. Final dimension denotes channels.
Returns
-------
out : (..., 3) ndarray
The image in RGB CIE format. Same dimensions as input.
Raises
------
ValueError
If `rgb` is not at least 2-D with shape (..., 3).
References
----------
.. [1] https://en.wikipedia.org/wiki/CIE_1931_color_space
Examples
--------
>>> from skimage import data
>>> from skimage.color import rgb2rgbcie
>>> img = data.astronaut()
>>> img_rgbcie = rgb2rgbcie(img)
"""
return _convert(rgbcie_from_rgb, rgb)
def rgbcie2rgb(rgbcie):
"""RGB CIE to RGB color space conversion.
Parameters
----------
rgbcie : (..., 3) array_like
The image in RGB CIE format. Final dimension denotes channels.
Returns
-------
out : (..., 3) ndarray
The image in RGB format. Same dimensions as input.
Raises
------
ValueError
If `rgbcie` is not at least 2-D with shape (..., 3).
References
----------
.. [1] https://en.wikipedia.org/wiki/CIE_1931_color_space
Examples
--------
>>> from skimage import data
>>> from skimage.color import rgb2rgbcie, rgbcie2rgb
>>> img = data.astronaut()
>>> img_rgbcie = rgb2rgbcie(img)
>>> img_rgb = rgbcie2rgb(img_rgbcie)
"""
return _convert(rgb_from_rgbcie, rgbcie)
def rgb2gray(rgb):
"""Compute luminance of an RGB image.
Parameters
----------
rgb : (..., 3) array_like
The image in RGB format. Final dimension denotes channels.
Returns
-------
out : ndarray
The luminance image - an array which is the same size as the input
array, but with the channel dimension removed.
Raises
------
ValueError
If `rgb` is not at least 2-D with shape (..., 3).
Notes
-----
The weights used in this conversion are calibrated for contemporary
CRT phosphors::
Y = 0.2125 R + 0.7154 G + 0.0721 B
If there is an alpha channel present, it is ignored.
References
----------
.. [1] http://poynton.ca/PDFs/ColorFAQ.pdf
Examples
--------
>>> from skimage.color import rgb2gray
>>> from skimage import data
>>> img = data.astronaut()
>>> img_gray = rgb2gray(img)
"""
if rgb.ndim == 2:
warn('The behavior of rgb2gray will change in scikit-image 0.19. '
'Currently, rgb2gray allows 2D grayscale image to be passed '
'as inputs and leaves them unmodified as outputs. '
'Starting from version 0.19, 2D arrays will '
'be treated as 1D images with 3 channels.',
FutureWarning, stacklevel=2)
return np.ascontiguousarray(rgb)
if rgb.shape[-1] > 3:
warn('Non RGB image conversion is now deprecated. For RGBA images, '
'please use rgb2gray(rgba2rgb(rgb)) instead. In version 0.19, '
'a ValueError will be raised if input image last dimension '
'length is not 3.', FutureWarning, stacklevel=2)
rgb = rgb[..., :3]
rgb = _prepare_colorarray(rgb)
coeffs = np.array([0.2125, 0.7154, 0.0721], dtype=rgb.dtype)
return rgb @ coeffs
@functools.wraps(rgb2gray)
def rgb2grey(rgb):
warn('rgb2grey is deprecated. It will be removed in version 0.19.'
'Please use rgb2gray instead.', FutureWarning, stacklevel=2)
return rgb2gray(rgb)
def gray2rgba(image, alpha=None):
"""Create a RGBA representation of a gray-level image.
Parameters
----------
image : array_like
Input image.
alpha : array_like, optional
Alpha channel of the output image. It may be a scalar or an
array that can be broadcast to ``image``. If not specified it is
set to the maximum limit corresponding to the ``image`` dtype.
Returns
-------
rgba : ndarray
RGBA image. A new dimension of length 4 is added to input
image shape.
"""
arr = np.asarray(image)
alpha_min, alpha_max = dtype_limits(arr, clip_negative=False)
if alpha is None:
alpha = alpha_max
if not np.can_cast(alpha, arr.dtype):
warn("alpha can't be safely cast to image dtype {}"
.format(arr.dtype.name), stacklevel=2)
rgba = np.empty(arr.shape + (4, ), dtype=arr.dtype)
rgba[..., :3] = arr[..., np.newaxis]
rgba[..., 3] = alpha
return rgba
def gray2rgb(image, alpha=None):
"""Create an RGB representation of a gray-level image.
Parameters
----------
image : array_like
Input image.
alpha : bool, optional
Ensure that the output image has an alpha layer. If None,
alpha layers are passed through but not created.
Returns
-------
rgb : (..., 3) ndarray
RGB image. A new dimension of length 3 is added to input image.
Notes
-----
If the input is a 1-dimensional image of shape ``(M, )``, the output
will be shape ``(M, 3)``.
"""
if alpha is not None:
warn("alpha argument is deprecated and will be removed in "
"version 0.19. Please use the gray2rgba function instead"
"to obtain an RGBA image.", FutureWarning, stacklevel=2)
is_rgb = False
is_alpha = False
dims = np.squeeze(image).ndim
if dims == 3:
if image.shape[2] == 3:
is_rgb = True
elif image.shape[2] == 4:
is_alpha = True
is_rgb = True
if is_rgb:
warn('Pass-through of possibly RGB images in gray2rgb is deprecated. '
'In version 0.19, input arrays will always be considered '
'grayscale, even if the last dimension has length 3 or 4. '
'To prevent this warning and ensure compatibility with future '
'versions, detect RGB images outside of this function.',
FutureWarning, stacklevel=2)
if alpha is False:
image = image[..., :3]
elif alpha is True and is_alpha is False:
alpha_layer = (np.ones_like(image[..., 0, np.newaxis]) *
dtype_limits(image, clip_negative=False)[1])
image = np.concatenate((image, alpha_layer), axis=2)
return image
else:
image = image[..., np.newaxis]
if alpha:
alpha_layer = (np.ones_like(image)
* dtype_limits(image, clip_negative=False)[1])
return np.concatenate(3 * (image,) + (alpha_layer,), axis=-1)
else:
return np.concatenate(3 * (image,), axis=-1)
@functools.wraps(gray2rgb)
def grey2rgb(image):
warn('grey2rgb is deprecated. It will be removed in version 0.19.'
'Please use gray2rgb instead.', FutureWarning, stacklevel=2)
return gray2rgb(image)
def xyz2lab(xyz, illuminant="D65", observer="2"):
"""XYZ to CIE-LAB color space conversion.
Parameters
----------
xyz : (..., 3) array_like
The image in XYZ format. Final dimension denotes channels.
illuminant : {"A", "D50", "D55", "D65", "D75", "E"}, optional
The name of the illuminant (the function is NOT case sensitive).
observer : {"2", "10"}, optional
The aperture angle of the observer.
Returns
-------
out : (..., 3) ndarray
The image in CIE-LAB format. Same dimensions as input.
Raises
------
ValueError
If `xyz` is not at least 2-D with shape (..., 3).
ValueError
If either the illuminant or the observer angle is unsupported or
unknown.
Notes
-----
By default Observer= 2A, Illuminant= D65. CIE XYZ tristimulus values
x_ref=95.047, y_ref=100., z_ref=108.883. See function `get_xyz_coords` for
a list of supported illuminants.
References
----------
.. [1] http://www.easyrgb.com/index.php?X=MATH&H=07
.. [2] https://en.wikipedia.org/wiki/Lab_color_space
Examples
--------
>>> from skimage import data
>>> from skimage.color import rgb2xyz, xyz2lab
>>> img = data.astronaut()
>>> img_xyz = rgb2xyz(img)
>>> img_lab = xyz2lab(img_xyz)
"""
arr = _prepare_colorarray(xyz)
xyz_ref_white = get_xyz_coords(illuminant, observer, arr.dtype)
# scale by CIE XYZ tristimulus values of the reference white point
arr = arr / xyz_ref_white
# Nonlinear distortion and linear transformation
mask = arr > 0.008856
arr[mask] = np.cbrt(arr[mask])
arr[~mask] = 7.787 * arr[~mask] + 16. / 116.
x, y, z = arr[..., 0], arr[..., 1], arr[..., 2]
# Vector scaling
L = (116. * y) - 16.
a = 500.0 * (x - y)
b = 200.0 * (y - z)
return np.concatenate([x[..., np.newaxis] for x in [L, a, b]], axis=-1)
def lab2xyz(lab, illuminant="D65", observer="2"):
"""CIE-LAB to XYZcolor space conversion.
Parameters
----------
lab : (..., 3) array_like
The image in Lab format. Final dimension denotes channels.
illuminant : {"A", "D50", "D55", "D65", "D75", "E"}, optional
The name of the illuminant (the function is NOT case sensitive).
observer : {"2", "10"}, optional
The aperture angle of the observer.
Returns
-------
out : (..., 3) ndarray
The image in XYZ format. Same dimensions as input.
Raises
------
ValueError
If `lab` is not at least 2-D with shape (..., 3).
ValueError
If either the illuminant or the observer angle are not supported or
unknown.
UserWarning
If any of the pixels are invalid (Z < 0).
Notes
-----
By default Observer= 2A, Illuminant= D65. CIE XYZ tristimulus values x_ref
= 95.047, y_ref = 100., z_ref = 108.883. See function 'get_xyz_coords' for
a list of supported illuminants.
References
----------
.. [1] http://www.easyrgb.com/index.php?X=MATH&H=07
.. [2] https://en.wikipedia.org/wiki/Lab_color_space
"""
arr = _prepare_colorarray(lab).copy()
L, a, b = arr[..., 0], arr[..., 1], arr[..., 2]
y = (L + 16.) / 116.
x = (a / 500.) + y
z = y - (b / 200.)
if np.any(z < 0):
invalid = np.nonzero(z < 0)
warn('Color data out of range: Z < 0 in %s pixels' % invalid[0].size,
stacklevel=2)
z[invalid] = 0
out = np.stack([x, y, z], axis=-1)
mask = out > 0.2068966
out[mask] = np.power(out[mask], 3.)
out[~mask] = (out[~mask] - 16.0 / 116.) / 7.787
# rescale to the reference white (illuminant)
xyz_ref_white = get_xyz_coords(illuminant, observer)
out *= xyz_ref_white
return out
def rgb2lab(rgb, illuminant="D65", observer="2"):
"""Conversion from the sRGB color space (IEC 61966-2-1:1999)
to the CIE Lab colorspace under the given illuminant and observer.
Parameters
----------
rgb : (..., 3) array_like
The image in RGB format. Final dimension denotes channels.
illuminant : {"A", "D50", "D55", "D65", "D75", "E"}, optional
The name of the illuminant (the function is NOT case sensitive).
observer : {"2", "10"}, optional
The aperture angle of the observer.
Returns
-------
out : (..., 3) ndarray
The image in Lab format. Same dimensions as input.
Raises
------
ValueError
If `rgb` is not at least 2-D with shape (..., 3).
Notes
-----
RGB is a device-dependent color space so, if you use this function, be
sure that the image you are analyzing has been mapped to the sRGB color
space.
This function uses rgb2xyz and xyz2lab.
By default Observer= 2A, Illuminant= D65. CIE XYZ tristimulus values
x_ref=95.047, y_ref=100., z_ref=108.883. See function `get_xyz_coords` for
a list of supported illuminants.
References
----------
.. [1] https://en.wikipedia.org/wiki/Standard_illuminant
"""
return xyz2lab(rgb2xyz(rgb), illuminant, observer)
def lab2rgb(lab, illuminant="D65", observer="2"):
"""Lab to RGB color space conversion.
Parameters
----------
lab : (..., 3) array_like
The image in Lab format. Final dimension denotes channels.
illuminant : {"A", "D50", "D55", "D65", "D75", "E"}, optional
The name of the illuminant (the function is NOT case sensitive).
observer : {"2", "10"}, optional
The aperture angle of the observer.
Returns
-------
out : (..., 3) ndarray
The image in RGB format. Same dimensions as input.
Raises
------
ValueError
If `lab` is not at least 2-D with shape (..., 3).
Notes
-----
This function uses lab2xyz and xyz2rgb.
By default Observer= 2A, Illuminant= D65. CIE XYZ tristimulus values
x_ref=95.047, y_ref=100., z_ref=108.883. See function `get_xyz_coords` for
a list of supported illuminants.
References
----------
.. [1] https://en.wikipedia.org/wiki/Standard_illuminant
"""
return xyz2rgb(lab2xyz(lab, illuminant, observer))
def xyz2luv(xyz, illuminant="D65", observer="2"):
"""XYZ to CIE-Luv color space conversion.
Parameters
----------
xyz : (..., 3) array_like
The image in XYZ format. Final dimension denotes channels.
illuminant : {"A", "D50", "D55", "D65", "D75", "E"}, optional
The name of the illuminant (the function is NOT case sensitive).
observer : {"2", "10"}, optional
The aperture angle of the observer.
Returns
-------
out : (..., 3) ndarray
The image in CIE-Luv format. Same dimensions as input.
Raises
------
ValueError
If `xyz` is not at least 2-D with shape (..., 3).
ValueError
If either the illuminant or the observer angle are not supported or
unknown.
Notes
-----
By default XYZ conversion weights use observer=2A. Reference whitepoint
for D65 Illuminant, with XYZ tristimulus values of ``(95.047, 100.,
108.883)``. See function 'get_xyz_coords' for a list of supported
illuminants.
References
----------
.. [1] http://www.easyrgb.com/index.php?X=MATH&H=16#text16
.. [2] https://en.wikipedia.org/wiki/CIELUV
Examples
--------
>>> from skimage import data
>>> from skimage.color import rgb2xyz, xyz2luv
>>> img = data.astronaut()
>>> img_xyz = rgb2xyz(img)
>>> img_luv = xyz2luv(img_xyz)
"""
input_is_one_pixel = xyz.ndim == 1
if input_is_one_pixel:
xyz = xyz[np.newaxis, ...]
arr = _prepare_colorarray(xyz)
# extract channels
x, y, z = arr[..., 0], arr[..., 1], arr[..., 2]
eps = np.finfo(float).eps
# compute y_r and L
xyz_ref_white = np.array(get_xyz_coords(illuminant, observer))
L = y / xyz_ref_white[1]
mask = L > 0.008856
L[mask] = 116. * np.cbrt(L[mask]) - 16.
L[~mask] = 903.3 * L[~mask]
u0 = 4 * xyz_ref_white[0] / ([1, 15, 3] @ xyz_ref_white)
v0 = 9 * xyz_ref_white[1] / ([1, 15, 3] @ xyz_ref_white)
# u' and v' helper functions
def fu(X, Y, Z):
return (4. * X) / (X + 15. * Y + 3. * Z + eps)
def fv(X, Y, Z):
return (9. * Y) / (X + 15. * Y + 3. * Z + eps)
# compute u and v using helper functions
u = 13. * L * (fu(x, y, z) - u0)
v = 13. * L * (fv(x, y, z) - v0)
out = np.stack([L, u, v], axis=-1)
if input_is_one_pixel:
out = np.squeeze(out, axis=0)
return out
def luv2xyz(luv, illuminant="D65", observer="2"):
"""CIE-Luv to XYZ color space conversion.
Parameters
----------
luv : (..., 3) array_like
The image in CIE-Luv format. Final dimension denotes channels.
illuminant : {"A", "D50", "D55", "D65", "D75", "E"}, optional
The name of the illuminant (the function is NOT case sensitive).
observer : {"2", "10"}, optional
The aperture angle of the observer.
Returns
-------
out : (..., 3) ndarray
The image in XYZ format. Same dimensions as input.
Raises
------
ValueError
If `luv` is not at least 2-D with shape (..., 3).
ValueError
If either the illuminant or the observer angle are not supported or
unknown.
Notes
-----
XYZ conversion weights use observer=2A. Reference whitepoint for D65
Illuminant, with XYZ tristimulus values of ``(95.047, 100., 108.883)``. See
function 'get_xyz_coords' for a list of supported illuminants.
References
----------
.. [1] http://www.easyrgb.com/index.php?X=MATH&H=16#text16
.. [2] https://en.wikipedia.org/wiki/CIELUV
"""
arr = _prepare_colorarray(luv).copy()
L, u, v = arr[..., 0], arr[..., 1], arr[..., 2]
eps = np.finfo(float).eps
# compute y
y = L.copy()
mask = y > 7.999625
y[mask] = np.power((y[mask] + 16.) / 116., 3.)
y[~mask] = y[~mask] / 903.3
xyz_ref_white = get_xyz_coords(illuminant, observer)
y *= xyz_ref_white[1]
# reference white x,z
uv_weights = np.array([1, 15, 3])
u0 = 4 * xyz_ref_white[0] / (uv_weights @ xyz_ref_white)
v0 = 9 * xyz_ref_white[1] / (uv_weights @ xyz_ref_white)
# compute intermediate values
a = u0 + u / (13. * L + eps)
b = v0 + v / (13. * L + eps)
c = 3 * y * (5 * b - 3)
# compute x and z
z = ((a - 4) * c - 15 * a * b * y) / (12 * b)
x = -(c / b + 3. * z)
return np.concatenate([q[..., np.newaxis] for q in [x, y, z]], axis=-1)
def rgb2luv(rgb):
"""RGB to CIE-Luv color space conversion.
Parameters
----------
rgb : (..., 3) array_like
The image in RGB format. Final dimension denotes channels.
Returns
-------
out : (..., 3) ndarray
The image in CIE Luv format. Same dimensions as input.
Raises
------
ValueError
If `rgb` is not at least 2-D with shape (..., 3).
Notes
-----
This function uses rgb2xyz and xyz2luv.
References
----------
.. [1] http://www.easyrgb.com/index.php?X=MATH&H=16#text16
.. [2] http://www.easyrgb.com/index.php?X=MATH&H=02#text2
.. [3] https://en.wikipedia.org/wiki/CIELUV
"""
return xyz2luv(rgb2xyz(rgb))
def luv2rgb(luv):
"""Luv to RGB color space conversion.
Parameters
----------
luv : (..., 3) array_like
The image in CIE Luv format. Final dimension denotes channels.
Returns
-------
out : (..., 3) ndarray
The image in RGB format. Same dimensions as input.
Raises
------
ValueError
If `luv` is not at least 2-D with shape (..., 3).
Notes
-----
This function uses luv2xyz and xyz2rgb.
"""
return xyz2rgb(luv2xyz(luv))
def rgb2hed(rgb):
"""RGB to Haematoxylin-Eosin-DAB (HED) color space conversion.
Parameters
----------
rgb : (..., 3) array_like
The image in RGB format. Final dimension denotes channels.
Returns
-------
out : (..., 3) ndarray
The image in HED format. Same dimensions as input.
Raises
------
ValueError
If `rgb` is not at least 2-D with shape (..., 3).
References
----------
.. [1] A. C. Ruifrok and D. A. Johnston, "Quantification of histochemical
staining by color deconvolution.," Analytical and quantitative
cytology and histology / the International Academy of Cytology [and]
American Society of Cytology, vol. 23, no. 4, pp. 291-9, Aug. 2001.
Examples
--------
>>> from skimage import data
>>> from skimage.color import rgb2hed
>>> ihc = data.immunohistochemistry()
>>> ihc_hed = rgb2hed(ihc)
"""
return separate_stains(rgb, hed_from_rgb)
def hed2rgb(hed):
"""Haematoxylin-Eosin-DAB (HED) to RGB color space conversion.
Parameters
----------
hed : (..., 3) array_like
The image in the HED color space. Final dimension denotes channels.
Returns
-------
out : (..., 3) ndarray
The image in RGB. Same dimensions as input.
Raises
------
ValueError
If `hed` is not at least 2-D with shape (..., 3).
References
----------
.. [1] A. C. Ruifrok and D. A. Johnston, "Quantification of histochemical
staining by color deconvolution.," Analytical and quantitative
cytology and histology / the International Academy of Cytology [and]
American Society of Cytology, vol. 23, no. 4, pp. 291-9, Aug. 2001.
Examples
--------
>>> from skimage import data
>>> from skimage.color import rgb2hed, hed2rgb
>>> ihc = data.immunohistochemistry()
>>> ihc_hed = rgb2hed(ihc)
>>> ihc_rgb = hed2rgb(ihc_hed)
"""
return combine_stains(hed, rgb_from_hed)
def separate_stains(rgb, conv_matrix):
"""RGB to stain color space conversion.
Parameters
----------
rgb : (..., 3) array_like
The image in RGB format. Final dimension denotes channels.
conv_matrix: ndarray
The stain separation matrix as described by G. Landini [1]_.
Returns
-------
out : (..., 3) ndarray
The image in stain color space. Same dimensions as input.
Raises
------
ValueError
If `rgb` is not at least 2-D with shape (..., 3).
Notes
-----
Stain separation matrices available in the ``color`` module and their
respective colorspace:
* ``hed_from_rgb``: Hematoxylin + Eosin + DAB
* ``hdx_from_rgb``: Hematoxylin + DAB
* ``fgx_from_rgb``: Feulgen + Light Green
* ``bex_from_rgb``: Giemsa stain : Methyl Blue + Eosin
* ``rbd_from_rgb``: FastRed + FastBlue + DAB
* ``gdx_from_rgb``: Methyl Green + DAB
* ``hax_from_rgb``: Hematoxylin + AEC
* ``bro_from_rgb``: Blue matrix Anilline Blue + Red matrix Azocarmine\
+ Orange matrix Orange-G
* ``bpx_from_rgb``: Methyl Blue + Ponceau Fuchsin
* ``ahx_from_rgb``: Alcian Blue + Hematoxylin
* ``hpx_from_rgb``: Hematoxylin + PAS
This implementation borrows some ideas from DIPlib [2]_, e.g. the
compensation using a small value to avoid log artifacts when
calculating the Beer-Lambert law.
References
----------
.. [1] https://web.archive.org/web/20160624145052/http://www.mecourse.com/landinig/software/cdeconv/cdeconv.html
.. [2] https://github.com/DIPlib/diplib/
.. [3] A. C. Ruifrok and D. A. Johnston, “Quantification of histochemical
staining by color deconvolution,” Anal. Quant. Cytol. Histol., vol.
23, no. 4, pp. 291299, Aug. 2001.
Examples
--------
>>> from skimage import data
>>> from skimage.color import separate_stains, hdx_from_rgb
>>> ihc = data.immunohistochemistry()
>>> ihc_hdx = separate_stains(ihc, hdx_from_rgb)
"""
rgb = _prepare_colorarray(rgb, force_copy=True)
np.maximum(rgb, 1E-6, out=rgb) # avoiding log artifacts
log_adjust = np.log(1E-6) # used to compensate the sum above
stains = (np.log(rgb) / log_adjust) @ conv_matrix
np.maximum(stains, 0, out=stains)
return stains
def combine_stains(stains, conv_matrix):
"""Stain to RGB color space conversion.
Parameters
----------
stains : (..., 3) array_like
The image in stain color space. Final dimension denotes channels.
conv_matrix: ndarray
The stain separation matrix as described by G. Landini [1]_.
Returns
-------
out : (..., 3) ndarray
The image in RGB format. Same dimensions as input.
Raises
------
ValueError
If `stains` is not at least 2-D with shape (..., 3).
Notes
-----
Stain combination matrices available in the ``color`` module and their
respective colorspace:
* ``rgb_from_hed``: Hematoxylin + Eosin + DAB
* ``rgb_from_hdx``: Hematoxylin + DAB
* ``rgb_from_fgx``: Feulgen + Light Green
* ``rgb_from_bex``: Giemsa stain : Methyl Blue + Eosin
* ``rgb_from_rbd``: FastRed + FastBlue + DAB
* ``rgb_from_gdx``: Methyl Green + DAB
* ``rgb_from_hax``: Hematoxylin + AEC
* ``rgb_from_bro``: Blue matrix Anilline Blue + Red matrix Azocarmine\
+ Orange matrix Orange-G
* ``rgb_from_bpx``: Methyl Blue + Ponceau Fuchsin
* ``rgb_from_ahx``: Alcian Blue + Hematoxylin
* ``rgb_from_hpx``: Hematoxylin + PAS
References
----------
.. [1] https://web.archive.org/web/20160624145052/http://www.mecourse.com/landinig/software/cdeconv/cdeconv.html
.. [2] A. C. Ruifrok and D. A. Johnston, “Quantification of histochemical
staining by color deconvolution,” Anal. Quant. Cytol. Histol., vol.
23, no. 4, pp. 291299, Aug. 2001.
Examples
--------
>>> from skimage import data
>>> from skimage.color import (separate_stains, combine_stains,
... hdx_from_rgb, rgb_from_hdx)
>>> ihc = data.immunohistochemistry()
>>> ihc_hdx = separate_stains(ihc, hdx_from_rgb)
>>> ihc_rgb = combine_stains(ihc_hdx, rgb_from_hdx)
"""
stains = _prepare_colorarray(stains)
# log_adjust here is used to compensate the sum within separate_stains().
log_adjust = -np.log(1E-6)
log_rgb = -(stains * log_adjust) @ conv_matrix
rgb = np.exp(log_rgb)
return np.clip(rgb, a_min=0, a_max=1)
def lab2lch(lab):
"""CIE-LAB to CIE-LCH color space conversion.
LCH is the cylindrical representation of the LAB (Cartesian) colorspace
Parameters
----------
lab : (..., 3) array_like
The N-D image in CIE-LAB format. The last (``N+1``-th) dimension must
have at least 3 elements, corresponding to the ``L``, ``a``, and ``b``
color channels. Subsequent elements are copied.
Returns
-------
out : (..., 3) ndarray
The image in LCH format, in a N-D array with same shape as input `lab`.
Raises
------
ValueError
If `lch` does not have at least 3 color channels (i.e. l, a, b).
Notes
-----
The Hue is expressed as an angle between ``(0, 2*pi)``
Examples
--------
>>> from skimage import data
>>> from skimage.color import rgb2lab, lab2lch
>>> img = data.astronaut()
>>> img_lab = rgb2lab(img)
>>> img_lch = lab2lch(img_lab)
"""
lch = _prepare_lab_array(lab)
a, b = lch[..., 1], lch[..., 2]
lch[..., 1], lch[..., 2] = _cart2polar_2pi(a, b)
return lch
def _cart2polar_2pi(x, y):
"""convert cartesian coordinates to polar (uses non-standard theta range!)
NON-STANDARD RANGE! Maps to ``(0, 2*pi)`` rather than usual ``(-pi, +pi)``
"""
r, t = np.hypot(x, y), np.arctan2(y, x)
t += np.where(t < 0., 2 * np.pi, 0)
return r, t
def lch2lab(lch):
"""CIE-LCH to CIE-LAB color space conversion.
LCH is the cylindrical representation of the LAB (Cartesian) colorspace
Parameters
----------
lch : (..., 3) array_like
The N-D image in CIE-LCH format. The last (``N+1``-th) dimension must
have at least 3 elements, corresponding to the ``L``, ``a``, and ``b``
color channels. Subsequent elements are copied.
Returns
-------
out : (..., 3) ndarray
The image in LAB format, with same shape as input `lch`.
Raises
------
ValueError
If `lch` does not have at least 3 color channels (i.e. l, c, h).
Examples
--------
>>> from skimage import data
>>> from skimage.color import rgb2lab, lch2lab
>>> img = data.astronaut()
>>> img_lab = rgb2lab(img)
>>> img_lch = lab2lch(img_lab)
>>> img_lab2 = lch2lab(img_lch)
"""
lch = _prepare_lab_array(lch)
c, h = lch[..., 1], lch[..., 2]
lch[..., 1], lch[..., 2] = c * np.cos(h), c * np.sin(h)
return lch
def _prepare_lab_array(arr, force_copy=True):
"""Ensure input for lab2lch, lch2lab are well-posed.
Arrays must be in floating point and have at least 3 elements in
last dimension. Return a new array.
"""
arr = np.asarray(arr)
shape = arr.shape
if shape[-1] < 3:
raise ValueError('Input array has less than 3 color channels')
return dtype.img_as_float(arr, force_copy=force_copy)
def rgb2yuv(rgb):
"""RGB to YUV color space conversion.
Parameters
----------
rgb : (..., 3) array_like
The image in RGB format. Final dimension denotes channels.
Returns
-------
out : (..., 3) ndarray
The image in YUV format. Same dimensions as input.
Raises
------
ValueError
If `rgb` is not at least 2-D with shape (..., 3).
Notes
-----
Y is between 0 and 1. Use YCbCr instead of YUV for the color space
commonly used by video codecs, where Y ranges from 16 to 235.
References
----------
.. [1] https://en.wikipedia.org/wiki/YUV
"""
return _convert(yuv_from_rgb, rgb)
def rgb2yiq(rgb):
"""RGB to YIQ color space conversion.
Parameters
----------
rgb : (..., 3) array_like
The image in RGB format. Final dimension denotes channels.
Returns
-------
out : (..., 3) ndarray
The image in YIQ format. Same dimensions as input.
Raises
------
ValueError
If `rgb` is not at least 2-D with shape (..., 3).
"""
return _convert(yiq_from_rgb, rgb)
def rgb2ypbpr(rgb):
"""RGB to YPbPr color space conversion.
Parameters
----------
rgb : (..., 3) array_like
The image in RGB format. Final dimension denotes channels.
Returns
-------
out : (..., 3) ndarray
The image in YPbPr format. Same dimensions as input.
Raises
------
ValueError
If `rgb` is not at least 2-D with shape (..., 3).
References
----------
.. [1] https://en.wikipedia.org/wiki/YPbPr
"""
return _convert(ypbpr_from_rgb, rgb)
def rgb2ycbcr(rgb):
"""RGB to YCbCr color space conversion.
Parameters
----------
rgb : (..., 3) array_like
The image in RGB format. Final dimension denotes channels.
Returns
-------
out : (..., 3) ndarray
The image in YCbCr format. Same dimensions as input.
Raises
------
ValueError
If `rgb` is not at least 2-D with shape (..., 3).
Notes
-----
Y is between 16 and 235. This is the color space commonly used by video
codecs; it is sometimes incorrectly called "YUV".
References
----------
.. [1] https://en.wikipedia.org/wiki/YCbCr
"""
arr = _convert(ycbcr_from_rgb, rgb)
arr[..., 0] += 16
arr[..., 1] += 128
arr[..., 2] += 128
return arr
def rgb2ydbdr(rgb):
"""RGB to YDbDr color space conversion.
Parameters
----------
rgb : (..., 3) array_like
The image in RGB format. Final dimension denotes channels.
Returns
-------
out : (..., 3) ndarray
The image in YDbDr format. Same dimensions as input.
Raises
------
ValueError
If `rgb` is not at least 2-D with shape (..., 3).
Notes
-----
This is the color space commonly used by video codecs. It is also the
reversible color transform in JPEG2000.
References
----------
.. [1] https://en.wikipedia.org/wiki/YDbDr
"""
arr = _convert(ydbdr_from_rgb, rgb)
return arr
def yuv2rgb(yuv):
"""YUV to RGB color space conversion.
Parameters
----------
yuv : (..., 3) array_like
The image in YUV format. Final dimension denotes channels.
Returns
-------
out : (..., 3) ndarray
The image in RGB format. Same dimensions as input.
Raises
------
ValueError
If `yuv` is not at least 2-D with shape (..., 3).
References
----------
.. [1] https://en.wikipedia.org/wiki/YUV
"""
return _convert(rgb_from_yuv, yuv)
def yiq2rgb(yiq):
"""YIQ to RGB color space conversion.
Parameters
----------
yiq : (..., 3) array_like
The image in YIQ format. Final dimension denotes channels.
Returns
-------
out : (..., 3) ndarray
The image in RGB format. Same dimensions as input.
Raises
------
ValueError
If `yiq` is not at least 2-D with shape (..., 3).
"""
return _convert(rgb_from_yiq, yiq)
def ypbpr2rgb(ypbpr):
"""YPbPr to RGB color space conversion.
Parameters
----------
ypbpr : (..., 3) array_like
The image in YPbPr format. Final dimension denotes channels.
Returns
-------
out : (..., 3) ndarray
The image in RGB format. Same dimensions as input.
Raises
------
ValueError
If `ypbpr` is not at least 2-D with shape (..., 3).
References
----------
.. [1] https://en.wikipedia.org/wiki/YPbPr
"""
return _convert(rgb_from_ypbpr, ypbpr)
def ycbcr2rgb(ycbcr):
"""YCbCr to RGB color space conversion.
Parameters
----------
ycbcr : (..., 3) array_like
The image in YCbCr format. Final dimension denotes channels.
Returns
-------
out : (..., 3) ndarray
The image in RGB format. Same dimensions as input.
Raises
------
ValueError
If `ycbcr` is not at least 2-D with shape (..., 3).
Notes
-----
Y is between 16 and 235. This is the color space commonly used by video
codecs; it is sometimes incorrectly called "YUV".
References
----------
.. [1] https://en.wikipedia.org/wiki/YCbCr
"""
arr = ycbcr.copy()
arr[..., 0] -= 16
arr[..., 1] -= 128
arr[..., 2] -= 128
return _convert(rgb_from_ycbcr, arr)
def ydbdr2rgb(ydbdr):
"""YDbDr to RGB color space conversion.
Parameters
----------
ydbdr : (..., 3) array_like
The image in YDbDr format. Final dimension denotes channels.
Returns
-------
out : (..., 3) ndarray
The image in RGB format. Same dimensions as input.
Raises
------
ValueError
If `ydbdr` is not at least 2-D with shape (..., 3).
Notes
-----
This is the color space commonly used by video codecs, also called the
reversible color transform in JPEG2000.
References
----------
.. [1] https://en.wikipedia.org/wiki/YDbDr
"""
return _convert(rgb_from_ydbdr, ydbdr)