Page 1 of 2

If color1 withing 'fuzz' of color2

Posted: 2010-12-02T19:17:25-07:00
by anthony
A challenge problem (looking for a simple solution)...

I have two colornames color1 and color2
Which could be ANY two colors in the colorname that IM generates, such as using...

Code: Select all

  identify -format '%[pixel:s]' image
Or in the identify color list, or in as a comment in a TXT: image.

I need to know if these two colors are within 'fuzz factor' of each other. that is do they 'match'.
Fuzz Factor - Matching Similar/Multiple Colors
http://www.imagemagick.org/Usage/color/#fuzz

I have been thru three solutions and all have been far to complex. What can you come up with?

Re: If color1 withing 'fuzz' of color2

Posted: 2010-12-02T19:23:14-07:00
by fmw42
Can you not just create two 1-pixel images of the two desired colors and use compare -metric rmse colorimage1 colorimage2 null:

The rmse error converted to percent via quantumrange should be pretty much how -fuzz calculates color difference in percent (though I have not looked at the code for -fuzz to see exactly how it does it)

rmse=sqrt(reddiff^2+greendiff^2+bluediff^2)

percent=rmse/quantumrange

Re: If color1 withing 'fuzz' of color2

Posted: 2010-12-02T20:57:36-07:00
by anthony
Yes nice idea too. And very exact.

For example

Code: Select all

   compare -metric RMSE xc:Navy xc:blue null:
18844.1 (0.287543)
However I specifically wanted to allow the user to specify it using a fuzz factor.

Also the above method also does not handle transparency properly. for example

Code: Select all

  compare -metric RMSE xc:None xc:'rgba(100%,100%,100%,0)' null:
56755 (0.866025)
This is a test of fully-transparent black vs fully-transparent white.

Not only that but fully-opaque black' is actually closer!

Code: Select all

  compare -metric RMSE xc:None xc:black null:
32767.5 (0.5)
The fuzz factor comparison routine in the library is designed to treat these two colors as equal (which to human eyes they are).

Re: If color1 withing 'fuzz' of color2

Posted: 2010-12-02T21:19:00-07:00
by fmw42
then what about doing your own -fx computation modifying the rmse to include the transparency channel

rmse=sqrt(reddiff^2+greendiff^2+bluediff^2+alphadiff^2)

where alphadiff is scaled to quantumrange

using -fx should not be that slow as it is only one pixel

Re: If color1 withing 'fuzz' of color2

Posted: 2010-12-02T22:08:00-07:00
by GreenKoopa
I'm not sure I fully understand or what format the answer should take.

Given two colors (named or otherwise), the fill color (white below) will be the result if they are within each other.

> convert -size 1x1 xc:red -fuzz 1% -fill white -opaque #e00 -format "%c" histogram:info:
1: (65535, 0, 0) #FFFF00000000 red

> convert -size 1x1 xc:red -fuzz 10% -fill white -opaque #e00 -format "%c" histogram:info:
1: (65535,65535,65535) #FFFFFFFFFFFF white

Re: If color1 withing 'fuzz' of color2

Posted: 2010-12-02T23:12:30-07:00
by GreenKoopa
You need a script to tell you if the two colors are within 'fuzz factor' of each other? Or you need to tell inside a -fx statement?

A more complex script, but a numerical output of the fuzzified color count:

outputs = Script(fuzz_distance, color_1, color_2)

> convert -size 1x1 xc:green xc:lime -append -fuzz 10% -fill green -opaque green -format "%k" info:
2

> convert -size 1x1 xc:green xc:lime -append -fuzz 40% -fill green -opaque green -format "%k" info:
1

Re: If color1 withing 'fuzz' of color2

Posted: 2010-12-02T23:40:42-07:00
by anthony
I need a script actually.

What if the original pixel was 'white' which is well outside the bounds and as such does not match.

However it does give me a solution, Use one of the colors as BOTH fill and the opaque color.

First convert the users input color to the color name IM would output for the match...

Code: Select all

    color2='#e00'
    color2=`convert xc:"$color2" -format '%[pixel:s]' info: `
The result was color2="rgb(238,0,0)"

Now do the test

Code: Select all

   color1=red
   fuzz=1%
   result=`convert  xc:"$color1"  -alpha set -channel RGBA -fuzz $fuzz \
                       -fill $color2 -opaque $color2 -format '%[pixel:s]' info:` 
