比较python / linux方式的两个图像

时间:2009-12-18 11:27:38

标签: python linux image

尝试解决防止上传重复图像的问题。

我有两个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保存应用到正在上传的文件而不用它做任何事情,它现在可以工作了!

12 个答案:

答案 0 :(得分:25)

有一个OSS项目使用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

答案 1 :(得分:19)

来自here

确定两个图像是否具有完全相同内容的最快方法是获取两个图像之间的差异,然后计算此图像中非零区域的边界框。如果图像相同,则差异图像中的所有像素均为零,并且边界框函数返回“无”。

import ImageChops

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

答案 2 :(得分:11)

我猜你应该对图像进行解码并逐像素进行比较,看看它们是否相似。

使用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的数值。

请注意,移动/旋转的图像将报告为非常不同,因为像素不会逐个匹配。

答案 3 :(得分:10)

使用ImageMagick,您只需在shell中使用[或通过程序内的OS库调用]

compare image1 image2 output

这将创建一个带有差异

的输出图像
compare -metric AE -fuzz 5% image1 image2 output

为了忽略微小的像素差异,会给你一个5%的模糊因子。 可以从here

获取更多信息

答案 4 :(得分:4)

知道是什么使图像的某些特征比其他特征更重要的问题是整个科学计划。我会根据你想要的解决方案建议一些替代方案:

  • 如果您的问题是要查看JPEG中的位是否有翻转,那么请尝试对差异图像进行成像(本地可能有一个小的编辑?),

  • 查看图片是否全局相同,使用Kullback Leibler距离来比较直方图,

  • 在应用其他答案之前,看看您是否进行了一些质量改变,使用以下功能过滤图像以提高高级频率的重要性:

代码:

def FTfilter(image,FTfilter):
    from scipy.fftpack import fft2, fftshift, ifft2, ifftshift
    from scipy import real
    FTimage = fftshift(fft2(image)) * FTfilter
    return real(ifft2(ifftshift(FTimage)))
    #return real(ifft2(fft2(image)* FTfilter))


#### whitening
def olshausen_whitening_filt(size, f_0 = .78, alpha = 4., N = 0.01):
    """
    Returns the whitening filter used by (Olshausen, 98)

    f_0 = 200 / 512

    /!\ you will have some problems at dewhitening without a low-pass

    """
    from scipy import mgrid, absolute
    fx, fy = mgrid[-1:1:1j*size[0],-1:1:1j*size[1]]
    rho = numpy.sqrt(fx**2+fy**2)
    K_ols = (N**2 + rho**2)**.5 * low_pass(size, f_0 = f_0, alpha = alpha)
    K_ols /= numpy.max(K_ols)

    return  K_ols

def low_pass(size, f_0, alpha):
    """
    Returns the low_pass filter used by (Olshausen, 98)

    parameters from Atick (p.240)
    f_0 = 22 c/deg in primates: the full image is approx 45 deg
    alpha makes the aspect change (1=diamond on the vert and hor, 2 = anisotropic)

    """

    from scipy import mgrid, absolute
    fx, fy = mgrid[-1:1:1j*size[0],-1:1:1j*size[1]]
    rho = numpy.sqrt(fx**2+fy**2)
    low_pass = numpy.exp(-(rho/f_0)**alpha)

    return  low_pass

