Given an RGB value what would be the best way to find the closest match in the database?
Solution 1:
Consider a color as a vector in 3-dimensional space, you can then easily compute the difference by using 3d pythagoras:
d = sqrt((r2-r1)^2 + (g2-g1)^2 + (b2-b1)^2)
However, note that due to colors being subject to interpretation by not-so-perfect eyes, you might want to adjust the colors to avoid them having the same importance.
For instance, using a typical weighted approach:
d = sqrt(((r2-r1)*0.3)^2 + ((g2-g1)*0.59)^2 + ((b2-b1)*0.11)^2)
Since eyes are most sensitive to green, and least sensitive to blue, two colors that differ only in the blue component must thus have a larger numeric difference to be considered "more different" than one that is the same numeric difference in the green component.
There's also various ways to optimize this calculation. For instance, since you're not really interested in the actual d
value, you can dispense with the square root:
d = ((r2-r1)*0.30)^2
+ ((g2-g1)*0.59)^2
+ ((b2-b1)*0.11)^2
Note here that in many C-syntax-based programming languages (like C#), ^
does not mean "raise to the power of", but rather "binary exclusive or".
So if this was C#, you would use Math.Pow
to calculate that part, or just expand and do the multiplication.
Added: Judging by the page on Color difference on Wikipedia, there's various standards that try to handle perceptual differences. For instance, the one called CIE94 uses a different formula, in the L*C*h
color model that looks like it's worth looking into, but it depends on how accurate you want it to be.
Solution 2:
The following does exactly what you describe:
select (abs(my_R - t.r) + abs(my_G - t.g) + abs(my_B - t.b)) / 3 as difference, t.*
from RGBtable t
order by difference desc;
However, you might get better results with something that was non-linear. In the "take the averages" approach, if you goal color is (25, 25, 25) the color (45, 25, 25) would be closer than (35, 35, 35). However, I bet the second would actually look closer, since it would also be gray.
A few ideas come to mind: you could try squaring the differences before you average them. Or you could do something complicated with finding the color with the closest ratio between the different values. Finding the closest ratios would get you closest to the right hue, but won't account for saturation (if I'm remembering the terms right...)
Solution 3:
The Euclidean distance difference = sqrt(sqr(red1 - red2) + sqr(green1 - green2) + sqr(blue1 - blue2))
is the standard way to determine the similarity of two colours.
However, if you have your colours in a simple list, then to find the nearest colour requires computing the distance from the new colour with every colour in the list. This is an O(n) operation.
The sqrt()
is an expensive operation, and if you're just comparing two distances then you can simply omit the sqrt()
.
If you have a very large palette of colours, it is potentially quicker to organise the colours into a kd tree (or one of the alternatives) so as to reduce the number of diffreences that require computing.