快速有效地检测两个图像在Python中是否完全相同
给定两张图片:
image1.jpg
image2.jpg
有没有什么快速的方法可以在Python中检查它们是否在视觉上是相同的?比如,它们可能有不同的EXIF数据,这会导致不同的校验和,尽管图片内容是一样的。
Imagemagick有一个很棒的工具,叫做“identify”,它可以生成图片的视觉哈希值,但这个过程非常消耗处理器资源。
5 个回答
虽然还没有人提到,但 空间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
使用 https://github.com/andrewekhalel/sewar 来比较图像的相似性
> from sewar.full_ref import uqi
> uqi(img1,img2)
0.9586952304831419
在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
我现在还在尝试解决这个问题——即使提问者说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.pdf
与ref3.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
如你所见,数字0
(零)和字母O
(大写的o)的不同形状非常明显。
接下来比较ref1.pdf
和ref2.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
如你所见,在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.pdf
和ref3.pdf
之间的差异要小。
在视觉比较中,这个差异现在清晰可见,尽管只是通过一条细细的红色像素线:
使用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"