比较两个直方图
对于一个小项目,我需要比较一个图像和另一个图像,以确定图像是否大致相同。 图像很小,从25到100px不等。 图像意味着是相同的图像数据,但sublty不同,所以一个简单的像素相等性检查将无法正常工作。 考虑这两种可能的情况:
- 一个博物馆里的安全(CCTV)相机在看一个展览:我们想快速看看两个不同的video框架是否显示相同的场景,但是照明和相机聚焦的细微差别意味着它们将不相同。
- 与以48×48呈现的相同图标(但是两个图像将被缩小到32×32,所以直方图具有相同的总像素数)相比,以64×64呈现的vector计算机GUI图标的图片。
我决定使用直方图来表示每个图像,使用三个1D直方图:每个RGB通道一个 – 对于我来说,只使用颜色和忽略纹理和边缘直方图是安全的(另一种方法是对每个图像使用单个3D直方图,但我避免这一点,因为它增加了额外的复杂性)。 因此,我需要比较直方图,看它们是多么相似,如果相似性度量通过某个阈值,那么我可以有把握地说,各自的图像在视觉上是相同的 – 我会比较每个图像的相应的通道直方图(例如图像1的红色直方图与图像2的红色直方图,然后图像1的蓝色直方图与图像2的蓝色直方图,然后绿色直方图 – 所以我没有比较图像1的红色直方图与图像2的蓝色直方图,这将是愚蠢的)。
假设我有这三个直方图,它们代表三个图像的红色RGB通道的总结(为简单起见,使用5个像素来显示7像素图像):
H1 H2 H3 XXX XXXXX XXXXXXXXXXXXX 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不是。
当我粗略地search“直方图差异”algorithm(至less我可以理解的)时,我发现一个stream行的方法是总结每个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
这是不正确的。
有没有办法来确定两个直方图之间的差异,考虑到分布的形状?
直方图比较本身就是一个相当的课题。
你有两大类比较函数: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] = 0.1 and H2.red[0] = 0.11
,即使在两种情况下|H1.red[0] - H2.red[0]| = 0.01
|H1.red[0] - H2.red[0]| = 0.01
。 - 交叉比较:称为bin-similaritymatrix的标准实例需要一些相似度matrixM,其中
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))
。 这种方法考虑了你所说的“closures”垃圾箱! 地球移动距离 (EMD)是另一种交叉距离。
结束,我有三点:
- 您应该阅读关于直方图距离的文章 。 这很容易,并介绍了直方图距离。 我所谈到的所有距离都很好地总结为第一章。老实说,文章中描述的最后一件事情并不那么复杂,但对您的情况来说可能是过度的。
- 交叉点距离是非常好的,但可能是昂贵的(即:计算时间长,因为它涉及matrix,因此是O(n ^ 2))。 规避昂贵的交叉bin计算(并广泛完成)的最简单的方法是做一些软分配:如果一个像素是红色的,那么你应该填充所有远程看起来像红色的bin(当然,给予更多重量到最接近的颜色)。 然后你可以使用bin-to-binalgorithm。
- 以math为中心的一点是:以前的观点是把交叉仓比较降低到仓到仓的比较。 实际上,它包含对相似度matrixM的隐式对angular化。如果可以对angular化
M = P'*D*P
其中P'
是P'
的转置matrix,则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
是一个软赋值,并且您使用隐式相似性matrixM = P'*Id*P
我很惊讶没有人提到过直方图比较的opencv实现,并且可以轻松处理不同格式(uchar,float,double等)的多通道图像(灰度,rgb,rgba等)
包括Bhattacharyya距离,卡方,相关和相交方法。 你可以find
compareHist(InputArray H1, InputArray H2, int method)
function在这里的手册。
Earth Mover的距离(EMD)通常用于这种types的直方图比较。 EMD使用一个值来定义从“直方图”的一个bin到另一个bin的“移动”像素的成本,并提供将特定直方图转换为目标直方图的总成本。 箱子越远,成本就越高。
在你的例子中,将5个单位从红色[0]移动到红色1会花费(c*1*5)
而将5个单位从红色[0]移动到红色[10]会花费(c*10*5)
。
那里有几个实现。 FastEMD在C ++,Java和Matlab中有代码。 我相信OpenCV也有一些支持。
使用这种技术发表了大量的论文,用于大型图像数据库相似性search。
当比较直方图时,我发现卡方检验是一个很好的开始。 如果在每个直方图中没有相同数量的条目,则必须谨慎一些,因为不能使用“正常”expression式。 从记忆中,如果你假设直方图有不平等的条目数卡方检验概括
1 /(MN)SUM_i [((Mni-Nmi)^ 2)/(mi + ni)]。
M和N是每个直方图中条目的总数,mi是直方图M的条目i中条目的数量,ni是直方图N的条目i中条目的数量。
另一个testing是Kolmogorov-Smirnovtesting。 该testing着眼于两个直方图的累积概率分布之间的最大差异。 这很难实现,我认为C中的数值食谱在C中有一个代码片段,而我很确定它在Matlab中。 如果你更感兴趣的是直方图的形状,而不是那么多的确切的价值观,这可能是一个更好的testing也是它的非参数。
你基本上想要看一个概率距离 。 有很多,你必须决定哪个是适合你的应用程序。 最近我和Chi-squared和Kullback-Leibler有幸运。
通过将input直方图中每个bin中的值除以直方图所基于的像素总数来规格化直方图。 然后使用@tkerwin的EMD 。
我认为EMD是一个很好的解决scheme,比较bin-bin方法来解决cross-bin问题。 但是,正如一些人所说,EMD是很长的时间。 你可以向我build议一些其他的方法吗?
正如其他人所说,地球移动者的距离或EMD(又名Wasserstein公制)可能是最佳的解决scheme。 用于快速EMD计算的候选名单方法可在R包, 运输中获得 。 它是从2014年的一篇论文中引入的,将其与其他方法进行比较,显示出更快的计算时间。 唯一的缺点是,它是在R,这是不是很快,除非在C程序的引擎盖下。