This command mixes the channels of each pixel in an output image from its counterpart in an input image. Nine transfer weights, W<in_chn>→<out_chn>, govern the reconstitution, one for each pairing of an input channel to an output channel. The final value of each output channel arises from a weighted sum of the three input channels, illustrated in the following schematic:
The format of the G'MIC command is:
-mix_rgb Wrr,Wgr,Wbr,Wrg,Wgg,Wbg,Wrb,Wgb,Wbb
where each parameter represents one weight and is a floating point numeral ranging from negative to positive infinity, The subscripts denote a particular input-output channel pair which the weight connects:
Red Input | Green Input | Blue Input | |
Red Output | Wrr | Wgr | Wbr |
Green Output | Wrg | Wgg | Wbg |
Blue Output | Wrb | Wgb | Wbb |
For example, weight Wbr scales the input pixel's blue channel, Iblue, producing a contributory term for the output pixel's red channel, Ored. The complete computations are:
Ored | = | Ired × Wrr | + | Igreen × Wgr | + | Iblue × Wbr |
Ogreen | = | Ired × Wrg | + | Igreen × Wgg | + | Iblue × Wbg |
Oblue | = | Ired × Wrb | + | Igreen × Wgb | + | Iblue × Wbb |
Red Input | Green Input | Blue Input | |
Red Output | 1.684 | 0.435 | 1.289 |
Green Output | 0.882 | 0.385 | 0.913 |
Blue Output | -0.239 | 0.672 | 0.597 |
We find the output channel Ogreen by scaling input red channel Ired by Wrg=0.882, input green channel Igreen by Wgg=0.385, and input blue channel Iblue by Wbg=0.913 and taking the sum. We proceed in a similar fashion for Ored and Oblue. The following G'MIC commands implement this mix and produces an approximate display:
$ gmic -input test_rgb.png --mix_rgb 1.684,0.435,1.289,0.882,0.385,0.913,-0.239,0.672,0.597 \
-display -normalize[-1] 0,255 -output[-1] test_rgb_out.png
Input test Pattern | Normalized output (beware color shifting) |
Readers familar with color spaces will recognize that the table of weights W<input>→<output> form a linear map that transforms color vectors from one to another color space. We can compactly describe '-mix_rgb' with the matrix equation:
for input i and output o, each red-green-blue column vectors, and linear map W, the weights expressed as a 3×3 matrix. The identity matrix corresponds to Wrr = Wgg = Wbb = 1.0, with all other weights equal zero. This set of weights will not alter an image at all under '-mix_rgb'.
Armed with this insight, technically inclined readers may tap standard scaling, rotation and shearing linear maps to achieve various effects. Examples follow:
Identity Matrix: Wrr=Wgg=Wbb=1.0. All other weights are zero. Obviously the identity matrix leaves the test image unchanged. | |
Scale, Green Filter. Wgg=1.0. All other weights are zero. One may generally manipulate Wrr, Wgg, and Wbb to change the color balance, keeping the off-diagonal weights to zero. | |
Rotation of 180° around the red axis: Wrr=1.0, Wgg=Wbb=-1.0. All other weights are zero. This weighting introduces color values less than zero, as pure green and pure blue both equal negative one. Normalization repositions black to mid-gray. negative colors transform under normalization to darker shades. |
Color rotation furnishes a means of varying pixel hues selectively, some dramatically and others hardly at all, a kind of selective color adjustment tool. We pick an anchor color that we like and to which we would like to attune – or mis-attune – the others, depending on artistic inclinations. The animation illustrates the concept. We first anchor a dirty orange color in Gimicky's coat and then step through numerous rotations from 0° to 360°. The coat varies very little in relation to other colors in the image. We then pick a color in the sky, and while the blue of heaven's vault remains relatively unchanged, the two characters change a great deal.The two color transitions are very different.
In both runs, '-mix_rgb' computed the pixel-level changes. A front-end script translated rotation axes and degrees of turning into linear maps. Details follow.
We imagine that in color space, the hue we pick and black, an implied anchor at the origin, establish an axis of rotation. Colors on this axis – the hub – will not change under rotations. The anchor color, darker versions of it running toward black, and lighter versions running out to infinity, have no radial distance from the hub, cannot move under any of its rotations, and thus are "preserved". On the other hand, off-hub colors have positive radial distances and, like points on a wheel rim, will move, depending on the rotation angle of the hub, its direction, clockwise or counterclockwise, and the radial distance of the color from the hub. Colors very close to the hub, and closely related to the anchor color, hardly move at all even under large angles of rotation. Conversely, colors far removed from the hub, and very much unlike the anchor color, change a great deal, under even small rotations.
The nine weights we feed to '-mix_rgb' constitutes a linear map, a general means of translating colors from one spatial configuration to another. '-mix_rgb' can visit every pixel in an image and apply such a map; for our selective color adjustor, it is the computational backend. To connect our notions of hubs and rotations to the linear maps used by '-mix_rgb', we engage Rodriques' Rotation Formula:
W is the 3x3 matrix of weights to be used by '-mix_rgb'. θ represents the angle of rotation and u represents the normalized color vector, that is, the given color vector c scaled by its length |c| so that the results, u, has a length of 1.0:
I is the 3x3 identity matrix. The cross product of u isand the tensor product of u is . This formulation is for right handed coordinate systems, with positive rotations being counterclockwise. Changing the sign of the second term converts the formular for left handed systems.
To produce the animation, we picked a color c and normalized it, precomputing the cross and tensor matrices. We then stepped through 360° with Rodriques Formula at 0.4° increments for thirty seconds of footage - 900 linear maps fed sequentially to -mix_rgb. Gory details can be found in these Python reference scripts, which depend on numpy, pycairo and pango. The latter two may be dropped if there is no need to render progress text (Degrees: xxxx).
Garry Osgood