Сравните два изображения в стиле Python / Linux - PullRequest
37 голосов
/ 18 декабря 2009

Попытка решить проблему предотвращения загрузки дубликатов изображений.

У меня есть два JPG. Глядя на них, я вижу, что они на самом деле идентичны. Но по какой-то причине они имеют разный размер файла (один извлекается из резервной копии, другой - другую загрузку) и поэтому у них другая контрольная сумма md5.

Как я могу эффективно и уверенно сравнивать два изображения в том же смысле, в каком человек мог бы видеть, что они явно идентичны?

Пример: http://static.peterbe.com/a.jpg и http://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 к загружаемому файлу, ничего не делая с ним, и теперь оно работает!

Ответы [ 10 ]

25 голосов
/ 18 декабря 2009

Существует проект OSS, который использует WebDriver для создания снимков экрана, а затем сравнивает изображения, чтобы увидеть, есть ли какие-либо проблемы (http://code.google.com/p/fighting-layout-bugs/)).. Он делает это, открывая файл в поток, а затем сравнивая каждый бит.

Вы можете сделать что-то подобное с PIL .

EDIT:

После дополнительных исследований я обнаружил

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

19 голосов
/ 01 июня 2011

С здесь

Самый быстрый способ определить, имеют ли два изображения одинаковое содержимое, - это получить разницу между двумя изображениями, а затем вычислить ограничивающую рамку ненулевых областей на этом изображении. Если изображения идентичны, все пиксели в разностном изображении равны нулю, а функция ограничительной рамки возвращает None.

import ImageChops

def equal(im1, im2):
    return ImageChops.difference(im1, im2).getbbox() is None
11 голосов
/ 18 декабря 2009

Полагаю, вам следует декодировать изображения и выполнять попиксельное сравнение, чтобы убедиться, что они достаточно схожи.

С 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, если изображения совершенно одинаковые.

Обратите внимание, что смещенные / повернутые изображения будут отображаться как очень разные, поскольку пиксели не будут совпадать по одному.

10 голосов
/ 14 марта 2012

Используя ImageMagick, вы можете просто использовать в своей оболочке [или вызывать из библиотеки ОС из программы]

compare image1 image2 output

Это создаст выходное изображение с пометками

compare -metric AE -fuzz 5% image1 image2 output

Даст вам коэффициент размытости 5%, чтобы игнорировать незначительные различия в пикселях. Дополнительную информацию можно получить у здесь

4 голосов
/ 18 декабря 2009

Проблема знания того, что делает некоторые особенности изображения более важными, чем другие, представляет собой целую научную программу. Я бы предложил несколько альтернатив в зависимости от того, какое решение вы хотите:

  • если ваша проблема состоит в том, чтобы увидеть, есть ли в ваших файлах изображения биты, то попробуйте изобразить разностное изображение (возможно, было незначительное редактирование локально?),

  • чтобы увидеть, являются ли изображения в целом одинаковыми, используйте расстояние Кулбека Лейблера для сравнения ваших гистограмм,

  • , чтобы увидеть, есть ли у вас какие-либо качественные изменения, прежде чем применять другие ответы, отфильтруйте ваше изображение, используя функции ниже, чтобы повысить важность частот высокого уровня:

код:

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 )

2 голосов
/ 05 марта 2016

Я проверил этот, и он работает лучше всех методов и очень быстро!

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

здесь ссылка для справки

2 голосов
/ 18 декабря 2009

Во-первых, я должен отметить, что они не идентичны; b был повторно сжат и потерял качество. Это можно увидеть, если внимательно посмотреть на хороший монитор.

Чтобы определить, что они субъективно «одинаковы», вам нужно будет сделать что-то вроде того, что предложил Фортран, хотя вам придется произвольно установить порог для «одинаковости». Чтобы сделать s независимым от размера изображения и обрабатывать немного более разумно, я хотел бы рассмотреть вопрос о том, как сделать среднеквадратичное (евклидово) среднеквадратичное расстояние в цветовом пространстве между пикселями двух изображений. У меня нет времени писать код прямо сейчас, но в основном для каждого пикселя вы вычисляете

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

, добавив в

(A_2 - A_1) ** 2

термин, если изображение имеет альфа-канал и т. Д. Результатом является квадрат расстояния цветового пространства между двумя изображениями. Найти среднее (среднее) по всем пикселям, затем взять квадратный корень из полученного скаляра. Затем определите разумный порог для этого значения.

Или вы можете просто решить, что копии одного и того же исходного изображения с разным сжатием с потерями не являются «одинаковыми» и придерживаться хэша файла.

1 голос
/ 29 марта 2018

Я пробовал 3 метода, упомянутых выше и в других местах. Похоже, существует два основных типа сравнения изображений: пиксель за пикселем и гистограмма.

Я испробовал оба варианта, и первый пиксель потерпел неудачу на 100%, как и должно быть, как если бы мы смещали второе изображение на 1 пиксель, все пиксели не будут совпадать, и у нас не будет совпадения на 100%.

Но сравнение гистограмм в теории должно работать очень хорошо, но это не так.

Вот два изображения со слегка смещенным портом просмотра, и гистограмма выглядит на 99% похожей, но алгоритм выдает результат, который говорит: «Очень разные»

1010 * Сосредоточенный *

То же, но смещено ~ 15º

4 различных алгоритма результата:

  • Идеальное совпадение: False
  • Разница в пикселях: 115816402
  • Сравнение гистограммы: 83,69564286668303
  • HistComparison: 1744.8160719686186

И то же сравнение первого изображения (в центре QR) с другим изображением, на 100%:

Совершенно другое изображение и гистограмма

Результаты алгоритма:

  • Идеальное совпадение: False
  • Разница в пикселях: 207893096
  • Сравнение гистограммы: 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"))
1 голос
/ 18 декабря 2009

Вы можете сравнить его, используя PIL (перебрать пиксели / сегменты изображения и сравнить) или, если вы ищете полное идентичное сравнение копий, попробуйте сравнить MD5-хэш обоих файлов.

0 голосов
/ 20 марта 2019

Используя только 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 к вычислению квадратов весит каждый цвет в равной степени. С помощью среднеквадратичных формул многих предыдущих ответов значения синего пикселя в 3 раза превышают вес значений красного, а зеленого пикселя - в 2 раза вес красного значения.

Проще грокать. Хотя среднеквадратичное вычисление может быть записано в виде одной строки с лямбдами и методом сокращения, расширение его до 3 строк значительно улучшает читаемость с первого взгляда.

Этот код правильно обнаруживает, что повернутые изображения отличаются от по-разному ориентированного базового изображения. Это позволяет избежать ошибок при использовании гистограмм для сравнения изображений, на что указывает @musicinmybrain. Если создаются гистограммы из 2 изображений, то по сравнению друг с другом, если одно изображение является вращением другого, сравнение сообщит, что в изображениях нет различий, поскольку гистограммы изображений идентичны. С другой стороны, если сначала сравниваются изображения, то создается гистограмма результатов сравнения, изображения будут сравниваться точно, даже если одно вращение другого.

Код, использованный в этом ответе, является копией / вставкой из этого code.activestate.com поста с учетом 3-го комментария, который исправляет более тяжелый вес значений зеленого и синего пикселей.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...