I've an application that indexes the top 16 colors that appear in videos.
I'm trying to write another application that allows the user to select a color and then the application finds all videos that this color appears in.
The problem is that as I only index 16 colors per video, the users choose an RGB color. The probability that this color is indexed is very low, so almost always my application returns no results.
I thought of a way I could make this work - I could index the colors that appear in the video and convert them to closest 8-bit color.
Then when a user selects an RGB color, I could convert the user choice to the same 8-bit closest color.
This way I'd always have matches.
The only major problem I've right now is how to convert an RGB color to the closest 8 bit color?
To convert to the web-safe palette, you need to convert the range of each of the r,g,b components from 0-255 to 0-5 and combine them:
color = (r*6/256)*36 + (g*6/256)*6 + (b*6/256)
What you need to do is convert the RGB to an HSB (hue saturation brightness) value. HSB is 3 bytes, just like RGB, the difference is that HSB values can be compared much more easily than RGB.
Your next step is decide on an "importance" weighting. For example, if all you care about is "color/hue", not saturation or brightness, then you can throw away the S and B bytes and just use the color byte.
If it were me and I were constrained to 8 bits I would use 4 bits of color information (16 different colors), 3 bits of saturation (8 different values), and 1 bit of brightness information (light or dark).
This article describes how to do HSB in Java:
http://java.sys-con.com/node/43559
The source code for this article has an RGB to HSB converter in Java.
One possibility is to simply scale your 24-bit color down into an 8-bit color space. As cHao mentions, you could use RRRGGGBB for your 8-bit number. Then each color component can be calculated by a simple scaling algorithm such as:
byte red = (originalColor.red * 8) / 256;
byte green = (originalColor.green * 8) / 256;
byte blue = (originalColor.blue * 4) / 256;
The 8, 4, and 254 are the number of possible values in each color component. In your original 24-bit color, red, green, and blue can all have 256 possible values, so that is the divisor of the scaling equation. In the example 8-bit color, red and green are each 3 bits (8 possible values) and blue is 2 bits (4 possible values).
After you get these three components, you can combine them with some simple bit shift arithmetic:
byte eightBitColor = (red << 5) | (green << 2) | blue;
Then you can simply compare these 8-bit colors. Their drastically reduced resolution may help you.
Alternately, you can do something like Tyler suggested, and convert to HSB or HSV first, and only compare hues (depending on whether or not you need brightness and saturation information). Depending on your goal, that may actually be a more ideal solution.
Edit: Modified scaling algorithm to fix a shortcoming pointed out by Mark Ransom.
Are you familiar with Floyd–Steinberg dithering? This is used to convert higher order colors to lower order colors, e.g. 24 bit RGB to 3 bit (8 colors) RGB or restricting an RGB image to 8 bits (256 colors) for a GIF conversion.
This algorithm is described on the linked wikipedia page.
Try this algorithm if you want to convert
24 bpp image to 8 bpp image:
for y = 0 to ImageHeight - 1
for x = 0 to ImageWidth - 1
GetPixel(x,y,red,grn,blu)
{read RGB data from 24bpp file}
d0 = red^2 + grn^2 + blu^2
ColorIndex = 0
for cl = 0 to 255
GetPaletteData(p_red,p_gre,p_blu)
{read RGB data from 8bpp palette}
d = (red - p_red)^2 + (grn - p_grn)^2 + (blu - p_blu)^2
if d0 >= d then
ColorIndex = cl
d0 = d
end if
next cl
{use ColorIndex to create your 8bpp file}
next x
next y
Before this step, read more about 8bpp files from Wikipedia or other sources.
Good luck!