tags:

views:

669

answers:

8

I have a buffered image in java and I want to record how similar each pixel is to another based on the color value. so the pixels with 'similar' colors will have a higher similarity value. for example red and pink will have a similarity value 1000 but red and blue will have something like 300 or less.

how can I do this. when I get the RGB from a buffered Image pixel it returns a negative integer I am not sure how to implement this with that.

+5  A: 

First, how are you getting the integer value?

Once you get the RGB values, you could try

((r2 - r1)2 + (g2 - g1)2 + (b2 - b1)2)1/3

This would give you the distance in 3D space from the two points, each designated by (r1,g1,b1) and (r2,g2,b2).

Or there are more sophisticated ways using the HSV value of the color.

lavinio
I think you've left out the ^2 after (b2-b1); anyway, +1 because I was about to post the same
ammoQ
Is that really how you measure distance in 3d space? I would have thought it would involve a square root somewhere? if this works, then it could be used in 2d space, and you've just outsmarted pythagoras.
Breton
He's taking the cube root, which is incorrect; it should be the square root. But taking the root is unnecessary, since you can compare squares of distances as easily as the distances themselves, and save the time of taking the root.
Loadmaster
+2  A: 

The easiest is to convert both colours to HSV value and find the difference in H values. Minimal changes means the colours are similar. It's up to you to define a threshold though.

EmFi
+2  A: 

You're probably calling getRGB() on each pixel which is returning the color as 4 8 bits bytes, the high byte alpha, the next byte red, the next byte green, the next byte blue. You need to separate out the channels. Even then, color similarity in RGB space is not so great - you might get much better results using HSL or HSV space. See here for conversion code.

In other words:

int a = (argb >> 24) & 0xff;
int r = (argb >> 16) & 0xff;
int g = (argb >> 8) & 0xff;
int b = argb & 0xff;

I don't know the specific byte ordering in java buffered images, but I think that's right.

plinth
-1 for reccomending HSL or HSV. Those are ad hoc transformations that don't really mean anything in the real world. they were invented for early graphics programs. distance in L*a*b space is based on measurements from hundreds of experiments with real eyes and real colors.
Breton
+1  A: 

You could get the separate bytes as follows:

int rgb = bufferedImage.getRGB(x, y); // Returns by default ARGB.
int alpha = (rgb >>> 24) & 0xFF;
int red = (rgb >>> 16) & 0xFF;
int green = (rgb >>> 8) & 0xFF;
int blue = (rgb >>> 0) & 0xFF;
BalusC
+2  A: 

I suggest you start reading here

Color difference formulas if you want to do this right. It explains the ΔE*ab, ΔE*94, ΔE*00 and ΔE*CMC formulas for calculating color difference.

jitter
+1  A: 

I find HSL values easier to understand. HSL Color explains how they work and provides the conversion routines. Like the other answer you would need to determine what similiar means to you.

camickr
+2  A: 

HSL is a bad move. L*a*b is a color space designed to represent how color is actually percieved, and is based on data from hundreds of experiments involving people with real eyes looking at different colors and saying "I can tell the difference between those two. But not those two".

Distance in L*a*b space represents actual percieved distance according to the predictions derived from those experiments.

Once you convert into L*a*b you just need to measure linear distance in a 3d space.

Breton
+1 nice thought. More info: http://en.wikipedia.org/wiki/Lab_color_space
BalusC
A: 

This is a similar question to #1634206.

If you're looking for the distance in RGB space, the Euclidean distance will work, assuming you treat red, green, and blue values all equally.

If you want to weight them differently, as is commonly done when converting color/RGB to grayscale, you need to weight each component by a different amount. For example, using the popular conversion from RGB to grayscale of 30% red + 59% green + 11% blue:

d2 = (30*(r1-r2))**2 + (59*(g1-g2))**2 + (11*(b1-b2))**2;

The smaller the value of d2, the closer the colors (r1,g1,b1)and(r2,g2,b2) are to each other.

But there are other color spaces to choose from than just RGB, which may be better suited to your problem.

Loadmaster