Reputation: 3510
I have an RGB image loaded into a numpy array via PIL. I get a rows x cols x 3 array. After tinkering, I arrived at the following code. I'd like to learn how to do such an array/matrix manipulation without the loop.
# Note using matrix not array.
rgb_to_ycc = np.matrix(
(0.2990, 0.5870, 0.1140,
-0.1687, -0.3313, 0.5000,
0.5000, -0.4187, -0.0813,)
).reshape( 3,3 )
ycc_to_rgb = np.matrix(
( 1.0, 0.0, 1.4022,
1.0, -0.3456, -0.7145,
1.0, 1.7710, 0, )
).reshape( 3, 3 )
def convert_ycc_to_rgb( ycc ) :
# convert back to RGB
rgb = np.zeros_like( ycc )
for row in range(ycc.shape[0]) :
rgb[row] = ycc[row] * ycc_to_rgb.T
return rgb
def convert_rgb_to_ycc( rgb ) :
ycc = np.zeros_like( rgb )
for row in range(rgb.shape[0]):
ycc[row] = rgb[row] * rgb_to_ycc.T
return ycc
I could use http://pypi.python.org/pypi/colormath (via Using Python to convert color formats?) but I'm using this as an exercise to learn numpy.
The aforementioned Colormath library uses a dot product.
# Perform the adaptation via matrix multiplication.
result_matrix = numpy.dot(var_matrix, rgb_matrix)
My math isn't where it should be. Is np.dot() my best bet?
EDIT. After deeper reading colormath's apply_RGB_matrix()-color_conversions.py, I found the np.dot() works if my conversion 3x3s are not matrices. Weird.
def convert_rgb_to_ycc( rgb ) :
return np.dot( rgb, np.asarray( rgb_to_ycc ).T )
Upvotes: 3
Views: 3193
Reputation: 879701
I'm not sure about the formula you are using to convert RGB to YCC, so I don't want to claim this is the complete calculation, but to simplify the function you posted, yes, use np.dot
with numpy arrays instead of numpy matrices.
np.dot
is more general than *
with numpy matrices. When using *
with numpy matrices, the two matrices have to be 2 dimensional.
But np.dot
can produce a result with arrays of different shape. This is important for your application since rgb
is 3-dimensional (e.g. when it has shape (1470, 2105, 3)).
The docs for np.dot
say:
For N dimensions it is a sum product over the last axis of `a` and
the second-to-last of `b`::
dot(a, b)[i,j,k,m] = sum(a[i,j,:] * b[k,:,m])
This is a generalization of regular matrix multiplication.
I suggest calling your ultimate function rgb_to_ycc
, instead of giving that designation to the constant matrix. (It's shorter and says exactly what you want the function to do.)
So below, rgb_to_ycc
is my suggested function, and I made some minor modifications to make convert_rgb_to_ycc
not raise exceptions and do the computation I think you are intending.
The final line, np.allclose(...)
shows the two functions return the same result.
import numpy as np
def rgb_to_ycc(rgb):
M = np.array(
(0.2990, 0.5870, 0.1140,
-0.1687, -0.3313, 0.5000,
0.5000, -0.4187, -0.0813,)
).reshape( 3,3 )
return np.dot(rgb, M.T)
def convert_rgb_to_ycc( rgb ) :
M = np.matrix(
(0.2990, 0.5870, 0.1140,
-0.1687, -0.3313, 0.5000,
0.5000, -0.4187, -0.0813,)
).reshape( 3,3 )
shape=rgb.shape
rgb=rgb.reshape((-1,3))
ycc = np.zeros_like( rgb )
for i in range(len(rgb)):
ycc[i] = rgb[i] * M.T
return ycc.reshape(shape)
rgb=np.random.random((100,100,3))
assert np.allclose(rgb_to_ycc(rgb),convert_rgb_to_ycc(rgb))
Upvotes: 4
Reputation: 76067
def convert_ycc_to_rgb(ycc):
return ycc * ycc_to_rgb.T
def convert_rgb_to_ycc(rgb):
return rgb * rgb_to_ycc.T
as simple as that, remember how matrix multiplication is defined in terms of inner products of rows and columns.
edit:
I was assuming that the rgb and ycc matrices were just a matrix that had as many rows as pixels and a column per colour component. So what we need to do first is to reshape them to a (rows*cols,3)
and then back again to (rows, cols, 3)
.
So the code finally is:
def convert_ycc_to_rgb(ycc):
shape = ycc.shape
return np.array(ycc.reshape(-1,3) * ycc_to_rgb.T).reshape(shape)
def convert_rgb_to_ycc(rgb):
shape = rgb.shape
return np.array(rgb.reshape(-1,3) * rgb_to_ycc.T).reshape(shape)
Upvotes: 3