比较两个直方图

时间:2011-06-27 22:06:31

标签: image-processing histogram

对于一个小项目,我需要将一个图像与另一个图像进行比较 - 以确定图像是否大致相同。图像很小,从25到100px不等。图像意味着具有相同的图像数据但是非常不同,因此简单的像素相等检查将不起作用。考虑以下两种可能的情况:

  1. 博物馆中的安全(CCTV)摄像机正在观看展览:我们希望快速查看两个不同的视频帧是否显示相同的场景,但是灯光和摄像机焦点的细微差别意味着它们将不相同。
  2. 以64x64渲染的矢量计算机GUI图标的图片与以48x48渲染的相同图标相比(但两个图像将缩小为32x32,因此直方图的总像素数相同)。
  3. 我决定使用直方图来表示每个图像,使用三个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
    

    这是不正确的。

    有没有办法确定两个直方图之间的差异,考虑到分布的形状?

8 个答案:

答案 0 :(得分:73)

比较直方图本身就是一个主题。

你有两大类比较函数:bin-to-bin比较和cross-bin比较。

  • Bin-to-bin比较:如你所说,差异的标准总和非常糟糕。有一个改进,卡方距离,表示如果H1.red[0] = 0.001 and H2.red[0] = 0.011H1.red[0] = 0.1 and H2.red[0] = 0.11重要得多,即使在|H1.red[0] - H2.red[0]| = 0.01两种情况下也是如此。< / LI>
  • 交叉区间比较:称为区间相似性矩阵的标准示例需要一些相似性矩阵M,其中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)是另一种跨行距离。

要完成,我有三点:

  • 您应该阅读this paper on histogram distance。它非常简单,并向您介绍直方图距离。我所谈到的所有距离都很好地总结了第一章。老实说,文章中描述的最后一件事情并不复杂,但对你的案子来说可能有些过分。
  • 跨箱距离非常好,但成本可能很高(即:计算时间长,因为它涉及矩阵,因此是O(n ^ 2))。绕过昂贵的跨仓计算(并且广泛完成)的最简单方法是做一些软分配:如果一个像素是红色的,那么你应该填充远程看起来像红色的所有箱子(当然,提供更多重量到最接近的颜色)。然后你可以使用bin-to-bin算法。
  • 更多以数学为中心:前一点是关于减少与bin-to-bin比较的cross-bin比较。事实上,它包含隐式对角化相似性矩阵M.如果你可以对齐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 ++编程,否则它不会很快。