【问题标题】:Checking images for similarity with OpenCV使用 OpenCV 检查图像的相似性
【发布时间】:2012-07-17 11:24:53
【问题描述】:

OpenCV 是否支持比较两个图像,返回一些值(可能是百分比)来指示这些图像的相似程度?例如。同一张图片传两次返回100%,完全不同则返回0%。

我已经在 StackOverflow 上阅读了很多类似的主题。我也做了一些谷歌搜索。遗憾的是,我无法给出令人满意的答案。

【问题讨论】:

标签: image opencv comparison similarity


【解决方案1】:

这是一个巨大的话题,从 3 行代码到整个研究杂志都有答案。

我将概述最常见的此类技术及其结果。

比较直方图

最简单、最快的方法之一。几十年前提出的一种寻找图片相似性的方法。这个想法是森林会有很多绿色,人脸会有很多粉红色,或者其他什么。因此,如果您将两张图片与森林进行比较,您会发现直方图之间有一些相似之处,因为两者都有很多绿色。

缺点:太简单了。香蕉和沙滩看起来一样,都是黄色的。

OpenCV 方法:compareHist()

模板匹配

这里是一个很好的例子matchTemplate finding good match。它将搜索图像与正在搜索的图像进行卷积。它通常用于在较大的图像中查找较小的图像部分。

缺点:它只返回具有相同图像、相同尺寸和方向的良好结果。

OpenCV 方法:matchTemplate()

特征匹配

被认为是进行图像搜索的最有效方法之一。从图像中提取许多特征,以确保即使在旋转、缩放或倾斜时也能再次识别相同的特征。以这种方式提取的特征可以与其他图像特征集进行匹配。另一张图片与第一张图片具有较高比例的特征被认为描绘的是同一场景。

找到两组点之间的单应性,还可以让您找到原始图片之间拍摄角度的相对差异或重叠量。

有许多关于此的 OpenCV 教程/示例,还有一个不错的视频 here。一个完整的 OpenCV 模块 (features2d) 专用于它。

缺点:它可能会很慢。它并不完美。


在OpenCV Q&A 网站上,我正在讨论特征描述符之间的区别,这在比较整个图像和纹理描述符时非常有用,纹理描述符用于识别图像中的人脸或汽车等对象。

【讨论】:

【解决方案2】:

如果用于匹配相同的图像(相同的大小/方向)

// Compare two images by getting the L2 error (square-root of sum of squared error).
double getSimilarity( const Mat A, const Mat B ) {
if ( A.rows > 0 && A.rows == B.rows && A.cols > 0 && A.cols == B.cols ) {
    // Calculate the L2 relative error between images.
    double errorL2 = norm( A, B, CV_L2 );
    // Convert to a reasonable scale, since L2 error is summed across all pixels of the image.
    double similarity = errorL2 / (double)( A.rows * A.cols );
    return similarity;
}
else {
    //Images have a different size
    return 100000000.0;  // Return a bad value
}

Source

【讨论】:

    【解决方案3】:

    Sam 的解决方案应该足够了。我使用了直方图差异和模板匹配的组合,因为没有一种方法能 100% 地为我工作。不过,我对直方图方法的重视程度较低。这是我在简单的 python 脚本中实现的方法。

    import cv2
    
    class CompareImage(object):
    
        def __init__(self, image_1_path, image_2_path):
            self.minimum_commutative_image_diff = 1
            self.image_1_path = image_1_path
            self.image_2_path = image_2_path
    
        def compare_image(self):
            image_1 = cv2.imread(self.image_1_path, 0)
            image_2 = cv2.imread(self.image_2_path, 0)
            commutative_image_diff = self.get_image_difference(image_1, image_2)
    
            if commutative_image_diff < self.minimum_commutative_image_diff:
                print "Matched"
                return commutative_image_diff
            return 10000 //random failure value
    
        @staticmethod
        def get_image_difference(image_1, image_2):
            first_image_hist = cv2.calcHist([image_1], [0], None, [256], [0, 256])
            second_image_hist = cv2.calcHist([image_2], [0], None, [256], [0, 256])
    
            img_hist_diff = cv2.compareHist(first_image_hist, second_image_hist, cv2.HISTCMP_BHATTACHARYYA)
            img_template_probability_match = cv2.matchTemplate(first_image_hist, second_image_hist, cv2.TM_CCOEFF_NORMED)[0][0]
            img_template_diff = 1 - img_template_probability_match
    
            # taking only 10% of histogram diff, since it's less accurate than template method
            commutative_image_diff = (img_hist_diff / 10) + img_template_diff
            return commutative_image_diff
    
    
        if __name__ == '__main__':
            compare_image = CompareImage('image1/path', 'image2/path')
            image_difference = compare_image.compare_image()
            print image_difference
    

    【讨论】:

    • 我不太懂python。但什么是 'commutative_image_diff' 类型? cv.Mat 或双。如果是 cv.Mat,比较 'commutative_image_diff
    【解决方案4】:

    pythonic numpy 方法有点离题但很有用。它强大且快速,但只比较像素而不是图片包含的对象或数据(并且它需要相同大小和形状的图像):

    在没有 openCV 和任何计算机视觉库的情况下,一种非常简单且快速的方法是通过以下方式对图片数组进行规范

    import numpy as np
    picture1 = np.random.rand(100,100)
    picture2 = np.random.rand(100,100)
    picture1_norm = picture1/np.sqrt(np.sum(picture1**2))
    picture2_norm = picture2/np.sqrt(np.sum(picture2**2))
    

    在定义了两个标准化图片(或矩阵)之后,您可以将要比较的图片相乘相加:

    1)如果你比较相似的图片,总和将返回1:

    In[1]: np.sum(picture1_norm**2)
    Out[1]: 1.0
    

    2) 如果它们不相似,您将得到一个介于 0 和 1 之间的值(乘以 100 后的百分比):

    In[2]: np.sum(picture2_norm*picture1_norm)
    Out[2]: 0.75389941124629822
    

    请注意,如果您有彩色图片,则必须在所有 3 个维度上执行此操作,或者只比较灰度版本。我经常需要将大量图片与任意内容进行比较,这是一种非常快速的方法。

    【讨论】:

    • 嗨,我只是按照您的步骤操作,但我发现规范化部分无法获得正确的结果。最终结果远大于 1.0。有什么想法吗?
    猜你喜欢
    • 2018-09-25
    • 2012-11-03
    • 2023-03-12
    • 1970-01-01
    • 2021-07-29
    • 1970-01-01
    • 2018-02-06
    • 2020-05-23
    • 2015-03-20
    相关资源
    最近更新 更多