now if $result = $color2 both strings generated using the same method, then you have a close match.
For fuzz = 1% result = red which does not equal color2 no near match
For fuzz = 10% result = rgb(238,0,0) which matches color2 so match.

I have checked and it works well for ANY color, transparent or otherwise.

The -alpha and -channel options are important for transparent colors,
for example #0002 verses #FFF1 which fuzzy match at about 8% as dose none and #FFF1

Now documented on
Quantization, Color Palette Handling, Comparing Two Colors
http://www.imagemagick.org/Usage/quantize/#compare

Actually Fuzzy matching is still showing mistakes.
It thinks '#0001' is much closer to 'none' than '#FFF1' when really they should be equidistant.
Though I think I know why that is the case.

Re: If color1 withing 'fuzz' of color2

Posted: 2010-12-03T00:02:54-07:00
by GreenKoopa
anthony wrote: What if the original pixel was 'white' which is well outside the bounds and as such does not match.

However it does give me a solution, Use one of the colors as BOTH fill and the opaque color.
Funny that my post lead me to the same wondering, and my second post an hour later lead me to the same solution.

Re: If color1 withing 'fuzz' of color2

Posted: 2010-12-03T00:13:27-07:00
by anthony
It was a lot better than my previous solutions.

one was using the -fuzz with convert and extracting the match from the output image, but the colors need to be thresholded to allow positive identification
see the last example on fuzzy matching in
http://www.imagemagick.org/Usage/compare/#compare

Unfortunately AE pixel error counts are not effected by fuzzy matching.
It would be nice is we could get a metric that would give a fuzzy match count.


The second was using a difference image of the two colors but then you still need to apply the fuzz factor to that result, making things again very messy, on what should be a quick and simple check.

Re: If color1 withing 'fuzz' of color2

Posted: 2010-12-03T12:45:53-07:00
by fmw42
Regarding your example at http://www.imagemagick.org/Usage/quantize/#compare

This tells me:

echo $(convert -size 1x1 xc:navy xc:blue -format "%[fx:(100)*sqrt(((u.r-v.r)^2+(u.g-v.g)^2+(u.b-v.b)^2+(u.a-v.a)^2)/3)]%%" info:) | cut -d" " -f1
28.7543%


which is what I think the -fuzz code seems to be computing (rmse color difference in % in RGB colorspace)

It seems to agree with your less than 30% fuzz and greater than 20% fuzz results. In fact, if I do:

convert xc:Navy -fuzz 29% -fill Blue -opaque Blue txt:
# ImageMagick pixel enumeration: 1,1,65535,rgb
0,0: ( 0, 0,65535) #00000000FFFF blue

convert xc:Navy -fuzz 28.5% -fill Blue -opaque Blue txt:
# ImageMagick pixel enumeration: 1,1,65535,rgb
0,0: ( 0, 0,32896) #000000008080 navy

So the fuzz value is indeed between 28.5% and 29%


By the way you could loop over your example changing the fuzz value until you converge upon some fuzz value within some tolerance if you want to go that far. It would be interesting to see how close you get to my figure above.

Re: If color1 withing 'fuzz' of color2

Posted: 2010-12-05T01:18:59-07:00
by anthony
Your comment about using FX calculation is only valid for images with fully opaque colors!

It will not give fully-transparent-white and fully-transparent-black a 0% fuzzy distance.

Currently color values are being multiplied by their alpha values to generate the distance (in a 3D sphere, 4D for CMYK)

The problem as I just discovered is that really it isn't a 3D sphere but a 4D cone (with a 3-D sphere at the top), with fully-transparent at the apex! But I have no idea how the formula for that should be worked out.

CMYK I think has it even harder in that it is really a 5D double cone (black=1 for any CMYK color is juts black). Calculating distances in multiple dimensions give me a headache!

Re: If color1 withing 'fuzz' of color2

Posted: 2010-12-05T12:50:14-07:00
by fmw42
Your comment about using FX calculation is only valid for images with fully opaque colors!

It will not give fully-transparent-white and fully-transparent-black a 0% fuzzy distance.
At least it gives you an exact and seemingly correct value for opaque colors. That is better than iterating with your other method.

Actually there was a mistake above.

