Understanding color matrix of hue - math

This is a color matrix in ARGB:
[
R,0,0,0,0,
0,G,0,0,0,
0,0,B,0,0,
0,0,0,A,0,
0,0,0,0,1
]
And I know its hue color matrix below:
(
refers to gskinner's source code:
http://gskinner.com/blog/archives/2007/12/colormatrix_cla.html
)
define lr=0.213
define lg=0.715
define lb=0.072
define a=0.143
define b=0.140
define c=-0.283
define hueangle
define cos=cos(hueangle)
define sin=sin(hueangle)
[
lr+cos*(1-lr)+sin*(-lr),lg+cos*(-lg) +sin*(-lg),lb+cos(-lb) +sin*(1-lb),0,0,
lr+cos*(-lr)+ sin*(a),lg+cos*(1-lg)+sin*(b),lb+cos(-lb) +sin*(c),0,0,
lr+cos*(-lr)+ sin*(-(1-lr)),lg+cos*(-lg) +sin*(lg) ,lb+cos(1-lb)+sin*(lb),0,0,
0,0,0,1,0,
0,0,0,0,1
]
(Thanks son_of_fire for pointing out the inexplicitness of the questions in the last edition.)
Question
What formulas led to this hue matrix especially the numbers a,b and c in it?

lr, lg, and lb are the luminance constants. If you compute the dot product of <R, G, B> with <lr, lg, lb> you'll get the luminance of the color. This is useful if you want to change the hue and/or saturation without changing the luminance of the color.
I don't recognize a, b, and c off the top of my head. They're probably chrominance (saturation)-related.
The hueangle is the angle you want to rotate your color. It rotates around the R=G=B axis. If you rotate 180° red becomes cyan, green becomes magenta, yellow becomes blue, etc.
You might find the Gamma FAQ useful, as it describes a lot of these concepts. And this page describes the math behind rotating around an arbitrary axis.
Also, you generally want a 4x4 matrix for color conversion, not 5x5.

Related

Domain coloring (color wheel) plots of complex functions in Octave (Matlab)

