Python + OpenCV - 如何正确比较图像(通过直方图)?

3
我有一堆图片(来自M.C. Escher收藏),我想要整理它们,所以我首先想到的步骤是通过比较它们(你知道的,有些具有不同的分辨率/形状等)来将它们分组。
我编写了一个非常粗暴的脚本来: * 读取文件 * 计算它们的直方图 * 进行比较
但是比较的质量真的很低,就像有些完全不同的文件匹配起来了。
请看看我到目前为止所写的内容:
准备直方图。
Original Answer: "最初的回答"
files_hist = {}

for i, f in enumerate(files):
    try:
        frame = cv2.imread(f)
        frame = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
        hist = cv2.calcHist([frame],[0],None,[4096],[0,4096])
        cv2.normalize(hist, hist, alpha=0, beta=1, norm_type=cv2.NORM_MINMAX)

        files_hist[f] = hist
    except Exception as e:
        print('ERROR:', f, e)

对比直方图

pairs = list(itertools.combinations(files_hist.keys(), 2))

for i, (f1, f2) in enumerate(pairs):
    correl = cv2.compareHist(files_hist[f1], files_hist[f2], cv2.HISTCMP_CORREL)

    if correl >= 0.999:
        print('MATCH:', correl, f1, f2)


现在,举个例子,我找到了这两个文件的匹配项: m._c._escher_244_(1933).jpg m._c._escher_244_(1933).jpg
m._c._escher_208_(1931).jpg m._c._escher_208_(1931).jpg 使用上述代码,它们之间的相关性是0.9996699595530539(所以它们几乎相同:( ) 我做错了什么?如何改进代码以避免这种错误匹配? 谢谢! 最初的回答:以上的代码使用的是图像的平均哈希算法来比较它们的相似度。然而,对于一些图像,这种方法可能会导致错误的匹配结果。要改善结果,你可以尝试使用其他算法,例如结构化相似性索引(SSIM)算法进行比较。

你可以尝试使用cv::norm()函数和NORM_L2方法来比较两张图片。 - Bahramdun Adil
你的比较的目的是什么?是寻找相似之处吗?还是寻找重复项,或者其他什么? - Eypros
目的是查找重复项,是的这是主要目的(我还有另一个项目,它通过在特定间隔内抓取帧并进行比较来比较视频)。 - Sandro Tosi
1个回答

4
直方图不是比较图像的好方法。例如,在黑白图像中,如果它们具有相同数量的黑色像素,则直方图将是相同的,而不考虑图像中像素的分布情况(这就是为什么您提到的图像被分类为几乎相等的原因)。
有更好的方法来量化图像之间的差异,此帖子提到了一个很好的选择:
- 将两个图像加载为数组(scipy.misc.imread),并计算逐元素(逐像素)差异。 计算差异的范数。
编辑:
回答一些问题:
我认为每像素的零范数将是0.0-1.0值,接近于0.0的值意味着“图像相同”,对吗?
接近0.0的值意味着像素相同。要将图像作为整体进行比较,您需要对所有像素求和。如果总和接近0.0,则意味着图像几乎相同。
如果2个图像尺寸不同怎么办?
那是一个好问题。为了计算范数差异,图像必须具有相同的大小。我看到两种实现方法:
- 第一种方法是将其中一个图像调整为另一个图像的形状,但这可能会导致图像失真。 - 第二种方法是使用零填充较小的图像,直到尺寸匹配。
注:如果您对逐像素范数求和,则得到介于零和图像中的像素数之间的值。如果要比较多个图像,则可能会产生混淆。例如,假设您正在比较图像A和B,并且两者均具有形状50x50(因此,图像具有2500个像素);接近2500的值表示图像完全不同。现在假设您正在比较图像C和D,并且两者都具有形状1000x1000,在这种情况下,类似2500的值表示图像相似。为了解决这个问题,可以将逐像素总和除以图像中的像素数,这将导致值介于0.0和1.0之间,其中0.0表示图像相同,1.0表示它们完全不同。
是的,当我比较大小不同的2个图像时,收到错误消息diff = image1 - image2 ValueError: operands could not be broadcast together with shapes (850,534) (663,650) 这是因为图像具有不同的形状。调整大小或填充可以避免此错误(如上所述)。

谢谢Heitor!这是一个非常有趣的方法,我将尝试一下。还有一些问题:
  • 我认为每像素的零范数将是0.0-1.0值,接近0.0的值意味着“图像相同”,对吗?
  • 如果两个图像的尺寸不同怎么办?每像素的零范数会稍微偏离,因为它使用了“img1.size”,对吗?
- Sandro Tosi
这是在比较两个尺寸不同的图像时收到的错误信息: diff = image1 - image2 ValueError: operands could not be broadcast together with shapes (850,534) (663,650) - Sandro Tosi
我也使用了https://dev59.com/Y3VC5IYBdhLWcg3wxEJ1#49574931,以及在https://dev59.com/Y3VC5IYBdhLWcg3wxEJ1#QLajEYcBWogLw_1bMFRo的评论中提到的skimage函数,但仍然没有成功:即使图像“明显”相同,也只有很少的匹配项,或者是太广泛的匹配项:( - Sandro Tosi

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