用Python/Linux方式比较两张图片

48 投票
13 回答
82716 浏览
提问于 2025-04-15 17:10

我在尝试解决一个问题,就是防止重复图片被上传。

我有两张JPG图片。看上去它们实际上是完全一样的。但出于某种原因,它们的文件大小不同(一个是从备份中提取的,另一个是另一个上传的),所以它们的md5校验和也不同。

我该如何有效且可靠地比较这两张图片,就像人类能看出它们明显相同一样?

举个例子:http://static.peterbe.com/a.jpghttp://static.peterbe.com/b.jpg

更新

我写了这个脚本:

import math, operator
from PIL import Image
def compare(file1, file2):
    image1 = Image.open(file1)
    image2 = Image.open(file2)
    h1 = image1.histogram()
    h2 = image2.histogram()
    rms = math.sqrt(reduce(operator.add,
                           map(lambda a,b: (a-b)**2, h1, h2))/len(h1))
    return rms

if __name__=='__main__':
    import sys
    file1, file2 = sys.argv[1:]
    print compare(file1, file2)

然后我下载了这两张视觉上相同的图片,并运行了这个脚本。输出结果是:

58.9830484122

有没有人能告诉我一个合适的阈值应该是多少?

更新 II

a.jpg和b.jpg之间的区别在于第二张是用PIL保存的:

b=Image.open('a.jpg')
b.save(open('b.jpg','wb'))

这显然对图片进行了非常轻微的质量修改。我现在通过对上传的文件应用相同的PIL保存方法解决了我的问题,结果现在可以正常工作了!

13 个回答

13

我想你应该先把图片解码,然后逐个像素进行比较,这样才能看看它们是否差不多。

使用PIL和Numpy,你可以很简单地做到这一点:

import Image
import numpy
import sys

def main():
    img1 = Image.open(sys.argv[1])
    img2 = Image.open(sys.argv[2])

    if img1.size != img2.size or img1.getbands() != img2.getbands():
        return -1

    s = 0
    for band_index, band in enumerate(img1.getbands()):
        m1 = numpy.array([p[band_index] for p in img1.getdata()]).reshape(*img1.size)
        m2 = numpy.array([p[band_index] for p in img2.getdata()]).reshape(*img2.size)
        s += numpy.sum(numpy.abs(m1-m2))
    print s

if __name__ == "__main__":
    sys.exit(main())

这样做会给你一个数字,如果图片非常相似,这个数字应该会非常接近0。

需要注意的是,如果图片有移动或者旋转,它们会被认为非常不同,因为像素就不会一一对应了。

24

来自 这里

判断两张图片内容是否完全相同的最快方法是先计算这两张图片的差异,然后找出差异图像中非零区域的边界框。

如果这两张图片一模一样,那么差异图像中的所有像素值都是零,这时边界框的函数会返回“无”(None)。

from PIL import ImageChops


def equal(im1, im2):
    return ImageChops.difference(im1, im2).getbbox() is None
29

有一个开源项目,它使用WebDriver来截屏,然后比较这些图片,看看有没有问题(http://code.google.com/p/fighting-layout-bugs/)。这个项目的做法是把文件打开成一个流,然后逐个比对每一个像素。

你也可以用PIL做类似的事情。

编辑:

经过更多的研究,我发现了

h1 = Image.open("image1").histogram()
h2 = Image.open("image2").histogram()

rms = math.sqrt(reduce(operator.add,
    map(lambda a,b: (a-b)**2, h1, h2))/len(h1))

http://snipplr.com/view/757/compare-two-pil-images-in-python/http://effbot.org/zone/pil-comparing-images.htm上。

撰写回答