(来自http://www.incm.cnrs-mrs.fr/LaurentPerrinet/Publications/Perrinet08spie的无耻副本)

答案 5 :(得分:2)

首先,我应该注意他们相同; b已被重新压缩并且质量下降。如果你仔细观察好的显示器,你可以看到这一点。

要确定它们是主观上“相同”,你必须做类似于fortran所建议的事情,尽管你必须随意建立一个“同一性”的阈值。使s独立于图像大小,并处理我会考虑在两个图像的像素之间的颜色空间中进行RMS(均方根)欧几里德距离。我现在没有时间写出代码,但基本上对于每个像素,你都要计算

(R_2 - R_1) ** 2 + (G_2 - G_1) ** 2 + (B_2 - B_1) ** 2

,添加

(A_2 - A_1)** 2

术语,如果图像具有Alpha通道等,则结果是两个图像之间的颜色空间距离的平方。找到所有像素的平均值(平均值),然后取得结果标量的平方根。然后为此值确定合理的阈值。

或者,您可能只是认为具有不同有损压缩的同一原始图像的副本并非真正“相同”并坚持使用文件哈希。

答案 6 :(得分:2)

我测试了这个并且它在所有方法中都是最好的并且非常快!

def rmsdiff_1997(im1, im2):
    "Calculate the root-mean-square difference between two images"

    h = ImageChops.difference(im1, im2).histogram()

    # calculate rms
    return math.sqrt(reduce(operator.add,
        map(lambda h, i: h*(i**2), h, range(256))
    ) / (float(im1.size[0]) * im1.size[1]))

here link以供参考

答案 7 :(得分:1)

您可以使用PIL进行比较(迭代图片的像素/片段并进行比较),或者如果您正在寻找完全相同的副本比较,请尝试比较两个文件的MD5哈希值。

答案 8 :(得分:1)

我尝试了上面和别处提到的3种方法。 似乎有两种主要的图像比较类型,Pixel-By-Pixel和Histogram。

我已经试过了两个,Pixel one确实失败了100%,因为它实际上应该如此,好像我们将第二个图像移动1个像素,所有像素都不匹配,我们将100%无法匹配。

但直方图比较在理论上应该非常好,但事实并非如此。

这是两个略有移位视图端口的图像,直方图看起来有99%相似,但算法产生的结果显示“非常不同”

Centered

Same, but Shifted ~15º

4种不同的算法结果:

  • 完美匹配:错误
  • 像素差异:115816402
  • 直方图比较:83.69564286668303
  • HistComparison:1744.8160719686186

第一张图像(居中的QR)与100%不同图像的相同比较:

Totally different image and histogram

算法结果:

  • 完美匹配:错误
  • 像素差异:207893096
  • HistogramComparison:104.30194643642095
  • HistComparison:6875.766716148522

如何以更精确和可用的方式测量两个图像差异的任何建议将非常受欢迎。在这个阶段,这些算法似乎都没有产生可用的结果,因为略有不同的图像具有与100%不同图像非常相似/接近的结果。

from PIL import Image
    from PIL import ImageChops
    from functools import reduce
    import numpy
    import sys
    import math
    import operator

# Just checking if images are 100% the same


def equal(im1, im2):
    img1 = Image.open(im1)
    img2 = Image.open(im2)
    return ImageChops.difference(img1, img2).getbbox() is None


def histCompare(im1, im2):
    h1 = Image.open(im1).histogram()
    h2 = Image.open(im2).histogram()

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

# To get a measure of how similar two images are, we calculate the root-mean-square (RMS)
# value of the difference between the images. If the images are exactly identical,
# this value is zero. The following function uses the difference function,
# and then calculates the RMS value from the histogram of the resulting image.


def rmsdiff_1997(im1, im2):
    #"Calculate the root-mean-square difference between two images"
    img1 = Image.open(im1)
    img2 = Image.open(im2)

    h = ImageChops.difference(img1, img2).histogram()

    # calculate rms
    return math.sqrt(reduce(operator.add,
                            map(lambda h, i: h * (i**2), h, range(256))
                            ) / (float(img1.size[0]) * img1.size[1]))

# Pixel by pixel comparison to see if images are reasonably similar.


def countDiff(im1, im2):
    s = 0
    img1 = Image.open(im1)
    img2 = Image.open(im2)

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

    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))

    return s


print("[Same Image]")
print("Perfect match:", equal("data/start.jpg", "data/start.jpg"))
print("Pixel difference:", countDiff("data/start.jpg", "data/start.jpg"))
print("Histogram Comparison:", rmsdiff_1997("data/start.jpg", "data/start.jpg"))
print("HistComparison:", histCompare("data/start.jpg", "data/start.jpg"))

