以编程方式寻找类似颜色

16

我有一个Java中的缓冲图像,并且我想记录每个像素与另一个像素相似程度基于颜色值。因此,'相似'颜色的像素将具有更高的相似性值。例如,红色和粉色将具有相似性值1000,但红色和蓝色将具有类似300或更低的数值。

如何实现这个功能?当我从Buffered Image像素中获取RGB时,它返回一个负整数,我不确定如何使用它来实现这一点。

12个回答

24

首先,你是如何获取整数值的?

一旦获取到RGB值,你可以尝试使用以下公式:

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

这将给出两个点在三维空间中的距离,每个点由(r1, g1, b1)和(r2, g2, b2)指定。

或者有更复杂的方法,使用颜色的HSV值。


我认为你在 (b2-b1) 后面漏掉了 ^2;不管怎样,我还是给你点赞,因为我也正要发同样的评论。 - Erich Kitzmueller
这就是你在三维空间中测量距离的方式吗?我本以为它会涉及到平方根呢?如果这个方法可行,那么它也可以用于二维空间,你已经比毕达哥拉斯更聪明了。 - Breton
8
他正在取立方根,这是错误的;正确应该是取平方根。但是,取根是不必要的,因为你可以像比较距离本身一样轻松地比较距离的平方,并且节省取根的时间。 - David R Tribble
这可以用来解决Robot.getPixelColor(int x, int y)在OSX上颜色匹配不一致的问题。 - Charles Mosndup

11

HSL是一个不好的选择。L*a*b是一个颜色空间,旨在表示颜色实际上是如何被感知的,并且基于数百个涉及真正有眼睛的人观察不同颜色并说“我可以区分那两个颜色,但不是这两个”的实验数据。

L*a*b空间中的距离表示根据这些实验导出的预测实际感知距离。

一旦你转换为L*a*b,你只需要在3D空间中测量线性距离。


9
我建议你从这里开始阅读:
如果你想要正确地做这件事,请参考颜色差异公式。它解释了计算颜色差异的ΔE*abΔE*94ΔE*00ΔE*CMC公式。

6
如果你要使用HSV,你需要意识到HSV不是三维空间中的点,而是锥体的角度、大小和距离。要计算HSV值的距离,你需要通过变换确定三维空间中的点。
X = Cos(H)*S*V
Y = Sin(H)*S*V
Z = V
对于这两个点,然后取它们之间的欧几里得距离。
Sqrt((X0 - X1)*(X0 - X1) + (Y0 - Y1)*(Y0 - Y1) + (Z0 - Z1)*(Z0 - Z1))

花费2 Cos、2 Sin和一个平方根。

或者,如果您愿意,您实际上可以更容易地计算距离,只需意识到当被压缩为2D空间时,您只需要从原点得到两个向量,并应用余弦定理来找到XY空间中的距离:

C² = A² + B² + 2*A*B*Cos(Theta)

A等于第一个值的S*V,B等于第二个值的S*V,cosine是theta或H0-H1的差。

然后您需要将Z因子考虑进去,以将2D空间扩展为3D空间。

A = S0*V0
B = S1*V1
dTheta = H1-H0
dZ = V0-V1
distance = sqrt(dZ*dZ + A*A + B*B + 2*A*B*Cos(dTheta);

请注意,由于余弦定理给出了C²,我们只需将其与Z的变化一起插入其中。这需要花费1 Cos和1 Sqrt。HSV非常有用,您只需要知道它描述的是哪种颜色空间。您不能只将它们放在欧几里得函数中,然后得到一个连贯的结果。

2
最简单的方法是将两个颜色转换为HSV值,并找到H值的差异。变化最小意味着颜色相似。不过,你需要自己定义一个阈值。

2
您可能正在对每个像素调用getRGB()方法,该方法将颜色作为4个8位字节返回,高字节为alpha通道,接下来的字节分别是红、绿、蓝三个通道。您需要将各个通道分离开来。即使如此,在RGB空间中颜色相似度也不太好 - 您可能使用HSL或HSV空间可以获得更好的结果。请参见这里的转换代码。
换句话说:
int a = (argb >> 24) & 0xff;
int r = (argb >> 16) & 0xff;
int g = (argb >> 8) & 0xff;
int b = argb & 0xff;

我不知道Java缓冲图像中的具体字节顺序,但我认为这是正确的。


1
不推荐使用HSL或HSV。这些都是为早期图形程序而发明的临时转换,实际上在现实世界中毫无意义。在Lab空间中的距离是基于数百个真实眼睛和真实颜色的实验测量得出的。 - Breton

2
您可以按以下方式获取单独的字节:
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;

1

我发现HSL值更容易理解。HSL Color解释了它们的工作原理并提供了转换例程。就像其他答案一样,您需要确定“相似”对您来说意味着什么。


1

关于这个问题,有一篇有趣的论文:

一种新的感知均匀颜色空间及其相关的基于内容的图像和视频检索的颜色相似度测量方法 作者:M. Sarifuddin 和 Rokia Missaoui

您可以在谷歌或者特别是[谷歌学术][1]上轻松找到它。

总结一下,一些颜色空间(例如RGB、HSV、Lab)和距离测量方法(如几何平均值和欧几里得距离)比其他方法更好地表示了人类对颜色相似性的感知。该论文介绍了一种新的颜色空间,它比其他颜色空间更好,但它也提供了常见现有颜色空间和距离测量方法的良好比较。从定性上来看,使用常见的颜色空间进行感知距离测量的最佳方法是:HSV颜色空间和圆柱形距离测量。

*至少根据参考论文中的第15张图表。

圆柱形距离测量公式(使用Latex符号)如下:

D_{cyl} = \sqrt{\Delta V^{2}+S_1^{2}+S_2^{2}-2S_1S_2cos(\Delta H)}


2
阅读了论文并实现了算法,基本上是垃圾。它并不可怕,但真的比 HSV 更好。C 和 L 变量的推导很好,但论文错误地列出了色相(如果按给定值使用,则绿色=红色)。图15的相似之处是由于有缺陷的方法学所致。它拒绝颜色,直到有足够数量来填充该区域。因此,任何颜色空间都将根据其被赋予的拒绝阈值进行评分,而不是其正确反映平均人类会评为最不同的颜色的能力。这甚至还不是全部错误。 - Tatarize

0

这是一个类似于#1634206的问题。

如果你想要在RGB空间中计算距离,欧几里得距离可以使用,假设你平等地对待红色、绿色和蓝色值。

如果你想要按不同的权重进行计算,通常在将颜色/RGB转换为灰度时会这样做,你需要按不同的比例给每个分量赋予不同的权重。例如,使用从RGB到灰度的流行转换方式:30%的红色+59%的绿色+11%的蓝色:

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

d2的值越小,颜色(r1,g1,b1)(r2,g2,b2)就越接近。

但是除了RGB之外,还有其他可供选择的颜色空间,可能更适合您的问题。


那里的权重是伽马校正权重。虽然蓝色(11)对人类亮度知觉的贡献不大,但它们确实在人类颜色距离确定中扮演了很大的区分作用。 http://www.compuphase.com/cmetric.htm权重2,4,3更适合所述目的。我将LAB(delta E)运行到每个颜色距离排列(2 ^ 24 * 2 ^ 24)中并对其求平均值。而权重为22,43,35更好(使用LAB代替人眼)。此外,我已经实现了加权欧几里得算法,它很糟糕! - Tatarize

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接