diff --git a/CONTRIBUTORS.txt b/CONTRIBUTORS.txt index 8528002b..44214d85 100644 --- a/CONTRIBUTORS.txt +++ b/CONTRIBUTORS.txt @@ -234,3 +234,6 @@ - Kirill Malev Frangi and Hessian filters implementation + +- Abdeali Kothari + Alpha blending to convert from rgba to rgb diff --git a/doc/source/user_guide/transforming_image_data.rst b/doc/source/user_guide/transforming_image_data.rst index 79c6e03a..817e8190 100644 --- a/doc/source/user_guide/transforming_image_data.rst +++ b/doc/source/user_guide/transforming_image_data.rst @@ -42,6 +42,17 @@ transformed to floating-point type by the conversion operation:: +Conversion from RGBA to RGB - Removing alpha channel through alpha blending +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Converting an RGBA image to an RGB image by alpha blending it with a +background is realized with :func:`rgba2rgb` :: + + >>> from skimage.color import rgba2rgb + >>> from skimage import data + >>> img_rgba = data.horse() + >>> img_rgb = rgba2rgb(img_rgba) + Conversion between color and gray values ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/skimage/color/__init__.py b/skimage/color/__init__.py index 80d8a03a..fe4c71bc 100644 --- a/skimage/color/__init__.py +++ b/skimage/color/__init__.py @@ -1,5 +1,6 @@ from .colorconv import (convert_colorspace, guess_spatial_dimensions, + rgba2rgb, rgb2hsv, hsv2rgb, rgb2xyz, diff --git a/skimage/color/colorconv.py b/skimage/color/colorconv.py index e8dd4224..29baa91e 100644 --- a/skimage/color/colorconv.py +++ b/skimage/color/colorconv.py @@ -156,6 +156,70 @@ def _prepare_colorarray(arr): return dtype.img_as_float(arr) +def _prepare_rgba_array(arr): + """Check the shape of the array to be RGBA and convert it to + floating point representation. + + """ + arr = np.asanyarray(arr) + + if arr.ndim not in [3, 4] or arr.shape[-1] != 4: + msg = ("the input array must have a shape == (.., ..,[ ..,] 4)), " + "got {0}".format(arr.shape)) + raise ValueError(msg) + + return dtype.img_as_float(arr) + + +def rgba2rgb(rgba, background=(1, 1, 1)): + """RGBA to RGB conversion. + + Parameters + ---------- + rgba : array_like + The image in RGBA format, in a 3-D array of shape ``(.., .., 4)``. + background : array_like + The color of the background to blend the image with. A tuple + containing 3 floats between 0 to 1 - the RGB value of the background. + + Returns + ------- + out : ndarray + The image in RGB format, in a 3-D array of shape ``(.., .., 3)``. + + Raises + ------ + ValueError + If `rgba` is not a 3-D array of 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.horse() + >>> img_rgb = color.rgba2rgb(img_rgba) + """ + arr = _prepare_rgba_array(rgba) + if isinstance(background, tuple) and len(background) != 3: + raise ValueError('the background must be a tuple with 3 items - the ' + 'RGB color of the background. Got {0} items.' + .format(len(background))) + + alpha = arr[..., -1] + channels = arr[..., :-1] + out = np.empty_like(channels) + + for ichan in range(channels.shape[-1]): + out[..., ichan] = np.clip( + (1 - alpha) * background[ichan] + alpha * channels[..., ichan], + a_min=0, a_max=1) + return out + + def rgb2hsv(rgb): """RGB to HSV color space conversion. diff --git a/skimage/color/tests/test_colorconv.py b/skimage/color/tests/test_colorconv.py index 106eabee..6cb1aa84 100644 --- a/skimage/color/tests/test_colorconv.py +++ b/skimage/color/tests/test_colorconv.py @@ -41,6 +41,7 @@ from skimage.color import (rgb2hsv, hsv2rgb, rgb2yiq, yiq2rgb, rgb2ypbpr, ypbpr2rgb, rgb2ycbcr, ycbcr2rgb, + rgba2rgb, guess_spatial_dimensions ) @@ -67,6 +68,9 @@ class TestColorconv(TestCase): img_rgb = imread(os.path.join(data_dir, 'color.png')) img_grayscale = imread(os.path.join(data_dir, 'camera.png')) + img_rgba = np.array([[[0, 0.5, 1, 0], + [0, 0.5, 1, 1], + [0, 0.5, 1, 0.5]]]).astype(np.float) colbars = np.array([[1, 1, 0, 0, 1, 1, 0, 0], [1, 1, 1, 1, 0, 0, 0, 0], @@ -95,6 +99,22 @@ class TestColorconv(TestCase): [[46.228, -43.774, 56.589]], # green ]) + # RGBA to RGB + def test_rgba2rgb_conversion(self): + rgba = self.img_rgba + rgb = rgba2rgb(rgba) + expected = np.array([[[1, 1, 1], + [0, 0.5, 1], + [0.5, 0.75, 1]]]).astype(np.float) + self.assertEqual(rgb.shape, expected.shape) + assert_almost_equal(rgb, expected) + + def test_rgba2rgb_error_grayscale(self): + self.assertRaises(ValueError, rgba2rgb, self.img_grayscale) + + def test_rgba2rgb_error_rgb(self): + self.assertRaises(ValueError, rgba2rgb, self.img_rgb) + # RGB to HSV def test_rgb2hsv_conversion(self): rgb = img_as_float(self.img_rgb)[::16, ::16]