对于一个小项目,我需要将一个图像与另一个图像进行比较 - 以确定图像是否大致相同。图像很小,从25到100px不等。图像意味着具有相同的图像数据但是非常不同,因此简单的像素相等检查将不起作用。考虑以下两种可能的情况:
我决定使用直方图来表示每个图像,使用三个1D直方图:每个RGB通道一个 - 我可以安全地使用颜色并忽略纹理和边缘直方图(另一种方法使用单个3D直方图每个图像,但我避免这样,因为它增加了额外的复杂性)。因此,我需要比较直方图,看看它们有多相似,如果相似性度量超过某个阈值,那么我可以放心地说各个图像在视觉上是相同的 - 我将比较每个图像的相应通道直方图(例如图像1的红色直方图带有图像2的红色直方图,然后是图像1的蓝色直方图和图像2的蓝色直方图,然后是绿色直方图 - 所以我不是将图像1的红色直方图与图像2的蓝色直方图进行比较,这只是愚蠢的)。
假设我有这三个直方图,它们代表三个图像的红色RGB通道的摘要(为简单起见,使用5个像素的7个像素图像):
H1 H2 H3
X X X
X X X X X
X X X X X X X X X X X X X
0 1 2 3 4 0 1 2 3 4 0 1 2 3 4
H1 = [ 1, 3, 0, 2, 1 ]
H2 = [ 3, 1, 0, 1, 2 ]
H3 = [ 1, 1, 1, 1, 3 ]
图像1(H1
)是我的参考图像,我想看看图像2(H2
)和/或图像3(H3
)是否与图像1类似。请注意,在此示例中,图像2类似于图像1,但图像3不是。
当我粗略搜索“直方图差异”算法(至少是我能理解的那些)时,我发现一种流行的方法是只计算每个bin之间的差异,但是这种方法通常会失败,因为它会权衡所有bin差异相同。
为了演示这种方法的问题,在C#代码中,如下所示:
Int32[] image1RedHistogram = new Int32[] { 1, 3, 0, 2, 1 };
Int32[] image2RedHistogram = new Int32[] { 3, 2, 0, 1, 2 };
Int32[] image3RedHistogram = new Int32[] { 1, 1, 1, 1, 3 };
Int32 GetDifference(Int32[] x, Int32[] y) {
Int32 sumOfDifference = 0;
for( int i = 0; i < x.Length; i++ ) {
sumOfDifference += Math.Abs( x[i] - y[i] );
}
return sumOfDifferences;
}
其输出为:
GetDifference( image1RedHistogram, image2RedHistogram ) == 6
GetDifference( image1RedHistogram, image3RedHistogram ) == 6
这是不正确的。
有没有办法确定两个直方图之间的差异,考虑到分布的形状?
答案 0 :(得分:73)
比较直方图本身就是一个主题。
你有两大类比较函数:bin-to-bin比较和cross-bin比较。
H1.red[0] = 0.001 and H2.red[0] = 0.011
比H1.red[0] = 0.1 and H2.red[0] = 0.11
重要得多,即使在|H1.red[0] - H2.red[0]| = 0.01
两种情况下也是如此。< / LI>
M(i,j)
是区间i和j之间的相似性。假设bin[i]
为红色。如果bin[j]
为深红色,则M(i,j)
很大。如果bin[j]
为绿色,则M(i,j)
很小。然后,直方图H1和H2之间的距离将是sqrt((H1-H2)*M*(H1-H2))
。这种方法考虑了你所说的“关闭”垃圾箱! 地球移动距离(EMD)是另一种跨行距离。要完成,我有三点:
M = P'*D*P
,其中P'
是P
的转置,那么sqrt((H1-H2)'*M*(H1-H2)) = sqrt((H1-H2)'*P'*D*P*(H1-H2)) = sqrt((P(H1-H2))'*D*(P(H1-H2)))
。根据计算P(H1-H2)
的微不足道,这可以节省您的计算时间。直观地,如果H1
是您的原始直方图,P*H1
是一个软分配,您使用隐式相似性矩阵M = P'*Id*P
答案 1 :(得分:23)
我很惊讶没有人提到直方图比较的opencv实现,并且可以轻松处理不同格式的多通道图像(灰度,rgb,rgba等)(uchar,float,double等)
包括Bhattacharyya距离,卡方,相关和交叉方法。你可以找到
compareHist(InputArray H1, InputArray H2, int method)
手册here中的功能。
答案 2 :(得分:14)
Earth Mover的距离(EMD)通常用于此类直方图比较。 EMD使用一个值来定义从直方图的一个区域到另一个区域“移动”像素的成本,并提供将特定直方图转换为目标直方图的总成本。垃圾箱越远,成本越高。
在您的示例中,将5个单位从红色[0]移动到红色1将花费(c*1*5)
,而将5个单位从红色[0]移动到红色[10]将花费(c*10*5)
。
有几种实现方式。 FastEMD包含C ++,Java和Matlab中的代码。我相信OpenCV也有一些支持。
使用这种技术发表的大量论文用于大型图像数据库相似性搜索。
答案 3 :(得分:6)
我发现比较直方图时,卡方检验是一个很好的起点。如果在每个直方图中没有相同数量的条目,则必须更加小心,因为您不能使用“正常”表达式。从记忆中,如果你假设直方图具有不等数量的条目,则卡方检验推广到
1 /(MN)SUM_i [((Mni - Nmi)^ 2)/(mi + ni)]。
M和N是每个直方图中条目的总数,mi是直方图M的bin i中的条目数,ni是直方图N的bin i中的条目数。
另一项测试是Kolmogorov-Smirnov检验。该测试着眼于两个直方图的累积概率分布之间的最大差异。这很难实现,我认为C中的数字配方在C中有一个代码片段,我很确定它在Matlab中。如果您对直方图的形状更感兴趣而不是确切的值,这可能是一个更好的测试,也可以是非参数。
答案 4 :(得分:4)
你基本上想看probability distances。有很多,你必须决定哪个适合你的应用程序。最近,我对Chi-squared和Kullback-Leibler感到很幸运。
答案 5 :(得分:2)
通过将输入直方图中每个bin中的值除以直方图所基于的总像素数来标准化直方图。然后使用@tkerwin 's EMD。
答案 6 :(得分:0)
我认为EMD是解决使用bin to bin方法比较cross-bin问题的好方法。然而,正如一些人提到的,EMD很长时间。你可以向我建议一些其他方法来交叉吗?
答案 7 :(得分:0)
正如其他人所提到的,Earth Mover的距离或EMD(又名Wasserstein度量)可能是最佳解决方案。快速EMD计算的候选名单方法可在R包transport中找到。它在a paper from 2014中引入,将其与其他方法进行比较,显示出更快的计算时间。唯一的缺点是它在R中,除非用C ++编程,否则它不会很快。