Easily change the color of an image with ColorMatrix, explained with examples

Elye
Elye
Aug 16 · 10 min read
Picture by Denise Chan on Unsplash

If we want to change color of an image, is it complicated? It is very easy if we understand the use of ColorMatrix to filter it. I’ll describe this in detail and provide various useful examples of it here.

For illustration, I’ll use the picture of the colorful roses.


ColorMatrix Mathematically

ColorMatrix is a data class that contains a four rows x five columns array of values.

Mathematically, if the array is as below:

[ a, b, c, d, e,
f, g, h, i, j,
k, l, m, n, o,
p, q, r, s, t ]

Then the new color [R’G'B'A'] applied to the image is a mathematic operation on the original color [RGBA] as below:

R’ = a*R + b*G + c*B + d*A + e;
G’ = f*R + g*G + h*B + i*A + j;
B’ = k*R + l*G + m*B + n*A + o;
A’ = p*R + q*G + r*B + s*A + t;

This looks complicated. Don’t worry, let me illustrate it simply, sample-by-sample, below.

But before I do that, to use it, an example that converts an image to gray is listed below.

val matrix = floatArray(
1f, 1f, 1f, 0f, 0f,
1f, 1f, 1f, 0f, 0f,
1f, 1f, 1f, 0f, 0f,
0f, 0f, 0f, 1f, 0f)
image.colorFilter = ColorMatrixColorFilter(matrix)

Original Color

If we want to have an original color as the image, the matrix will be as below:

[ 1, 0, 0, 0, 0,
0, 1, 0, 0, 0,
0, 0, 1, 0, 0,
0, 0, 0, 1, 0 ]

You will notice with this, the new colors will be as below, as the others are multiplied by 0.

R' = 1*R
G' = 1*G
B' = 1*B
A' = 1*A

Colorized It

To colorize it to a specific color only, we just need to focus on the row specific to the color we want to change it to.

Red

If we want to make the picture either red or no color, let’s focus on the first row. (As that line generates the new red.)

[ 1, 1, 1, 0, 0,
0, 0, 0, 0, 0,
0, 0, 0, 0, 0,
0, 0, 0, 1, 0 ]

The resulting formula is:

R' = 1*R + 1*G + 1*B
G' = 0
B' = 0
A' = 1*A (we like to maintain the opacity level)

This means that any strong red, green, or blue color pixel will now be strong red. But those weak ones will all be darker red or black.

Green

Similarly, for green, we could make the same thing but this time for the second row for the green value:

[ 0, 0, 0, 0, 0,
1, 1, 1, 0, 0,
0, 0, 0, 0, 0,
0, 0, 0, 1, 0 ]

The image is:

Blue

Similarly, for blue, we could make the same thing but this time for the second row for the green value:

[ 0, 0, 0, 0, 0,
0, 0, 0, 0, 0,
1, 1, 1, 0, 0,
0, 0, 0, 1, 0 ]

The image is:


Gray Scale It

A gray color is a color where R, G, and B have the same value.

Even gray scale

To evenly gray-everything, let use the formula below. (Note: I use 0.33 instead of 1 to ensure it is not too bright. The bigger the value, the brighter (or whiter) the image will be.)

[ 0.33, 0.33, 0.33, 0, 0,
0.33, 0.33, 0.33, 0, 0,
0.33, 0.33, 0.33, 0, 0,
0, 0, 0, 1, 0 ]

The result will be as below where R'=G'=B'.

R' = 0.33*R + 0.33*G + 0.33*B
G' = 0.33*R + 0.33*G + 0.33*B
B' = 0.33*R + 0.33*G + 0.33*B
A' = 1*A (we like to maintain the opacity level)

Gray bright on red

We could also make a different gray by focusing on where the original red color spot is brighter compared to others. We could use the following:

[ 1, 0, 0, 0, 0,
1, 0, 0, 0, 0,
1, 0, 0, 0, 0,
0, 0, 0, 1, 0 ]

Where the result is as below.

Notice that, if there’s a greater original R, the result will be greater R'G'B'.

R' = 1*R
G' = 1*R
B' = 1*R
A' = 1*A (we like to maintain the opacity level)

Gray bright on green

Similarly, if we like to focus on brighter grays for areas originally green:

[ 0, 1, 0, 0, 0,
0, 1, 0, 0, 0,
0, 1, 0, 0, 0,
0, 0, 0, 1, 0 ]

Gray bright on blue

Similarly, if we like to focus on brighter grays for areas originally green:

[ 0, 0, 1, 0, 0,
0, 0, 1, 0, 0,
0, 0, 1, 0, 0,
0, 0, 0, 1, 0 ]

Inverting the Color

Just like olden-days photo film, if we would like to invert the color of the photo, we could do that by generating a number with 255-(originalColor).

To do this, we’ll have to be mathematically creative, using the below matrix.

[ -1, 0, 0, 0, 255,
0, -1, 0, 0, 255,
0, 0, -1, 0, 255,
0, 0, 0, 1, 0 ]

This will make all the new colors as below (which match with our intended formula).

R' = -1*R + 255 = 255 -1*R
G' = -1*G + 255 = 255 -1*G
B' = -1*B + 255 = 255 -1*B
A' = 1*A (we like to maintain the opacity level)

Binary the Color

We have the primary colors of red, green, and blue, and the secondary colors of magenta, cyan, and yellow. We could convert all colors to the binary colors, depending on the dominant color of the pixel.

To do that, we need to come up with a formula.

But before that, one important note:

If a calculated color value is greater than 255, it will cap at 255. If a calculated color value is smaller than 0, it will be cap at 0.

The above note is very handy for our case. We want to ensure that all colors are either 255 or 0. We could have a formula like this:

NewColor = 255 * OriginalColor — 128 * 255 [Cap at 0 to 255]

Let’s test the value:

- Assuming original color is 0, the new value is -32640. But since it is cap at minimum 0, it is 0.
- Assuming original color is 255, the new value is 32640. But since it is cap at maximum 255, it is 255.
- Assuming original color is 127, the new value is -255, which is converted to 0
- Assuming original color is 128, the new value is 0
- Assuming original color is 129, the new value is 255

So, we have proven that any original color of 128 or less is converted to 0 and any original color of 129 to 255, will be 255.

With that, we could have our matrix as below:

[ 255, 0, 0, 0, -128*255,
0, 255, 0, 0, -128*255,
0, 0, 255, 0, -128*255,
0, 0, 0, 1, 0 ]

You’ll realize that the decision to convert to either 0 or 255 is based on the coefficient 128-set. If we make that a variable, we could then adjust how much we make it bright/dim for the binary color, as per the demo below.


Binary Black and White

By understanding how the binary works, we could now also make it into black and white. This is easier, as we only need to make sure that all R' G' and B' have the same value.

I’ll be using the below matrix:

[ 85, 85, 85, 0, -128*255,
85, 85, 85, 0, -128*255,
85, 85, 85, 0, -128*255,
0, 0, 0, 1, 0 ]

The reason I use 85 is because of this: 85*3=255. I just want to make it balance with 128*255 in the formula.

The result is as below.

The above picture is a bit darker. We could consider making it brighter by changing the 128 coefficient to a lower value.

Below is an example of dynamically changing the coefficient:


Swapping Color

Or just for fun, if you like, we could swap color.

Red to green, green to blue, blue to red

Let’s try this combination and see what it looks like.

[ 0, 0, 1, 0, 0,
1, 0, 0, 0, 0,
0, 1, 0, 0, 0,
0, 0, 0, 1, 0 ]

The formula became:

R' = 1*B
G' = 1*R
B' = 1*G
A' = 1*A

Red to blue, blue to green, green to red

Let’s try this combination and see what it looks like.

[ 0, 1, 0, 0, 0,
0, 0, 1, 0, 0,
1, 0, 0, 0, 0,
0, 0, 0, 1, 0 ]

The formula became:

R' = 1*G
G' = 1*B
B' = 1*R
A' = 1*A

ColorMatrix Data Class

If manually manipulating the matrix is troublesome for you, you could use the ColorMatrix class. Initializing it as below will generate the original matrix.

val colorMatrix = ColorMatrix()

The original matrix is as below:

[ 1, 0, 0, 0, 0,
0, 1, 0, 0, 0,
0, 0, 1, 0, 0,
0, 0, 0, 1, 0 ]

Applying this will not make any change to the image. To make changes, you’ll need to use a function as below.

But before that, to use it, an example is shown below.

val matrix = Matrix()
image.colorFilter = ColorMatrixColorFilter(matrix)

Scaling

The scale basically brightens each color accordingly.

Normally, it would be set from 0–1 (which is from no color to the original color). But, it could be set to more than 1 as well to brighten the particular color (if it is not at its brightest yet (255)).

matrix.setScale(redScale, greenScale, blueScale, alphaScale)

For simplicity’s sake, below is an illustration of scaling from 0–2 evenly across red, green, and blue, showing the effect of brightening the image color evenly.

Saturation

Saturation is converting a color from an unsaturated gray to its original color and higher. It looks similar to scaling but it is more towards even color (not likely hitting a whitening effect, as scaling does).

matrix.setSaturation(saturationValue)

Below is a saturation from 0 to 2.

Rotating

This is similar to scaling but applies specifically towards one full color (or red, green, or blue).

matrix.setRotate(colorInt, degree)

The colorInt is where 0=red, 1=green, 2=blue, and degree is from 0 to 360, where it is the original color when degree=0 or 360, and degree=180 is the full color indicated by colorInt.


RGB to YUV

Besides RGB, there’s another color scheme, YUV. I don’t understand what it is, but the matrix has a function to convert RGB to YUV and vice versa.

matrix.setRGB2YUV()

Below is the result:


Conclusion

That’s it. So many ways to manipulate the color.

Mostly, it is just one line of control.

You can get the code from:

If you would like to blending on two images and produce some nice effect, check out the blog below.

If you only want to control the brightness and you’re looking for something simpler, you could refer to the blog below:

I hope this post is helpful to you.

Better Programming

Advice for programmers.

Elye

Written by

Elye

Learning and Sharing Android and iOS Development

Better Programming

Advice for programmers.

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade