快速有效地检测两个图像在Python中是否完全相同

12 投票
5 回答
16709 浏览
提问于 2025-04-18 08:12

给定两张图片:

image1.jpg
image2.jpg

有没有什么快速的方法可以在Python中检查它们是否在视觉上是相同的?比如,它们可能有不同的EXIF数据,这会导致不同的校验和,尽管图片内容是一样的。

Imagemagick有一个很棒的工具,叫做“identify”,它可以生成图片的视觉哈希值,但这个过程非常消耗处理器资源。

5 个回答

1

虽然还没有人提到,但 空间CIELAB 是一个很有用的图像相似度指标。

其实它比听起来简单:你先把两张图片模糊处理,模糊的程度和观察者的视觉敏锐度有关,然后计算它们在CIELAB色彩空间中的差异(也叫delta E)。根据你的需求,你可以选择差异图像的峰值或平均值。

如果使用pyvips,你可以这样写:

#!/usr/bin/python3

import sys
import pyvips

# the access hint means these images can be streamed in parallel rather 
# than fully decoded
image1 = pyvips.Image.new_from_file(sys.argv[1], access="sequential")
image2 = pyvips.Image.new_from_file(sys.argv[2], access="sequential")

# blur by an amount related to the visual acuity of the observer -- this will
# help remove peaks caused by small alignment differences, then take the
# CIELAB76 colour difference
sigma = 3.0
# diff = image1.gaussblur(sigma).dE76(image2.gaussblur(sigma))
diff = image1.resize(1.0 / sigma).dE76(image2.resize(1.0 / sigma))

# compute the peak difference ... over perhaps 20 means a visible difference
print(f"peak difference of {diff.max()} visual units")

作为一个小优化,调整图片大小而不是模糊处理,可以减少需要计算颜色差异的像素数量。

这台电脑大约需要400毫秒来计算一对6k x 4k的JPG图片之间的差异。

$ vipsheader ~/pics/theo.jpg
/home/john/pics/theo.jpg: 6048x4032 uchar, 3 bands, srgb, jpegload
$ time ./try51.py ~/pics/theo2.jpg ~/pics/theo.jpg
peak difference of 0.0 visual units
real    0m0.396s
user    0m0.952s
sys 0m0.197s
2

使用 https://github.com/andrewekhalel/sewar 来比较图像的相似性

> from sewar.full_ref import uqi
> uqi(img1,img2)
0.9586952304831419
2

在Python和OpenCV中,有一种方法可以做到这一点,就是先计算两个图像之间的绝对差值(absdiff),然后再对整个绝对差值图像求平均值。

输入图像1(PNG格式):

这里输入图像描述

输入图像2(JPG格式):

这里输入图像描述

import cv2
import numpy as np

# read image 1
img1 = cv2.imread('lena.png')

# read image 2
img2 = cv2.imread('lena.jpg')

# do absdiff
diff = cv2.absdiff(img1,img2)

# get mean of absdiff
mean_diff = np.mean(diff)

# print result
print(mean_diff)

1.8992767333984375
15

我现在还在尝试解决这个问题——即使提问者说ImageMagick的方法太耗处理器(而且我的方法不涉及Python)……也许我的回答对其他人有用,他们通过搜索引擎找到了这个页面。

要知道,任何图像比较,如果是要发现高分辨率图像中的细微差异,都会比发现低分辨率图像中的大差异更耗处理器,因为需要比较的像素数量要多得多。

差异可视化

这里有一个ImageMagick命令,可以比较两个(大小相同的!)图像,并将所有不同的像素显示为红色,相同的像素显示为白色。第一个图像作为背景图像,淡化后用于红白像素矩阵的组合。.img可以是任何IM支持的格式(.png, .jpg, .tif等):

 compare reference.img similar.img  delta.img
 compare reference.img similar.img  -compose src delta.img

默认情况下,比较是在72 PPI(每英寸像素数)下进行的。如果你需要更高的分辨率(比如处理基于矢量的图像,如PDF页面),可以添加-density来提高分辨率。当然,处理时间也会相应增加:

 compare -density 300 reference.img similar.img delta.img

如果你添加一个模糊因子,可以告诉ImageMagick将所有颜色距离不超过某个值的像素视为相同:

 compare -fuzz '3%' reference.img similar.img -compose src delta.img

pHash差异值

更新版的ImageMagick支持phash算法:

 compare -metric phash reference.img similar.img -compose src delta.img

这个算法除了生成可视化的delta.img外,还会返回一个数值,表示两个图像之间的“差异”。这个值越接近0,说明两个图像越相似。

示例:

创建几个小的PDF页面,里面有一些小差异。我使用Ghostscript:

gs -o ref1.pdf -sDEVICE=pdfwrite -g1050x1350 \
 -c "/Courier findfont 160 scalefont setfont 10.0 10.0 moveto (0) show showpage"

gs -o ref2.pdf -sDEVICE=pdfwrite -g1050x1350 
 -c "/Courier findfont 160 scalefont setfont 10.1 10.1 moveto (0) show showpage"

gs -o ref3.pdf -sDEVICE=pdfwrite -g1050x1350 \
 -c "/Courier findfont 160 scalefont setfont 10.0 10.0 moveto (O) show showpage"

gs -o ref4.pdf -sDEVICE=pdfwrite -g1050x1350 \
 -c "/Courier findfont 160 scalefont setfont 10.1 10.1 moveto (O) show showpage"

现在将ref1.pdfref3.pdf在默认的72 PPI分辨率下进行比较:

compare -metric phash ref1.pdf ref3.pdf delta-ref1-ref3.pdf
  7.61662

返回的pHash值是7.61662。这表明ImageMagick的compare发现了至少一些差异。

接下来看看可视化效果。我将创建三个PDF/图像的并排可视化(如下所示):

convert                                    \
   -mattecolor blue                        \
      \( ref1.pdf -frame 2x2 \)            \
    null:                                  \
      \( ref3.pdf -frame 2x2 \)            \
    null:                                  \
      \( delta-ref1-ref3.pdf -frame 2x2 \) \
   +append                                 \
    ref1-ref3-delta.png 

差异可视化:<code>ref1.pdf</code>(右),<code>ref3.pdf</code>(中间)和<code>ref1-ref3-delta.png</code>(右)

如你所见,数字0(零)和字母O(大写的o)的不同形状非常明显。

接下来比较ref1.pdfref2.pdf,同样在72 PPI下。

compare -metric phash ref1.pdf ref2.pdf delta-ref1-ref2.pdf
  0

返回的pHash值现在是0。这表明ImageMagick没有发现差异!

创建三个PDF/图像的并排可视化:

convert                                    \
   -mattecolor blue                        \
      \( ref1.pdf -frame 2x2 \)            \
    null:                                  \
      \( ref2.pdf -frame 2x2 \)            \
    null:                                  \
      \( delta-ref1-ref2.pdf -frame 2x2 \) \
   +append                                 \
    ref1-ref2-delta.png 

差异可视化:<code>ref1.pdf</code>(右),<code>ref2.pdf</code>(中间)和<code>ref1-ref2-delta.png</code>(右)

如你所见,在72 PPI下,ImageMagick没有发现两个PDF之间的差异(红色像素表示差异)。根据Ghostscript命令,两个PDF都显示数字0,但位置在x和y方向上偏移了0.1点。因此实际上,在原始PDF中确实存在差异。但在72 PPI下渲染时,这个差异并不可见。

那我们试试用density 600来查看差异:

compare        \
 -metric phash \
 -density 600  \
  ref1.pdf     \
  ref2.pdf     \
  ref1-ref2-at-density600-delta.png 

0.00172769

在600 PPI下返回的pHash值现在是0.00172769。这个值接近零,但仍然有差异。这个差异比ref1.pdfref3.pdf之间的差异要小。

在视觉比较中,这个差异现在清晰可见,尽管只是通过一条细细的红色像素线:

26

使用PIL/Pillow库:

from PIL import Image

im1 = Image.open('image1.jpg')
im2 = Image.open('image2.jpg')

if list(im1.getdata()) == list(im2.getdata()):
    print "Identical"
else:
    print "Different"

撰写回答