Color similarity/distance in RGBA color space - similarity

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.

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.

"Straight" version of an image with alpha channel

So I'm working on a shader for the upcoming CSS shader spec. I’m building something specifically targeted toward professional video product, and I need to separate out the alpha channel (as luminance, which I’ve done successfully), and a “straight” version of the image, which has no alpha channel.
Example: https://dl.dropbox.com/u/4031469/shadertest.html (only works with fancy adobe webkit browser)
I’m so close, just trying to figure out the last shader.
Here’s an example of what I’d expect to see. (This is from a Targa file)
https://dl.dropbox.com/u/4031469/Randalls%20Mess.png – the fill (what I haven’t figured out)
https://dl.dropbox.com/u/4031469/Randalls%20Mess%20Alpha.png – the key (aka alpha which I have figured out)
(The final, in case you're curious: https://dl.dropbox.com/u/4031469/final.png )
I thought it'd be a matrix transform, but I'm thinking now that i've tried more and more, it's going to be something more complex than a matrix transform. Am I sadly correct? And if so, how would I even get started attacking this problem?
In your shader, I presume you have some piece of code that samples the textures similar to the following, yes?
vec4 textureColor = texture2D(texture1, texCoord);
textureColor at that point contains 4 values: the Red, Green, Blue, and Alpha channels, each ranging from 0 to 1. You can access each of these colors separately:
float red = textureColor.r;
float alpha = textureColor.a;
or by using a technique known as "swizzling" you can access them in sets:
vec3 colorChannels = textureColor.rgb;
vec2 alphaAndBlue = textureColor.ab;
The color values that you get out of this should not be premultipied, so the alpha won't have any effect unless you want it to.
It's actually a very common to use this to do things like packing the specular level for a texture into the alpha channel of the diffuse texture:
float specularLevel = textureColor.a;
float lightValue = lightFactor + (specularFactor * specularLevel); // Lighting factors calculated from normals
gl_FragColor = vec4(textureColor.rgb * lightValue, 1.0); // 1.0 gives us a constant alpha
Given the flexibility of shaders any number of effects are possible that use and abuse various combinations of color channels, and as such it's difficult to say the exact algorithm you'll need. Hopefully that gives you an idea of how to work with the color channels separately, though.
Apparently, according to one of the adobe guys, this is not possible in CSS shader language since the matrix transform is only able to transform existing values, and not add a 'bias' vector.
The alternative, which I'm exploring now, is to use SVG filters.
SVG filters are now the way to pull this off in Chrome.
https://dl.dropbox.com/u/4031469/alphaCanvases.html
It's still early though, and CSS animations are only supported in the Canary build currently.

Simulating GDI+ gamma correction in QT

I need to implement some GDI+ functionality in QT, particularly, a LinearGradientBrush. The only method that I have troubles with is SetGammaCorrection. I found a topic that mentioned that MSDN has a pretty thorough description of GDI+ gamma correction algorithm, but I couldn't find it.
I tried to simulate gamma correction as follows:
1) Suppose we have a simple LinearGradientBrush with two-color interpolation. Divide the interval between these two colors into a predefined number of points (100) with equal distance between each point.
2) Assign a value to each point. First point will have a value of 0, second--0.01, ..., the last point will have a value of 1.
3) Calculate an interpolated color value in each point:
current_color = start_color * (1 - current_point_value) + end_color * current_point_value;
Start color and end color are the gradient boundary colors, if it wasn't clear enough.
4) Perform actual gamma correction on each calculated color value (except the two boundary colors):
gamma_corrected_color_value = color_value ^ (1 / gamma);
The value of gamma is 2.2.
Then I take the QLinearGradient, make an array of gradient stops with calculated colors and their positions (point values), assign those stops to the gradient and finally create a QBrush with this gradient.
Now if I fill a rectangle with this brush, I get a result that is pretty close to the result of actual GDI+ LinearGradientBrush, but they are not the same. I have tried a different combinations of gamma values and number of segments, but I didn't manage to get almost equal gradients.
Does anyone know how the gamma correction is implemented in GDI+ or how to simulate it in QT?
Thanks, Tony.
Qt gradients are linear, gamma is non-linear. Looks like you're going to have to regenerate the gradient whenever the gamma changes -- as opposed to having the gamma be a parameter of the gradient.

Volume rendering: confusion with front-to-back compositing