print("\n[Same Position]")
print("Perfect match:", equal("data/start.jpg", "data/end.jpg"))
print("Pixel difference:", countDiff("data/start.jpg", "data/end.jpg"))
print("Histogram Comparison:", rmsdiff_1997("data/start.jpg", "data/end.jpg"))
print("HistComparison:", histCompare("data/start.jpg", "data/end.jpg"))

print("\n[~5º off]")
print("Perfect match:", equal("data/start.jpg", "data/end2.jpg"))
print("Pixel difference:", countDiff("data/start.jpg", "data/end2.jpg"))
print("Histogram Comparison:", rmsdiff_1997("data/start.jpg", "data/end2.jpg"))
print("HistComparison:", histCompare("data/start.jpg", "data/end2.jpg"))

print("\n[~15º off]")
print("Perfect match:", equal("data/start.jpg", "data/end3.jpg"))
print("Pixel difference:", countDiff("data/start.jpg", "data/end3.jpg"))
print("Histogram Comparison:", rmsdiff_1997("data/start.jpg", "data/end3.jpg"))
print("HistComparison:", histCompare("data/start.jpg", "data/end3.jpg"))

print("\n[100% different]")
print("Perfect match:", equal("data/start.jpg", "data/end4.jpg"))
print("Pixel difference:", countDiff("data/start.jpg", "data/end4.jpg"))
print("Histogram Comparison:", rmsdiff_1997("data/start.jpg", "data/end4.jpg"))
print("HistComparison:", histCompare("data/start.jpg", "data/end4.jpg"))

答案 9 :(得分:0)

仅使用PIL和某些Python数学库,就可以以简单明了的方式查看两个图像是否相同。该方法仅在具有相同尺寸和扩展名的图像文件上进行了测试,但是避免了在此问题的其他答案中出现的一些错误。

import math, operator
from PIL import Image
from PIL import ImageChops

image1 = Image.open(img1)
image2 = Image.open(img2)
diff = ImageChops.difference(img1, img2).histogram()

sq = (value * (i % 256) ** 2 for i, value in enumerate(diff))
sum_squares = sum(sq)
rms = math.sqrt(sum_squares / float(img1.size[0] * img1.size[1]))

优点:
% 256添加到平方的计算中,每种颜色的权重相等。先前的许多答案的RMS公式都给出了蓝色像素值是红色值权重的3倍,绿色像素值是红色值权重的2倍。

更容易被骗。 RMS计算可以使用lambda和reduce方法编写成单行代码,但将其扩展到3行可以大大提高一目了然的可读性。

此代码正确检测到旋转的图像与方向不同的基础图像不同。如@musicinmybrain所指出的,这避免了使用直方图比较图像时的陷阱。如果创建了两个图像的直方图,然后将它们相互比较,如果一个图像是另一个图像的旋转,则比较将报告图像中没有差异,因为图像的直方图是相同的。另一方面,如果先比较图像,则将创建比较结果的直方图,即使一个图像是另一个图像,图像也将精确比较。

此答案中使用的代码是此code.activestate.com帖子的复制/粘贴,并考虑了第3条评论,该评论纠正了绿色和蓝色像素值的较重权重。

答案 10 :(得分:0)

使用numpy

的简单解决方案
img1 = Image.open(img1_path)
img2 = Image.open(img2_path)

然后使用array_equal

np.array_equal(img1, img2)

如果收益为True,则它们在所有渠道中的收益都完全相同

答案 11 :(得分:0)

如果您要检查两个图像是否相同,此代码可能会对您有所帮助。

 import binascii
 
 
 with open('pic1.png', 'rb') as f:
     content1 = f.read()
 with open('pic2.png', 'rb') as f:
     content2 = f.read()
 if content1 == content2:
     print("same")
 else:
     print("not same")

`