I have a greyscale image. I want to filter it such that white -> color1
and black -> color2
. Both colors are in hex CSS syntax. What math do I need to do to get this effect?
I'm using this syntax:
<!-- just an example -->
<filter id="colorMeMatrix">
<feColorMatrix in="SourceGraphic"
type="matrix"
values="0 0 0 0 0
1 1 1 1 0
0 0 0 0 0
0 0 0 1 0" />
To understand the matrix you need, you have to clearly define what you're starting with, what you want to end up with, and how the colour matrix filter works.
The matrix has five columns and four rows. Each row represents one of the output numbers: R,G,B,A. The columns represent your input RGBA and a constant 1. You calculate the output value for each row by adding up each of the values in the row multiplied by the corresponding input value.
Both the input and the output numbers are standardized to the range 0-1, so you don't have to worry about multiplying everything by 256.
So for the matrix in your example:
/*R G B A 1 */
0 0 0 0 0 // R = 0*R + 0*G + 0*B + 0*A + 0
1 1 1 1 0 // G = 1*R + 1*G + 1*B + 1*A + 0
0 0 0 0 0 // B = 0*R + 0*G + 0*B + 0*A + 0
0 0 0 1 0 // A = 0*R + 0*G + 0*B + 1*A + 0
It takes the input color, adds up all four channels (RGBA) and makes the result the green channel. The red and blue channels are zero and the alpha channel isn't changed. So your picture ends up with black areas still black, but all coloured/gray/white/transparent areas are converted into shades of green.
That's not what you wanted, of course. You wanted to have black areas one colour and white areas another colour, and all gray areas somewhere in between the two.
To make black areas a certain colour, you have to set the constant-factor parameters of your matrix. The input RGB values are going to be zero for black, so they don't factor in at this point.
If your colour2, the value you want to use for black in your monochrome image, is (r2, g2, b2), then that's what your constant factors have to be:
/*R G B A 1 */
? ? ? 0 r2 // R = ?*0 + ?*0 + ?*0 + 0*0 + r2 = r2
? ? ? 0 g2 // G = ?*0 + ?*0 + ?*0 + 0*0 + g2 = g2
? ? ? 0 b2 // B = ?*0 + ?*0 + ?*0 + 0*0 + b2 = b2
0 0 0 1 0 // A = 1*A
Of course, the above matrix will turn any input colour into that output colour, because it doesn't factor in anything from the input RGBA values. To get the gradient you want, you need white areas -- those with input values of 1 for R, G, and B, to end up with your colour1, which I'm going to write as (r1, g1, b1).
Now, to make things a little easier, remember that for a grayscale image the R, G and B values for any point will be equal. So we can just use any one of those values as the input and ignore the others. So if we just set the R-factor for each channel, when the input value is white the input R equals 1 and the equations are
/*R G B A 1 */
? 0 0 0 r2 // R = ?*1 + r2 = r1
? 0 0 0 g2 // G = ?*1 + g2 = g1
? 0 0 0 b2 // B = ?*1 + b2 = b1
0 0 0 1 0 // A = 1*A
Simple algebra tells you that in order to make those equations work, you need to replace the question marks with the difference between the colour1 and colour2 values:
/* R G B A 1 */
(r1-r2) 0 0 0 r2 // R = (r1-r2)*1 + r2 = r1
(g1-g2) 0 0 0 g2 // G = (g1-g2)*1 + g2 = g1
(b1-b2) 0 0 0 b2 // B = (b1-b2)*1 + b2 = b1
0 0 0 1 0 // A = 1*A
For example, if you want white areas of the input image to map to cyan (r=0,g=1,b=1) and your black input areas to map to a deep purple (r=0.1, g=0, b=0.2), you would use a matrix like
/*R G B A 1 */
-0.1 0 0 0 0.1 // R = (-0.1)*R + 0.1
1 0 0 0 0 // G = 1*R + 0
0.8 0 0 0 0.2 // B = 0.8*R + 0.2
0 0 0 1 0 // A = 1*A
Using that matrix in a filter applied to this original image.
Note that this is actually quite different from the example I linked to in a comment; in that filter, I was trying to maintain white as white and black as black, but change the grays to colour. For that I used a gamma correction filter, not a colour matrix.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With