Hayden Walker (www.haywalk.ca), 2022-04-17. CC-BY-3.0
Last revised 2022-04-18.
I had an idea in the car yesterday: What would happen if you treated colours as three-dimensional vectors, with red, green, and blue values as entries? Then, what would happen if you took an image, got the colour vectors for each pixel, applied some linear transformation to them, and then used the resulting vectors as the colours for pixels in a new image?
I thought this was an interesting idea, so I wrote some software in Java that takes in an image file and a matrix and does exactly that. I don't know if there are any practical applications for this, but I thought it was neat, and the results are some cool-looking images.
If you're interested in trying this out, the code for my little program is available on my GitHub.
See the revisions section for information about revisions to this page and the software.
This is the original image, on which all the transformations were performed. I got it from Wikimedia Commons and it is licenced under the CC0 license (it is in the public domain).
Multiplying an n-dimensional vector by the n*n Identity Matrix gives the same vector back. The Identity Matrix is:
1 | 0 | 0 |
0 | 1 | 0 |
0 | 0 | 1 |
And as expected, the resulting image is the same as the original:
Multiplying an n-dimensional vector by the n*n Zero Matrix gives back the n-dimensional Zero Vector. The Zero Matrix is:
0 | 0 | 0 |
0 | 0 | 0 |
0 | 0 | 0 |
As you might expect, it makes the whole image black:
Each column in the matrix represents one of red, green, or blue. If we want to isolate one colour, for example, red, we can multiply the image by a matrix like this:
1 | 0 | 0 |
0 | 0 | 0 |
0 | 0 | 0 |
By doing this, we remove all non-red pixels:
We can also isolate green with the matrix:
0 | 0 | 0 |
0 | 1 | 0 |
0 | 0 | 0 |
Which results in:
Similarly for blue:
0 | 0 | 0 |
0 | 0 | 0 |
0 | 0 | 1 |
Giving:
We can also multiply images by colours and get something like a filter, using the below matrix for a colour (R, G, B):
R/255 | 0 | 0 |
0 | G/255 | 0 |
0 | 0 | B/255 |
To apply a yellow filter, which is (255, 255, 0), we can multiply by:
1 | 0 | 0 |
0 | 1 | 0 |
0 | 0 | 0 |
And get:
Or for pink, which is (255, 192, 203):
1 | 0 | 0 |
0 | 0.7529 | 0 |
0 | 0 | 0.7961 |
To get:
The standard matrix for a projection onto the line in the direction of unit vector u is u*u^{T}. So to project vectors onto the line in the direction of (R, G, B), we multiply by the matrix:
1/(r^{2} + g^{2} + g^{2}) * | r^{2} | rg | rb | |
rg | g^{2} | gb | ||
rb | gb | b^{2} |
Where r = R/255, g = G/255, and b = B/255.
If we take the colour aquamarine's RGB vector (127, 255, 212), normalize it, and multiply it by its transpose as in the above formula, the resulting matrix is:
0.1279 | 0.2568 | 0.2135 |
0.2568 | 0.5157 | 0.4288 |
0.2135 | 0.4288 | 0.3564 |
And the result of that matrix being applied to the original image is:
It may be interesting to use a colour from the filter section to see the difference between multiplying the image by a colour and projecting it onto the line in the direction of a colour. Let's use yellow, which is (255, 255, 0). Using the projection formula, the matrix is:
0.5 | 0.5 | 0 |
0.5 | 0.5 | 0 |
0 | 0 | 0 |
And the resulting image is:
If you compare this image to the image in the previous section that was the result of multiplying the original image by yellow, you'll notice that this one is monochromatic and the previous one is not. The projection makes it so that every pixel is a shade of the colour the image was projected onto, instead of simply layering the new colour over the previous colours.
We can use projections from the above section to make images black and white, simply by projecting them onto the line in the direction of the colour white, which is (255, 255, 255).
Plugging (255, 255, 255) into the formula from the last section, the standard matrix is:
0.3333 | 0.3333 | 0.3333 |
0.3333 | 0.3333 | 0.3333 |
0.3333 | 0.3333 | 0.3333 |
And naturally, the result is:
The standard matrix for a rotation about the x-axis (really being the 'red' axis) by angle θ is:
1 | 0 | 0 |
0 | cos θ | –sin θ |
0 | sin θ | cos θ |
So if we rotate each colour vector by π/2 radians about the x-axis, we multiply it by:
1 | 0 | 0 |
0 | 0 | -1 |
0 | 1 | 0 |
And the result is:
This image has been revised. See the notes section.
The standard matrix for a rotation about the y-axis (the 'green' axis) by angle θ is:
cos θ | 0 | sin θ |
0 | 1 | 0 |
–sin θ | 0 | cos θ |
So to rotate π/2 radians about the green axis, we use:
0 | 0 | 1 |
0 | 1 | 0 |
-1 | 0 | 0 |
And get:
This image has been revised. See the notes section.
The standard matrix for a rotation about the z-axis (the 'blue' axis) by angle θ is:
cos θ | –sin θ | 0 |
sin θ | cos θ | 0 |
0 | 0 | 1 |
So to rotate π/2 radians about the blue axis, we multiply by:
0 | -1 | 0 |
1 | 0 | 0 |
0 | 0 | 1 |
And the result is:
This image has been revised. See the notes section.
The 3x3 Pascal matrix is:
1 | 0 | 0 |
1 | 1 | 0 |
1 | 2 | 1 |
When applied to the original image, the result is:
The Matrix of Ones is the matrix with all entries equal to one. The 3x3 Matrix of Ones is:
1 | 1 | 1 |
1 | 1 | 1 |
1 | 1 | 1 |
When applied to the image, the result is:
These are some things my software could do better:
Using this software and the idea of colours as vectors, I've been able to use matrices to apply filters, effects, and distortions to an image. This has been an interesting weekend project and I hope others might also find it interesting, and maybe even useful.