Image Matrix Transformations
If \(A\) is a \(3\times 3\) matrix then we can apply a linear transformation to each rgb vector via matrix multiplication, where \([r,g,b]\) are the original values and \([r',g',b']\) are the new values.
If img
is an image then we can apply a matrix transformation A
to the pixel at location \((i,j)\) by
A @ img[i,j,:]
This is a matrix-vector product. Alternatively, if we are viewing the rgb vector as a row vector then we would compute:
img[i,j,:] @ A.T
or using dot
we could do
img[i,j,:].dot(A.T)
The latter is best practice, since this allows us to avoid looping over rows/columns of the image, we could just call
img.dot(A.T)
and it will automatically compute the vector/matrix product for each pixel, and return a new image. This is the method we will use below.
For quick reference, some common matrices we will use are:
sepia_matrix = np.array([[ 0.393, 0.769, 0.189],
[ 0.349, 0.686, 0.168],
[ 0.272, 0.534, 0.131]])
gray_const_matrix = np.array([[ 0.333, 0.333, 0.333],
[ 0.333, 0.333, 0.333],
[ 0.333, 0.333, 0.333]])
gray_matrix = np.array([[0.2989, 0.5870, 0.1140],
[0.2989, 0.5870, 0.1140],
[0.2989, 0.5870, 0.1140],])
red_matrix = np.array([[ 1, 0, 0],
[ 0, 0, 0],
[ 0, 0, 0]])
grn_matrix = np.array([[ 0, 0, 0],
[ 0, 1, 0],
[ 0, 0, 0]])
blu_matrix = np.array([[ 0, 0, 0],
[ 0, 0, 0],
[ 0, 0, 1]])
Sepia Tone
Sepia (tone) is similar to black-and-white in that it is one colour, but it is more brown or tan. This is typically what you see in historic photos. We can convert a colour image to sepia as follows.
The sepia values of a pixel with rgb values \([r,g,b]\) are:
\(\text{sepia}_r = 0.393r + 0.769g + 0.189b\)
\(\text{sepia}_g = 0.349r + 0.686g + 0.168b\)
\(\text{sepia}_b = 0.272r + 0.534g + 0.131b\)
We can write this as a matrix transformation:
To apply this filter to a colour image img
we call
sepia_img = img.dot(sepia_matrix.T)
Since sepia matrix rows do not have unit sums, once the full sepia image is constructed, we need to rescale the result
sepia_img /= sepia_img.max()
then extend values from 0 to 255:
sepia_img *= 255
We'll put all these steps into a function called apply_matrix
:
def apply_matrix(img,A):
trans_img = img/256
trans_img = trans_img.dot(A.T)
trans_img /= trans_img.max()
trans_img *= 255
trans_img = trans_img.astype('uint8')
return(trans_img)
In this example we use this photo of a dog.
img = plt.imread('dog.jpg')
img_gray = apply_matrix(img,gray_matrix) # construct gray scale image
img_sepia = apply_matrix(img,sepia_matrix) # construct sepia tone image
# display all three images
fig = plt.figure(figsize=(12,10))
plt.imshow(np.concatenate((img, img_gray, img_sepia),axis=1))
Gray Scale
We've already covered how to convert an image to gray scale in the Image Manipulation Basics section. However, we can also do this using matrix multiplication. There are two gray scale matrices listed above, the second one give the channels different weights. This is the one we used in converting the dog image to gray scale above.
Red, Green, and Blue Channels
We've already covered how to convert an image to gray scale in the Image Manipulation Basics section. Here we just demonstrate how it could be done with matrix multiplication.
img = plt.imread('dog.jpg')
img_r = apply_matrix(img,red_matrix)
img_g = apply_matrix(img,grn_matrix)
img_b = apply_matrix(img,blu_matrix)
# dispaly all three images
fig = plt.figure(figsize=(12,10))
plt.imshow(np.concatenate((img_r, img_g, img_b),axis=1))