Without transparency, it should be (rgb is normalized 3 dimensional euclidean distance - see http://en.wikipedia.org/wiki/Euclidean_distance)

%[fx:(100)*sqrt(((u.r-v.r)^2+(u.g-v.g)^2+(u.b-v.b)^2)/3)]%%" for rgb ( 3 rgb dimensions)


%[fx:(100)*sqrt(((u.c-v.c)^2+(u.m-v.m)^2+(u.y-v.y)^2)+(u.k-v.k)^2/4)]%%" (4 cmyk dimensions)

and with transparency perhaps just extend to 4 or 5 dimensions with the transparent as the next dimension, such as

%[fx:(100)*sqrt(((u.r-v.r)^2+(u.g-v.g)^2+(u.b-v.b)^2+(u.a-v.a)^2)/4)]%%"

So that for example with transparent navy and transparent blue one gets:

echo $(convert -size 1x1 xc:"rgb(0,0,128,0)" xc:"rgb(0,0,255,0)" -channel rgba -alpha on -format \
"%[fx:(100)*sqrt(((u.r-v.r)^2+(u.g-v.g)^2+(u.b-v.b)^2+(u.a-v.a)^2)/4)]%%" info:) |\
cut -d" " -f1
24.902%


or perhaps(?) multiply each rgb component by the alpha value before computing euclidean distance

(u.r*u.a-v.r*v.a)^2 etc

I don't think it is a cone (that is for HSL and HSB)!

Re: If color1 withing 'fuzz' of color2

Posted: 2010-12-05T14:36:52-07:00
by GreenKoopa
anthony wrote:Your comment about using FX calculation is only valid for images with fully opaque colors!

It will not give fully-transparent-white and fully-transparent-black a 0% fuzzy distance.
I am fascinated by this question but I'm way behind the two of you in understanding. Will you lay a simpler case for me? Say a world with one color channel and with transparency. What are the fuzzy distances between a few key points?
anthony wrote: The problem as I just discovered is that really it isn't a 3D sphere but a 4D cone (with a 3-D sphere at the top), with fully-transparent at the apex! But I have no idea how the formula for that should be worked out.
Do you mean that a 3D sphere and 4D cone are somehow combined to form this space? Or simply that a 4D cone is a series of shrinking 3D sphere cross-sections like a 3D cone is a series of shrinking 2D circle cross-sections?

Re: If color1 withing 'fuzz' of color2

Posted: 2010-12-05T14:43:31-07:00
by fmw42
I have my doubts about the use of a cone for rgb even with transparency.

My thoughts are that one just computes normalized euclidean distances. So that for rgb that would be

"%[fx:(100)*sqrt(((u.r-v.r)^2+(u.g-v.g)^2+(u.b-v.b)^2)/3)]%%" for rgb ( 3 rgb dimensions)

"%[fx:(100)*sqrt(((u.r-v.r)^2+(u.g-v.g)^2+(u.b-v.b)^2+(u.a-v.a)^2)/4)]%%" (4 rgba dimensions)

For one graylevel then it would be:

"%[fx:(100)*sqrt((u.g-v.g)^2)]%%"

For one graylevel and transparency:

"%[fx:(100)*sqrt(((u.g-v.g)^2+(u.a-v.a)^2)/2)]%%"

But I cannot guarantee that is how IM deals with transparency in doing its fuzz calculation.

Re: If color1 withing 'fuzz' of color2

Posted: 2010-12-05T14:58:28-07:00
by fmw42
Looking at


% IsColorSimilar() returns MagickTrue if the distance between two colors is
% less than the specified distance in a linear three dimensional color space.
% This method is used by ColorFloodFill() and other algorithms which
% compare two colors.


It appears to multiply color components by transparency. So it might be more like this:

For both transparent:
echo $(convert -size 1x1 xc:"rgba(0,0,128,0)" xc:"rgba(0,0,255,0)" -channel rgba -alpha on -format \
"%[fx:(100)*sqrt(((u.r*u.a-v.r*v.a)^2+(u.g*u.a-v.g*v.a)^2+(u.b*u.a-v.b*v.a)^2)/3)]%%" info:) |\
cut -d" " -f1
0%

and

For both with alpha=0.5
echo $(convert -size 1x1 xc:"rgba(0,0,128,0.5)" xc:"rgba(0,0,255,0.5)" -channel rgba -alpha on -format \
"%[fx:(100)*sqrt(((u.r*u.a-v.r*v.a)^2+(u.g*u.a-v.g*v.a)^2+(u.b*u.a-v.b*v.a)^2)/3)]%%" info:) |\
cut -d" " -f1
14.3772%