Match Colors in Fecolormatrix Filter

Match colors in feColorMatrix filter

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.

Matching a color in SVG with feColorMatrix

feColorMatrix like most filters operates in the linearRGB colour space. If you want an sRGB colour, Try setting color-interpolation-filters="sRGB" as an attribute on the feColorMatrix.

    <svg width="100%" xmlns:xlink="http://www.w3.org/1999/xlink"



viewBox="0 0 640 480" height="100%"

xmlns="http://www.w3.org/2000/svg">



<filter id="cm">



<feColorMatrix in="SourceGraphic" type="matrix"

values="0.0157 0 0 0 0

0 0.3059 0 0 0

0 0 0.7765 0 0

0 0 0 1 0 "/>



</filter>



<filter id="cmRGB">



<feColorMatrix color-interpolation-filters="sRGB" in="SourceGraphic" type="matrix"

values="0.0157 0 0 0 0

0 0.3059 0 0 0

0 0 0.7765 0 0

0 0 0 1 0 "/>



</filter>



<rect width="100%" height="50%" fill="white" filter="url(#cm)"/>



<rect y="50%" width="100%" height="100%" fill="white" filter="url(#cmRGB)"/>



</svg>

SVG Filter - feColorMatrix has non-linear color

So, firstly, don't believe everything you read in the specs - especially when they're not
at least a Candidate Recommendation. That filter spec has been worked on for years and many things that are different than the SVG 1.1 spec have not been implemented.

In SVG 1.1 (the current cross browser baseline) - filters operate in the linearRGB space. From the SVG spec (15.1)

(‘color-interpolation-filters’ has an initial value of linearRGB

(187, 187, 187) is what happens when you do 50% in the linearRGB space and then measure the result using an sRGB eye-dropper. In order to over-ride it, you must put color-interpolation-filters="sRGB" in your filter element.

This is a perfectly fine way to convert colors. As Robert mentioned in the comment - you can also use a flood & a composite to do it.

<filter id="flood-recolor">
<feFlood flood-color="grey"/>
<feComposite operator="in" in2="SourceGraphic"/>
</filter>

Consolidate two SVG feColorMatrix filter effects into one matrix?

In my experience ColorMatrix operations are very fast, and blurs are incredibly slow, so I'm guessing that combining these won't get you very far. But FWIW, here's the combined matrix. (This is not hard to do - there are lots of online matrix multipliers:)

2.5537  -0.61224    0.52246 0   0
-0.4163 1.30776 -0.10754 0 0
0.3037 -0.88224 2.3546 0 0
0 0 0 1 0
0 0 0 0 1

Update:

Ok step by step. According to the svg filters spec the matrix equivalent of a saturate is:

| R' |     |0.213+0.787s  0.715-0.715s  0.072-0.072s 0  0 |   | R |
| G' | |0.213-0.213s 0.715+0.285s 0.072-0.072s 0 0 | | G |
| B' | = |0.213-0.213s 0.715-0.715s 0.072+0.928s 0 0 | * | B |
| A' | | 0 0 0 1 0 | | A |
| 1 | | 0 0 0 0 1 | | 1 |

So all you have to do is build a little excel table that multiples this out for you.

Then you look at the spec for hue rotate and you find that the matrix equivalent for huerotate is:

| R' |     | a00  a01  a02  0  0 |   | R |
| G' | | a10 a11 a12 0 0 | | G |
| B' | = | a20 a21 a22 0 0 | * | B |
| A' | | 0 0 0 1 0 | | A |
| 1 | | 0 0 0 0 1 | | 1 |

where the terms a00, a01, etc. are calculated as follows:

| a00 a01 a02 |    [+0.213 +0.715 +0.072]
| a10 a11 a12 | = [+0.213 +0.715 +0.072] +
| a20 a21 a22 | [+0.213 +0.715 +0.072]
[+0.787 -0.715 -0.072]
cos(hueRotate value) * [-0.213 +0.285 -0.072] +
[-0.213 -0.715 +0.928]
[-0.213 -0.715+0.928]
sin(hueRotate value) * [+0.143 +0.140-0.283]
[-0.787 +0.715+0.072]

Since radians are expected inputs to Excel trig formulae, you do a degree to radian conversion (340deg is about 5.9 radians - or just less than 2* pi) and then build the resulting matrix in Excel. You could hand multiply the matrices, but frankly I don't have a huge desire to relive linear algebra class. So you google "matrix multiply online" and pop the two matrices into any of the matrix multiply tools on page 1 of results. And there you go. A consolidated matrix.

Easy :-)



Related Topics



Leave a reply



Submit