From 1a4ae96024aed2826e13cbf712397f3b32233bf5 Mon Sep 17 00:00:00 2001 From: Ralf Gommers Date: Mon, 19 Oct 2009 18:57:31 +0200 Subject: [PATCH] Add RGB to XYZ bidi conversion. --- scikits/image/color/colorconv.py | 76 ++++++++++++++++++++- scikits/image/color/tests/test_colorconv.py | 50 +++++++++++++- 2 files changed, 121 insertions(+), 5 deletions(-) diff --git a/scikits/image/color/colorconv.py b/scikits/image/color/colorconv.py index 4016c65b..28444898 100644 --- a/scikits/image/color/colorconv.py +++ b/scikits/image/color/colorconv.py @@ -1,17 +1,31 @@ #!/usr/bin/env python # -*- coding: utf-8 -*- -""" -:author: Nicolas Pinto, 2009 +"""Functions for converting between color spaces. + +Supported color spaces +---------------------- +- RGB +- HSV +- XYZ + +Authors +------- +- rgb2hsv was written by Nicolas Pinto +- hsv2rgb was written by Ralf Gommers +- other functions were originally written by Travis Oliphant and adapted by + Ralf Gommers + :license: modified BSD """ from __future__ import division -__all__ = ['rgb2hsv', 'hsv2rgb'] +__all__ = ['rgb2hsv', 'hsv2rgb', 'rgb2xyz', 'xyz2rgb'] __docformat__ = "restructuredtext en" import numpy as np +from scipy import linalg def _prepare_colorarray(arr, dtype="float32"): @@ -155,3 +169,59 @@ def hsv2rgb(hsv): out[np.isnan(out)] = 0 return out + + +#--------------------------------------------------------------- +# Primaries for the coordinate systems +#--------------------------------------------------------------- +cie_primaries = [700, 546.1, 435.8] +sb_primaries = [1./155 * 1e5, 1./190 * 1e5, 1./225 * 1e5] + +#--------------------------------------------------------------- +# Matrices that define conversion between different color spaces +#--------------------------------------------------------------- + +# From sRGB specification +xyz_from_rgb = [[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) + +#------------------------------------------------------------- +# 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 : ndarray + The input array. + + Returns + ------- + out : ndarray + The converted array. + """ + arr = _prepare_colorarray(arr) + arr = np.swapaxes(arr, 0, 2) + oldshape = arr.shape + arr = np.reshape(arr, (3, -1)) + out = np.dot(matrix, arr) + out.shape = oldshape + out = np.swapaxes(out, 2, 0) + + return out + + +def xyz2rgb(xyz): + return _convert(rgb_from_xyz, xyz) + +def rgb2xyz(rgb): + return _convert(xyz_from_rgb, rgb) + + diff --git a/scikits/image/color/tests/test_colorconv.py b/scikits/image/color/tests/test_colorconv.py index 67f641bb..ff36f2f0 100644 --- a/scikits/image/color/tests/test_colorconv.py +++ b/scikits/image/color/tests/test_colorconv.py @@ -1,8 +1,13 @@ #!/usr/bin/env python # -*- coding: utf-8 -*- -""" -:author: Nicolas Pinto, 2009 +"""Tests for color conversion functions. + +Authors +------- +- the rgb2hsv test was written by Nicolas Pinto, 2009 +- other tests written by Ralf Gommers, 2009 + :license: modified BSD """ @@ -15,6 +20,8 @@ from scikits.image.io import imread from scikits.image.color import ( rgb2hsv, hsv2rgb, + rgb2xyz, + xyz2rgb ) from scikits.image import data_dir @@ -27,6 +34,13 @@ class TestColorconv(TestCase): img_rgb = imread(os.path.join(data_dir, 'color.png')) img_grayscale = imread(os.path.join(data_dir, 'camera.png')) + colbars = np.array([[1, 1, 0, 0, 1, 1, 0, 0], + [1, 1, 1, 1, 0, 0, 0, 0], + [1, 0, 1, 0, 1, 0, 1, 0]]) + colbars_array = np.swapaxes(colbars.reshape(3, 4, 2), 0, 2) + colbars_point75 = colbars * 0.75 + colbars_point75_array = np.swapaxes(colbars_point75.reshape(3, 4, 2), 0, 2) + # RGB to HSV def test_rgb2hsv_conversion(self): rgb = self.img_rgb.astype("float32")[::16, ::16] @@ -67,6 +81,38 @@ class TestColorconv(TestCase): self.assertRaises(TypeError, hsv2rgb, [self.img_rgb[0,0]]) + # RGB to XYZ + def test_rgb2xyz_conversion(self): + gt = np.array([[[ 0.950456, 1. , 1.088754], + [ 0.538003, 0.787329, 1.06942 ], + [ 0.592876, 0.28484 , 0.969561], + [ 0.180423, 0.072169, 0.950227]], + [[ 0.770033, 0.927831, 0.138527], + [ 0.35758 , 0.71516 , 0.119193], + [ 0.412453, 0.212671, 0.019334], + [ 0. , 0. , 0. ]]]) + + assert_almost_equal(rgb2xyz(self.colbars_array), gt) + + # stop repeating the "raises" checks for all other functions that are + # implemented with color._convert() + def test_rgb2xyz_error_grayscale(self): + self.assertRaises(ValueError, rgb2xyz, self.img_grayscale) + + def test_rgb2xyz_error_one_element(self): + self.assertRaises(ValueError, rgb2xyz, self.img_rgb[0,0]) + + def test_rgb2xyz_error_list(self): + self.assertRaises(TypeError, rgb2xyz, [self.img_rgb[0,0]]) + + + # XYZ to RGB + def test_xyz2rgb_conversion(self): + # only roundtrip test, we checked rgb2xyz above already + assert_almost_equal(xyz2rgb(rgb2xyz(self.colbars_array)), + self.colbars_array) + + if __name__ == "__main__": run_module_suite()