Compare RGB values



  • I can't figure out a good way of doing this, and decided to start here to prevent any WTFiness.

    I need to compare a given RGB value (0-255, 0-255, 0-255) to a LONG list (in a DB) of RGB values and determine which is closest.

    But it has to be closest on all 3 values to really get the closest color, and this has me stumped. Any ideas?



  • I would say the quickest way would be to use the distance formula.
    So you have your color Y(r,g,b) and the color in the database D(r,g,b). To obtain your closeness value calculate the distance between them

     Dist=sqrt( (Yr-Dr)^2 + (Yg-Dg)^2 + (Yb-Db)^2 )

    You could store with each color in the database the color's distance from the origin (0,0,0) for a faster search of the database colors.

    Thsi is no way an authoratative answer just an idea.



  • This sounds like one of those problems that someone, somewhere, must have written a Ph.D. thesis or journal article on.

    If "the closest color" means as seen by a human, well, good luck.  You might have to convert the colors to HSB coordinates (hue, saturation, brightness) and give them different weights.  Pastel pink (RGB 255, 188, 188, HSB 0, 26, 100) looks closer to red (RGB 255, 0, 0, HSB 0 ,100, 100) than brownish-yellow does (RGB 255, 188, 0, HSB 44, 100, 100).

    Look through the ACM SIGGRAPH publications for starters.

     



  • Old collision-detection trick: don't bother doing square roots.  If (d1 < d2), then (d1d1 < d2d2), so you can save huge amounts of time by just comparing the sum of the squares of the component differences.



  • You may want to obtain the source to any version of X11, and look at the code of the XLookupColor function.  It does pretty much the same thing:  given a requested color, it finds the closest match in a static colormap.  X11's source is freely available.  Since XLookupColor is a fairly old function, any version of X11 will do.

    As others have said, only someone versed in colorspace theory (like the guys who wrote XLookupColor, I'm guessing) would have an informed answer.



  • I would just like to reiterate the point that the closest RBG value will not necessarily look the closest.  For an example of this compare the how much different 0,200,0 to 0,250,0 is with the different of say 50,0,0

     

     

    NOTE:  I made up those numbers, but I know the top range of the grens starts getting really bright, something that is unique from red and blue values. 



  • [quote user="wscottp"]

    I can't figure out a good way of doing this, and decided to start here to prevent any WTFiness.

    I need to compare a given RGB value (0-255, 0-255, 0-255) to a LONG list (in a DB) of RGB values and determine which is closest.

    But it has to be closest on all 3 values to really get the closest color, and this has me stumped. Any ideas?

    [/quote]

     

    If you want closest NUMERIC rgb color value, that's simple. All we need to know if if the LONGS have the empty/unsused byte at the beginning of the word or the end.




  • With some simple first tries here, it seems that comparing them mathematically isn't too bad.

    It seems the best solution is to mathematically determine which x colors are closest, and let the user choose. A human sanity check I guess. Figuring the distance between the plotted colors is working well with the help of a human.

    My real solution might just be to buy something from http://www.easyrgb.com I found it was a great source of information (they have conversions for just about everything color related) then I found that they sell software that looks like it does exactly what I was asked to do :)



  • Sounds like you have several problems.  Problem 1:  what do you mean by 'close'?  It should probably mean Euclidean distance.

    Problem 2: How do you do this efficiently?  The overall problem here is the nearest neighbor search problem.  You can do better than exhaustive search by implementing a kd-tree, but that assumes that the given RGB value is variable while the long list is static (or mostly static).



  • Assuming SQL; a table with columns red, green, and blue; and input values of r, g, and b...

    If you want Euclidian distance:
    select red, green, blue from colors order by (red-r)(red-r)+(green-g)(green-g)+(blue-b)*(blue-b) limit 1;

    If you want Manhattan distance (extrapolated to three dimensions):
    select red, green, blue from colors order by abs(red-r)+abs(green-g)+abs(blue-b) limit 1;

    These will, of course, run through the whole table to try to find the answer, but there's not really a decent WHERE clause that would save you any calculation time, especially one that would also guarantee you a nonempty result set.

    If you're a real masochist, you can replace the multiplication in the Euclidian distance with pow(red-r, 2) and go through the agony of promotion to float and the time spent for the pow function.



  • [quote user="newfweiler"]If "the closest color" means as seen by a human, well, good luck.  You might have to convert the colors to HSB coordinates (hue, saturation, brightness) and give them different weights.  Pastel pink (RGB 255, 188, 188, HSB 0, 26, 100) looks closer to red (RGB 255, 0, 0, HSB 0 ,100, 100) than brownish-yellow does (RGB 255, 188, 0, HSB 44, 100, 100).

    Look through the ACM SIGGRAPH publications for starters.[/quote]

    Yeah, this is where I'd start playing ... Hue is generally the most important, but remember that hues 255 and 0 are very close (hue is cyclic) and the importance of hue is lower at lower saturation and furthest from brightness 128. (All blacks/whites/greys look the same, whatever colour black/white/grey they are!)

    I would convert the colour into HSB first, then into a 3-space where the H component was scaled by s*(b-0.5)² (assuming each component is 0.0-1.0), and use Euclidean distance on that 3-space.



  • [quote user="Bob Janova"]Hue is generally the most important, but remember that hues 255 and 0 are very close (hue is cyclic) and the importance of hue is lower at lower saturation and furthest from brightness 128.[/quote]

    You're right that hue becomes "less important" at lower saturations, but one should be aware that an n-bit RGB system's capability to express hues varies with saturation, and that there's no such thing as "Hue 0-255".

    At 100% saturation and 24-bit RGB (8+8+8) , there are 256*3*2 hues, which decreases as saturation does.

    What, exactly, constitutes "saturation" and "brightness" is debatable. Adobe uses the HSB model, while others use the HSL model (paint shop pro, Windows colour picker). Some programs take the middle ground and offer both. Some use a triangle for sat/bri, some use a square. Some use a circle for hue/sat, and a slider for bri (that's one method I find terribly useless).

    Distance in a normal RGB cube will get you mathematically close values, but if you consider two colours with a small hue change "far" and two colours with a large bri change "close", you're going to need a converter to HSB or HSL.

     



  • [quote user="dhromed"]there's no such thing as "Hue 0-255"[/quote]

    In the Windows colour model there certainly is, Hue Saturation and Brightness are all measured from 0-255.



  • [quote user="Bob Janova"][quote user="dhromed"]there's no such thing as "Hue 0-255"[/quote]

    In the Windows colour model there certainly is, Hue Saturation and Brightness are all measured from 0-255.

    [/quote]

    You're doing this on purpose, arentcha.



  • Euclidian distance as mentioned above is easy.  If you want a little more "accuracy" (using the term loosely) and don't want to go the HSB route, try just weighting your color components a bit before calculating the distance. Weight green the most, blue the least. 

    So instead of:    (R1 - R0)^2 + (G1 - G0)^2 + (B1 - B0)^2

    Try:    (a * (R1 - R0))^2 + (b * (G1 - G0))^2 + (c * (B1 - B0))^2   (where c <= a <= b)



  • @mattmoss said:

    Euclidian distance as mentioned above is easy.  If you want a little more "accuracy" (using the term loosely) and don't want to go the HSB route, try just weighting your color components a bit before calculating the distance. Weight green the most, blue the least. 

    So instead of:    (R1 - R0)^2 + (G1 - G0)^2 + (B1 - B0)^2

    Try:    (a * (R1 - R0))^2 + (b * (G1 - G0))^2 + (c * (B1 - B0))^2   (where c <= a <= b)

    With what factors, the regular greyscale weights (0.299, 0.587, 0.144)? I'm trying to use much the same formula myself to build color fading tables for a tiny 16-color palette, and I can't quite decide whether it looks better or not.

    Anyway, if the lookup is time critical and you can afford to waste some time on precalculations then how about storing a table of matching colors in indexed by RGB colors with reduced precision (with, say, 4 bits per component).

    In other words something like this:

    list lut[16][16][16];

    for(b = 0; b < 256; ++b)
     for(g = 0; g < 256; ++g)
      for(r = 0; r < 256; ++r)
       lut[b >> 16][g >> 16][r >> 16] += nearest(r, g, b);


Log in to reply