比较图片 Python PIL

35

我该如何比较两张图片?我发现了Python的PIL库,但我并不真正理解它是如何工作的。


2
这真的没有任何意义,这就是我为什么要问的原因。图像基本上是一个数组(2D或3D,取决于您是否处于RGB /灰度),有多种比较两个图像的方法:如果您需要查看它们是否相同,则image1-image2将为您提供信息。如果您需要找到两个图像之间的转换,那就是另一回事了。 - CoMartel
7个回答

37

要检查两个 jpg 文件是否完全相同,可以使用 pillow 库:

from PIL import Image
from PIL import ImageChops

image_one = Image.open(path_one)
image_two = Image.open(path_two)

diff = ImageChops.difference(image_one, image_two)

if diff.getbbox():
    print("images are different")
else:
    print("images are the same")

2
如果图像具有 alpha 通道 (github.com/python-pillow/Pillow/issues/4849),或者使用不同的模式 (pillow.readthedocs.io/en/stable/handbook/concepts.html#modes),则此方法可能无法正常工作。 - Yay295
很奇怪,我得到了一个失败:AssertionError: comparison of imgs failed: diff.getbbox()=None,但我几乎确定它应该可以工作。为什么会这样呢? - Charlie Parker

15
根据Victor Ilyenko的回答,我需要将图像转换为RGB格式,因为当你尝试比较的.png文件包含alpha通道时,PIL会默默失败。
image_one = Image.open(path_one).convert('RGB')
image_two = Image.open(path_two).convert('RGB')

3
这是否意味着一张带有透明通道的图像和另一张没有透明通道的图像会被视为相同,即使它们实际上不同? - bfontaine
是的,这将从比较中排除 alpha 通道。 我建议使用 imagemagick:compare -metric AE $file1 $file2 /dev/null - mara004
很奇怪,我得到了一个失败:AssertionError: comparison of imgs failed: diff.getbbox()=None,但我几乎确定它应该可以工作。为什么会这样呢? - Charlie Parker
@CharlieParker 这意味着这些图片是相同的。 - undefined

6

看起来 Viktor 的实现在图像大小不同时可能会失败。

这个版本还比较 alpha 值。视觉上相同的像素被认为是相同的,例如 (0, 0, 0, 0) 和 (0, 255, 0, 0)。

from PIL import ImageChops

def are_images_equal(img1, img2):
    equal_size = img1.height == img2.height and img1.width == img2.width

    if img1.mode == img2.mode == "RGBA":
        img1_alphas = [pixel[3] for pixel in img1.getdata()]
        img2_alphas = [pixel[3] for pixel in img2.getdata()]
        equal_alphas = img1_alphas == img2_alphas
    else:
        equal_alphas = True

    equal_content = not ImageChops.difference(
        img1.convert("RGB"), img2.convert("RGB")
    ).getbbox()

    return equal_size and equal_alphas and equal_content

很奇怪,我得到了一个失败:AssertionError: comparison of imgs failed: diff.getbbox()=None,但我几乎确定它应该可以工作。为什么会这样呢? - Charlie Parker
不知道,也许这些图像有些异常。如果你正在运行调试器,你可能可以找到代码偏离正常路径的点。 - Toren Darby
如果 alpha 通道具有不匹配的颜色,例如 (0,0,0,0) 和 (0,255,0,0),则此方法将无法正常工作。由于这个缺陷,我不得不实现一个不同的解决方案。 - jramm

4

对于我来说确实有效,您只需要设置接受的不同像素的数量即可,在我的情况下是100,因为图像差异是完全黑色的图像,但仍然有25个不同的像素。我测试了其他完全不同的图像,它们有成千上万个不同的像素。

from PIL import ImageChops
if len(set(ImageChops.difference(img1, img2).getdata())) > 100:
    print("Both images are diffent!")

4

使用Pillow实现Viktor的答案的另一种方法:

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

注意:

如果图像具有alpha通道或使用不同的模式,则此方法可能无法正常工作。


如果图像具有 alpha 通道(https://github.com/python-pillow/Pillow/issues/4849),或者使用不同的模式(https://pillow.readthedocs.io/en/stable/handbook/concepts.html#modes),则此方法可能无法正常工作。 - Yay295
im1、im2的类型是什么? - Charlie Parker
@CharlieParker 返回Image.open()的任何内容。更准确地说,是Image. - Michael

1

您可以结合使用ImageChops.differencegetbbox

from PIL import Image
from PIL import ImageChops

def are_equal(image_a: Image, image_b: Image) -> bool:
  diff = ImageChops.difference(image_a, image_b)
  channels = diff.split()
  for channel in channels:
    if channel.getbbox() is not None:
      return False
  return True

我们需要拆分通道的原因是由于可能存在 alpha 通道。如果您有两个具有完全相同 alpha 通道的图像(例如,两者都是完全不透明的),那么 ImageChops.difference 将生成一个 alpha 通道在整个图像上为零的图像。这意味着对于图像的像素值,我们忽略其他通道,因为我们知道最终像素只是黑色的。因此,两个完全不透明图像的差异的 bbox 是 None,这不是我们想要的。

0
只是为了比较一下,通过numpy检查所有像素的像素信息是否相同,以确定所有像素数据是否相同。
from pathlib import Path
from PIL import Image
import numpy as np
import PySimpleGUI as sg

def same(path1, path2):
    try:
        im1, im2 = Image.open(path1), Image.open(path2)
    except Exception as e:
        return e
    ar1, ar2 = np.asarray(im1), np.asarray(im2)
    im1.close(), im2.close()
    return "Same Images" if np.array_equal(ar1, ar2) else "Different Images"

layout = [
    [sg.Text("Image File 1"), sg.Input(key='File 1'), sg.FileBrowse()],
    [sg.Text("Image File 2"), sg.Input(key='File 2'), sg.FileBrowse()],
    [sg.Push(), sg.Button("Check"), sg.Push()],
    [sg.Text("(Result)", size=0, justification='center', expand_x=True, key='Result')],
]
window = sg.Window('Comparison for two Image files', layout)

while True:

    event, values = window.read()

    if event == sg.WIN_CLOSED:
        break
    elif event == 'Check':
        window['Result'].update(same(values["File 1"], values["File 2"]))

window.close()

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