In, for example, GPU Gems the front-to-back compositing equation (for colour) is
C'i = (1 - A'i-1)Ci + C'i-1
where C'i is the output accumulated colour value; A'i-1 is the accumulated alpha (opacity) value up to the previous voxel; Ci is the colour value of the current voxel; and C'i-1 is the accumulated colour value up to the previous voxel.
This formulation raises two questions to me:
Termination of front-to-back occurs once the accumulated opacity reaches approximately 1. What, then, to do about the colour channels (RGB) that go past the maximum before the opacity limit is reached? Do you just clamp the values between 0..255 (e.g. 500,1000,2000 would become 255,255,255), or look to the ratio between the channels (e.g. 500,1000,2000 would become 64,128,255).
The answer to the previous question possibly feeds into this. The colour output of the current voxel depends on one minus the accumulated opacity. What if the accumulated opacity is zero and the current voxel's opacity is zero? - the output is a completely opaque voxel, since (1 - A'i-1) = 1, even though it is supposedly a transparent voxel!?
Any hints much appreciated.
A and C should be in the range 0-1. (If you're using unsigned bytes as the representation, divide by 255.0, but note that for some volume rendering application areas this will give you insufficient control over small alpha/low opacity regions to really be satisfactory. These days it's generally just easier to compute using floats from the outset). It turns out that the alpha and color values can never escape outside this range using the your formulas.
The sequence for the ray alpha A' is A'(i) = (1-A'(i-1)).A(i) + A'(i-1) (where A(i) is the voxel alpha), so if your accumulated ray starts with A' zero, and passes through a transparent (zero A) voxel, the ray now has A' = (1-0)*0+0, which is still zero as expected.
A and C should be between 0 and 1. Use pre-multiplied alpha; you will have no overflow issues.
Hm, let's belive that C and A are between 0 and 1. as one can see sequense C'(i) = [1 - A'_(i+1)]C(i) + C'(i-1) is grows with grow of i. I think C is not color (of RGB or whatever model). Maybe it's 'greyness' of some voxel. I.e. if voxel has many voxels in front of it it should be more grey then top voxels.
So my assumption is that C_i does not describe color dirrectly. It tells us how grey we should make color of some voxel.
forgive me for my poor English and fill free to re-ask if something is not clear.
BTW: if you belive me C_0 (greyness of top voxel) should be 1, and A_0 should be 0.

In CSS, can HSL values be floats?

The CSS3 spec only specifies that:
The format of an HSLA color value in the functional notation is ‘hsla(’ followed by the hue in degrees, saturation and lightness as a percentage, and an , followed by ‘)’.
So am I to understand that these values would be interpreted not as integers but as floats? Example:
hsla(200.2, 90.5%, 10.2%, .2)
That would dramatically expand the otherwise small (relative to RGB) range of colors covered by HSL.
It seems to render fine in Chrome, though I don't know if they simply parse it as an INT value or what.
HSL values are converted to hexadecimal RGB values before they are handed off to the system. It's up to the device to clip any resulting RGB value that is outside the "device gamut" - the range of colors that can be displayed - to a displayable value. RGB values are denoted in Hexadecimal. This is the specified algorithm for browsers to convert HSL values to RGB values. Rounding behavior is not specified by the standard - and there are multiple ways of doing rounding since there doesn't appear to be a built-in rounding function in either C or C++.
HOW TO RETURN hsl.to.rgb(h, s, l):
SELECT:
l<=0.5: PUT l*(s+1) IN m2
ELSE: PUT l+s-l*s IN m2
PUT l*2-m2 IN m1
PUT hue.to.rgb(m1, m2, h+1/3) IN r
PUT hue.to.rgb(m1, m2, h ) IN g
PUT hue.to.rgb(m1, m2, h-1/3) IN b
RETURN (r, g, b)
From the proposed recommendation
In other words, you should be able to represent the exact same range of colors in HSLA as you can represent in RGB using fractional values for HSLA.
AFAIK, every browser casts them to INTs. Maybe. If I'm wrong you won't be able to tell the difference anyway. If it really matters, why not just go take screenshots an open them in photoshop or use an on-screen color meter. Nobody here is going to have a definitive answer without testing it, and it takes 2 minutes to test... so...
I wouldn't know exactly, but it makes sense to just put in some floating numbers and see if it works? it takes two seconds to try with a decimal, and without..

Resources