Closest color to...

I’ve struggled with colors in Xojo and RB since forever.

The big picture is , I work with images that can come from any source.
Open them in Xojo to work with.
Compare the pixel RGB values to a predefined list of colors.

eg these:
https://en.wikipedia.org/wiki/Web_colors
Either the X11 range or ‘web safe’ stuff

And I want to end up with a version of the image that only contains pixels of these websafe colors, and no others.
That means going from millions of colors down to maybe 256
Ive failed to find a decent quantisation method, and currently am using something from GraphicsMagic as a stogap.

Here are my problems: (they may all be the same thing)

a/ Colorspace (?) - I open an image in Xojo and just by looking at it, the colors dont match what I see in Preview or iPhoto
b/ Comparing… I have had a number of algorithms ove the years, but if I look at one pixel and get the RGB vales, then try to find the closest match in that list of colors, the one I end up with is usually darker than the original, or quite different, even though (to my eye) , better choices exist.
c/ Colors always seem darker using Xojo to manipulate them

eg I see a Bright Blue, but working on R,G,B comparisons, I end up ‘selecting’ something much darker blue.
Or getting a purple for a brown.

So:
Does anyone else have the same experience?
How would YOU go about finding the best match for one of these web colors from any random image?

I note in this article that the web safe colors were established due to monitors with a 16bit display.
I havent seen one of these for years, but still need to work the principle.
I dont think the root cause is a 16bit color display, but what is it that makes Xojo change the colors so?

I’ve seen Xojo display colors at the wrong value.
I’ve used g.fillrect with a specific color, and it showed up wrong, so I used an on screen color meter to find out Xojo was displaying it wrong. I had to guess and check the g.ForeColor until the built app would display the right color.

Not a smooth experience at all.

isn’t it a monitor profile problem ? or xojo that does not take the profile into account ?

Jeff, it would be nice if you posted in macOS or Windows instead of General, because what you describe varies between platforms.

On Mac, color profiles are applied to loaded pictures, resulting in colors that are not conformant to what is encoded in the picture file. The subject came up several times and never got any satisfactory answer.

I vaguely remember noticing that there was an Einhugur plugin that could load a picture without applying the color profile. Unfortunately, einhugur.com is down just now, so I could not look closer.

On Windows, quick tests seemed to indicate that colors in a loaded picture are identical to those encoded in the file, as shown by PhotoShop.

Websafe colors (216 palatte) is easy… since the RGB components must be 0x00, 0x33,0x66 , 0x99, or 0xFF

create a MAP

  For i=0 To 255
    map_websafe216(i)=round(i/&h33)*&h33 ' 216 color WEBSAFE
    map_websafe64(i)=round(i/&h55)*&h55 ' 64 color WEBSAFE
  next I

and use transform

myPicture.RGBSurface.Transform Map_WebSafe216

Thanks both.

Dave: Thats an interesting idea.
Unfortunately, in an attempt to make ‘the problem easy to understand’, I mentioned websafe color set as an example.
In reality the set of colors can vary, and can be more than 256 colors.

Michel: I think I have the problem on both platforms.

But I have two issues really:
One is ‘the color has changed before I even get a chance to look at a pixel’ , and I think you have a point about Mac and color profile.

The next is color matching
Given an array of colors, how best to find the closest for any given pixel?

In the extreme, if I compare R,G,B

My array has black, green, and white. 000000 00FF00 FFFFFF

The picture has a Blue pixel 0000FF

If I take red-red , green-green and blue - blue to see how much things vary, then in the list of 3 above, the white wins the prize, since R is 255 different, G is 255 different, and Blue is 0 different.

White isnt a good match but is the best I have.
Now thats very very extreme.

More ‘real world’ is when the sum of RedDiff,BlueDiff,GreenDiff comes to a smallish value, but a better color would have had an equal or higher value
So a brown may be closest numerically to a purple, even if I have some purples that the eye would class as closer to the color.

If there is an exact match for RGB, no problem.
When there is not, I suspect I need to be looking in a 3D Space.
Visuallised:

Try using Hue, Saturation and Value and convert them all into values between 0 and 1. I think you’ll have more success. When I do this, I average the results to get a single value.

You may want to add weights to them as well. For instance, if it’s more important that the color be similar than the value and saturation, beef that up a little.

HSV appears to give me awful results.
I need to do some more checking in case I’ve messed it up, but it seems to be very green skewed.

Can you show an example of the original, final and the palette you used?

I’ve spent a while looking at it, and Im now getting better results using a kind of 4 dimensional distance algorithm, taking

absolute distance from red-to-red, green-to-green,blue-to-blue, and also hue-to-hue (weighted a little for hue)

This seems to help… for example when a blue and a green are equi-distant from the target blueish color based on red,green,blue values alone, the hue swings it towards the better color choice.

I still need to look into the color profile aspect, and will probably see if I can get somewhere using GMImageMBS