I understand that domain or color wheel plotting is typical for complex functions.
Incredibly, I can't find a million + returns on a web search to easily allow me to reproduce some piece of art as this one in Wikipedia:
There is this online resource that reproduces plots with zeros in black - not bad at all... However, I'd like to ask for some simple annotated code in Octave to produce color plots of functions of complex numbers.
Here is an example:
I see here code to plot a complex function. However, it uses a different technique with the height representing the Re part of the image of the function, and the color representing the imaginary part:
Peter Kovesi has some fantastic color maps. He provides a MATLAB function, called colorcet, that we can use here to get the cyclic color map we need to represent the phase. Download this function before running the code below.
Let's start with creating a complex-valued test function f, where the magnitude increases from the center, and the phase is equal to the angle around the center. Much like the example you show:
% A test function
[xx,yy] = meshgrid(-128:128,-128:128);
z = xx + yy*1i;
f = z;
Next, we'll get its phase, convert it into an index into the colorcet C2 color map (which is cyclic), and finally reshape that back into the original function's shape. out here has 3 dimensions, the first two are the original dimensions, and the last one is RGB. imshow shows such a 3D matrix as a color image.
% Create a color image according to phase
cm = colorcet('C2');
phase = floor((angle(f) + pi) * ((size(cm,1)-1e-6) / (2*pi))) + 1;
out = cm(phase,:);
out = reshape(out,[size(f),3]);
The last part is to modulate the intensity of these colors using the magnitude of f. To make the discontinuities at powers of two, we take the base 2 logarithm, apply the modulo operation, and compute the power of two again. A simple multiplication with out decreases the intensity of the color where necessary:
% Compute the intensity, with discontinuities for |f|=2^n
magnitude = 0.5 * 2.^mod(log2(abs(f)),1);
out = out .* magnitude;
That last multiplication works in Octave and in the later versions of MATLAB. For older versions of MATLAB you need to use bsxfun instead:
out = bsxfun(#times,out,magnitude);
Finally, display using imshow:
% Display
imshow(out)
Note that the colors here are more muted than in your example. The colorcet color maps are perceptually uniform. That means that the same change in angle leads to the same perceptual change in color. In the example you posted, for example yellow is a very narrow, bright band. Such a band leads to false highlighting of certain features in the function, which might not be relevant at all. Perceptually uniform color maps are very important for proper interpretation of the data. Note also that this particular color map has easily-named colors (purple, blue, green, yellow) in the four cardinal directions. A purely real value is green (positive) or purple (negative), and a purely imaginary value is blue (positive) or yellow (negative).
There is also a great online tool made by Juan Carlos Ponce Campuzano for color wheel plotting.
In my experience it is much easier to use than the Octave solution. The downside is that you cannot use perceptually uniform coloring.

How to calculate RGB values with identical perceived brightness

I'm trying to generate RGB colors with the same perceived brightness.
The function R*0.2126+ G*0.7152+ B*0.0722 is said to calculate the perceived brightness (or equivalent grayscale color) for a given an RGB color.
Assuming we use the interval [0,1] for all RGB values, we can calculate the following:
yellow = RGB(1,1,0) => brightness=0.9278
blue = RGB(0,0,1) => brightness=0.0722
So, in order to make the yellow tone just as dim as the blue one i can simply perform this simple calculation on yellow for each of the RGB components:
dim_yellow = yellow * 0.0722 / 0.9278
However, when doing the opposite thing, thus "scaling" up the blue color to the same perceived brightness as the original yellow, the B component obviously exceeds 1, which cannot be displayed on a computer screen.
I guess the missing brightness from the excess B component could be "redistributed" to the R and G components, faking a brighter blue color. So what is the best general method to calculate those final RGB values?
THESE AREN'T THE MATHS YOU'RE LOOKING FOR
The function R*0.2126+ G*0.7152+ B*0.0722 is said to calculate the perceived brightness (or equivalent grayscale color) for a given an RGB color.
No this is incorrect, or at least incomplete. Yes, R*0.2126+ G*0.7152+ B*0.0722 are the spectral coefficients, but that is not the complete story.
First, Don't use the term brightness in this context. Brightness is not a measure of light, it is a perception, not a measurable quantity. When we are talking about light and colorimetry, use the term "luminance" (L or Y). Luminance is a linear measure of light, not perception.
Perceptual lightness, or L* (Lstar) from CIELAB, is based on human perception of changes in luminance. It is close to a power curve of about 0.43.
sRGB, the colorspace typically used for computer monitors and the web, is not linear like light, and it is also not exactly like the perceptual L* curve. sRGB's transfer curve is close to a 1/2.2 power curve. That is, the sRGB data/signal is raised to the power of 0.455, and then the monitor applies a power of 2.2.
WHAT'S BROKEN
Your math isn't working because you are not taking the transfer curves into account. You must linearize the sRGB values before applying the coefficients. Then the sum of these will equal a luminance of 1.
#FFFF00 in sRGB equals 0.9278 in luminance, but this is an sRGB value of 96.76% or an L* value of 97.14%
#0000FF in sRGB equals 0.0722 in luminance, but this is an sRGB value of 29.79% or an L* value of 32.3%
Here's a chart of some values, expanding on your example:
So to answer the rest of your question, to get a blue that matches a higher luminance than the monitor is capable of requires desaturating it, adding R and G to increase the lightness.
In this chart, we have the fully saturated but darker red and green to match the 7% blue luminance, then we have 18% luminance (as in an 18% grey card), and here we have to desaturate the blue to bring the luminance value up.
HOW TO CALC
First, you need to linearize the sRGB components, and THEN apply the coefficients, if you need to determine luminance. If you come up with some values doing math on linearized components, then you need to re-gamma encode to get back to sRGB.
I've discussed this is several other answers, such as this here.
I recommend you to use HSV color model instead of RGB since you can easily achive what you want only modifying Value(Brightness) component.
The wiki page also contains how to convert RGB to HSV and back
EDIT:
Try to use CIELAB color space since it approximate human's vision

How to replicate adding/mixing of HSV values in RGB space

At the moment I'm doing a colourizing effect using additive blending in HSV space. Have a diff value in HSV space which is added to an image texture's individual pixels to get the desired color effect. But this is turning out to be expensive as the fragment shader has to do two costly conversions to do the addition
RGB -> HSV
HSV addition
HSV -> RGB
Is there a better way to do this? The diff value will be provided in HSV only. And the final color representation is in RGB to draw.
Many Thanks,
Sak
You can get a similar effect to HSV manipulations by using a color matrix in RGB. For example, a rotation around the r=g=b axis is similar to a hue addition. (Adding x degrees in the hue channel is similar to a rotation of x degrees around r=g=b in RGB.) A translation along the r=g=b axis is similar to a value addition. (I believe that adding x to the value channel should be similar to adding x to all of r, g, and b.) And a uniform scale perpendicular to the r=g=b axis is similar to a saturation addition. I don't know off the top of my head the exact translation between adding x to saturation and scaling in RGB, but it shouldn't be too hard to work out. You should be able to compose these matrixes into a single matrix, and implement it as a single matrix multiply by the RGB value.

How to smoothly interpolate color along a Java2D path?

I'm from a 3D rendering background where this is trivial to do, but I can't find how to do this in Java2D:
Say I have points A, B and colors cA, cB.
The points are joined together by a quadTo() command and stroked. I want the colors to smoothly interpolate between each point. i.e. point A is color cA, point B is color cB, and the color of the line between A and B linearly interpolates between cA and cB.
I've tried stroking with a gradient fill but for my purposes it is far too slow, and also produces inaccurate results since I am actually joining up hundreds of these curve segments into continuous paths (using GeneralPath).
Is good 'ole point to point linear color interpolation not supported in Java2D?
The answer is: you can't do it. Java2D doesn't support this operation.

Color similarity/distance in RGBA color space

How to compute similarity between two colors in RGBA color space? (where the background color is unknown of course)
I need to remap an RGBA image to a palette of RGBA colors by finding the best palette entry for each pixel in the image*.
In the RGB color space the most similar color can be assumed to be the one with the smallest euclidean distance. However, this approach doesn't work in RGBA, e.g., Euclidean distance from rgba(0,0,0,0) to rgba(0,0,0,50%) is smaller than to rgba(100%,100%,100%,1%), but the latter looks much better.
I'm using premultiplied RGBA color space:
r = r×a
g = g×a
b = b×a
and I've tried this formula (edit: See the answer below for better formula):
Δr² + Δg² + Δb² + 3 × Δa²
but it doesn't look optimal — in images with semitransparent gradients it finds wrong colors that cause discontinuities/sharp edges. Linear proportions between opaque colors and alpha seem fishy.
What's the optimal formula?
*) for simplicity of this question I'm ignoring error diffusion, gamma and psychovisual color spaces.
Slightly related: if you want to find nearest color in this non-Euclidean RGBA space, vp-trees are the best.
Finally, I've found it! After thorough testing and experimentation my conclusions are:
The correct way is to calculate maximum possible difference between the two colors.
Formulas with any kind of estimated average/typical difference had room for discontinuities.
I was unable to find a working formula that calculates the distance without blending RGBA colors with some backgrounds.
There is no need to take every possible background color into account. It can be simplified down to blending maximum and minimum separately for each of R/G/B channels:
blend the channel in both colors with channel=0 as the background, measure squared difference
blend the channel in both colors with channel=max as the background, measure squared difference
take higher of the two.
Fortunately blending with "white" and "black" is trivial when you use premultiplied alpha.
The complete formula for premultiplied alpha color space is:
rgb *= a // colors must be premultiplied
max((r₁-r₂)², (r₁-r₂ - a₁+a₂)²) +
max((g₁-g₂)², (g₁-g₂ - a₁+a₂)²) +
max((b₁-b₂)², (b₁-b₂ - a₁+a₂)²)
C Source including SSE2 implementation.
Several principles:
When two colors have same alpha, rgbaDistance = rgbDistance * ( alpha / 255). Compatible with RGB color distance algorithm when both alpha are 255.
All Colors with very low alpha are similar.
The rgbaDistance between two colors with same RGB is linearly dependent on delta Alpha.
double DistanceSquared(Color a, Color b)
{
int deltaR = a.R - b.R;
int deltaG = a.G - b.G;
int deltaB = a.B - b.B;
int deltaAlpha = a.A - b.A;
double rgbDistanceSquared = (deltaR * deltaR + deltaG * deltaG + deltaB * deltaB) / 3.0;
return deltaAlpha * deltaAlpha / 2.0 + rgbDistanceSquared * a.A * b.A / (255 * 255);
}
My idea is integrating once over all possible background colors and averaging the square error.
i.e. for each component calculate(using red channel as example here)
Integral from 0 to 1 ((r1*a1+rB*(1-a1))-(r2*a2+rB*(1-a2)))^2*drB
which if I calculated correctly evaluates to:
dA=a1-a2
dRA=r1*a1-r2*a2
errorR=dRA^2+dA*dRA+dA^2/3
And then sum these over R, G and B.
First of all, a very interesting problem :)
I don't have a full solution (at least not yet), but there are 2 obvious extreme cases we should consider:
When Δa==0 the problem is similiar to RGB space
When Δa==1 the problem is only on the alpha 1-dim space
So the formula (which is very similar to the one you stated) that would satisfy that is:
(Δr² + Δg² + Δb²) × (1-(1-Δa)²) + Δa² or (Δr² + Δg² + Δb²) × (1-Δa²) + Δa²
In any case, it would probably be something like (Δr² + Δg² + Δb²) × f(Δa) + Δa²
If I were you, I would try to simulate it with various RGBA pairs and various background colors to find the best f(Δa) function. Not very mathematic, but will give you a close enough answer
I've never done it, but theory and practice say that converting the RGB values in the image and the palette to luminance–chrominance will help you find the best matches. I'd leave the alpha channel alone, as transparency should have little to nothing to do with the 'looking better' part.
This xmass I made some photomosaics for presents using open-source software that matches fragments of the original image to a collection of images. That seems like a harder problem than the one you're trying to solve. One of them programs was metapixel.
Lastly, the best option should be to use an existing library to convert the image to a format, like PNG, in which you can control the palette.

Resources