Eyeon:Script/Reference/Applications/Fuse/Concepts/Using the ColorMatrix
From VFXPedia
Using the ColorMatrix
Contents |
Introduction
In Using the Matrix we saw how a Matrix object could be used to collect multiple spatial transformations on an image. In this article we explore how a variant on that technique can also be used to perform color operations.
The key to this approach is that you consider the values in the red green and blue channels as co-ordinates in x, y and z instead. Once you do that, it becomes apparent that operations like Gain are really no different than spatial transformations like Scale.
In fact, Gain is identical to scaling the image, while brightness is nothing more than a translation of the image. A contrast operation is nothing more than a scale centered around 0.5 instead of 0.0. Even the conversion to YUV can be represented in this way. This allows us build up several 'linear' color operations into one operation, and then apply then as a single pass. We can even use a matrix to swap channels, or mix them together.
Non linear color operations (like gamma) cannot be represented this way in a Matrix.
Fusion provides a ColorMatrix object for RGB image operations, and the ColorMatrixFull object for RGBA images.
The Math of the Matrix
The 'ColorMatrix' is a 4x4 matrix, so you can use it to affect RGB. The 'ColorMatrixFull' is a 5x5 matrix, which adds Alpha to the RGB color channels.
In the terminology of Matrix mathematics, an "identity matrix" is one that doesn't change anything. A 4x4 identity matrix would look like:
b g r B 1.0 0.0 0.0 0.0 G 0.0 1.0 0.0 0.0 R 0.0 0.0 1.0 0.0 0.0 0.0 0.0 1.0
We could say that uppercase B, G and R are going to be the results, and lowercase b, g and r are the source/input values. You can completely ignore the bottom row, but not the right column.
NOTE: The underlying matrix uses the channel order Blue, Green, Red. This is different from the RGB channel order Fusion usually presents to the artist, but closer to how Fusion represents images internally.
So just looking at the first row, it says the R result will contain 1.0 of b, 0.0 of g and 0.0 of r (or 1.0 * b + 0.0 * g + 0.0 * r). And the second row says the B result will contain 0.0 of b, 1.0 of g and 0.0 of r. And similar for the 3rd row.
So far we just ignored the right column, but you can consider the source/input value for the right column to be 1.0. So that would make the first row of the matrix produce results for B = 1.0 * b + 0.0 * g + 0.0 * r + 0.0 * 1.0.
With that, we can do "brightness" by increasing or decreasing how much of the 1.0 input value is included in the result. Lets say we wanted a brightness of 0.5 in the B result. That would make the B row of the matrix:
b g b 1.0 B 1.0 0.0 0.0 0.5
B = 1.0 * b + 0.0 * g + 0.0 * r + 0.5 * 1.0 = 1.0 * b + 0.5 * 1.0 = b + 0.5
If instead we wanted a "gain" of 2.0 (so we want the B result to have 2 times b), that would make the B row of the matrix:
b g r 1.0 B 2.0 0.0 0.0 0.0
B = 2.0 * b + 0.0 * g + 0.0 * r + 0.0 * 1.0 = 2.0 * b
Say we wanted to put g into the resulting B, (copying the green channel into the Blue channel of the output) then the first row of the matrix would be:
b g r 1.0 B 0.0 1.0 0.0 0.0
B = 0.0 * b + 1.0 * g + 0.0 * r + 0.0 * 1.0 = 1.0 * g = g
If you wanted B to be the "luminance" of rgb, the first row of the matrix would be:
b g r 1.0 B 0.114 0.587 0.299 0.0
B = 0.114 * b + 0.587 * g + 0.299 * r + 0.0 * 1.0 = 0.114 * b + 0.587 * g + 0.299 * r
If we want B to be an inverted version of b, we want to do "1.0 - b", which means we want to make b negative (gain of -1.0) and then add 1.0 (brightness of 1.0), so the first row of the matrix would be:
b g r 1.0 B -1.0 0.0 0.0 1.0
B = -1.0 * b + 0.0 * g + 0.0 * r + 1.0 * 1.0 = -1.0 * b + 1.0 * 1.0 = -b + 1.0 = 1.0 - b
Editing the Matrix
Each matrix object exposes individual elements as properties. To access the first element in the first row, we would use the property matrix.n11, the second element would be matrix.n21, then matrix.n31 and so on. This is best demonstrated by the following code, which would print a table of all the elements in a 4x4 ColorMatrix, organized as in our examples above.
m = ColorMatrix() print("", "b", "g", "r", "1") print("B", m.n11, m.n21, m.n31, m.n41) print("G", m.n12, m.n22, m.n32, m.n42) print("R", m.n13, m.n23, m.n33, m.n43) print("A", m.n14, m.n24, m.n34, m.n44)
The following function could be used to copy a ColorMatrix.
function CopyColorMatrix(m) local new_m = ColorMatrix() new_m.n11 = m.n11 new_m.n21 = m.n21 new_m.n31 = m.n31 new_m.n41 = m.n41 new_m.n12 = m.n12 new_m.n22 = m.n22 new_m.n32 = m.n32 new_m.n42 = m.n42 new_m.n13 = m.n13 new_m.n23 = m.n23 new_m.n33 = m.n33 new_m.n43 = m.n43 return new_m end
The following could be used to apply a gain of 0.5 to each pixel in an image.
local img = InImg:GetValue(req) m = ColorMatrix() m.n11 = 0.5 m.n22 = 0.5 m.n33 = 0.5
Using Methods
While manipulating the Matrix directly is sometimes necessary, the ColorMatrix and ColorMatrixFull objects expose several methods than can make common operations much simpler. For example, we can use the Scale function to simplify the Gain example in the section above.
local img = InImg:GetValue(req) m = ColorMatrix() m:Scale(0.5, 0.5, 0.5)
The advantage to this approach is that the Scale method takes care of preserving the existing transformations applied to the Matrix. Our original example would have overwritten any existing transformations.
Using a similar technique, the Offset method can be used to perform a brightness operation.
local m = ColorMatrixFull() m:Offset(r, g, b, a)
You can find a complete list of the methods available at the at the ColorMatrix and ColorMatrixFull object reference pages.
Applying the matrix
Once the matrix has been created, you can apply it to the image using the Image objects ApplyMatrix and ApplyMatrixOf functions.