Сравнение изображения в URL-адресе с изображением в файловой системе в python

Есть ли быстрый и простой способ сделать такое сравнение?

Я нашел несколько вопросов о сравнении изображений из stackoverflow, но ни один из них не подтвердил ответ на этот вопрос.

У меня есть файлы изображений в моей файловой системе и скрипт, который извлекает изображения из URL-адресов. Я хочу проверить, совпадает ли изображение в URL-адресе с диском. Обычно я загружаю изображение на диск и URL-адрес объекта PIL и использую следующую функцию, которую я нашел:

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

но это не работает, если у вас есть изображение, сохраненное на диске с помощью PIL, поскольку оно сжимается, даже если вы устанавливаете качество на 100 im1.save(outfile,quality=100).

В настоящее время мой код выглядит следующим образом: http://pastebin.com/295kDMsp, но изображение всегда сохраняется повторно.


person Oskari Kantoniemi    schedule 14.12.2012    source источник
comment
Используйте формат изображения без потерь, а не JPEG. (Но если все, что вы делаете, это проверяете точную идентичность, вместо этого сравнивайте хэши изображений.)   -  person Gareth Rees    schedule 14.12.2012
comment
Сравните суммы md5 двух файлов   -  person Alexey Kachayev    schedule 14.12.2012
comment
Я попытался сравнить суммы md5, но после того, как я наконец нашел способ получить md5 из файла в URL-адресе без сохранения его на диск, у них оказалась другая сумма. Если у вас есть пример того, как сравнить суммы для файла на диске и для файла в URL-адресе без сохранения его на диск, опубликуйте, пожалуйста :) Сохранение каждого файла на диск для сравнения приведет к прекращению дискового ввода-вывода, поскольку он проходит через 13 тыс. изображений.   -  person Oskari Kantoniemi    schedule 14.12.2012
comment
Если изображения имеют хоть малейшую разницу, то брать сумму md5 — очень никчемная затея. Есть гораздо лучшие идеи для измерения схожести изображений, я включу одну из них позже.   -  person mmgp    schedule 14.12.2012


Ответы (3)


Название вопроса предполагает, что у вас есть два точных изображения для сравнения, и это делается тривиально. Теперь, если у вас есть похожие изображения для сравнения, это объясняет, почему вы не нашли полностью удовлетворительного ответа: нет метрики, применимой к каждой проблеме, которая дает ожидаемые результаты (обратите внимание, что ожидаемые результаты различаются в зависимости от приложения). Одна из проблем заключается в том, что трудно — в том смысле, что нет общего согласия — сравнивать изображения с несколькими полосами, например цветные изображения. Чтобы справиться с этим, я рассмотрю применение данной метрики в каждой полосе, и результатом этой метрики будет наименьшее результирующее значение. Это предполагает, что метрика имеет хорошо установленный диапазон, например [0, 1], и максимальное значение в этом диапазоне означает, что изображения идентичны (по данной метрике). И наоборот, минимальное значение означает, что изображения совершенно разные.

Итак, все, что я сделаю здесь, это дам вам две метрики. Один из них — SSIM, а другой я назову NRMSE (нормализация корня из среднеквадратичной ошибки). Я решил представить второй, потому что это очень простой метод, и его может быть достаточно для вашей проблемы.

Давайте начнем с примеров. Изображения расположены в следующем порядке: f = исходное изображение в формате PNG, g1 = JPEG с качеством 50 % f (сделано с помощью convert f -quality 50 g), g2 = качество JPEG 1 % f, h = «осветленное» g2.

введите здесь описание изображениявведите здесь описание изображения введите здесь описание изображениявведите здесь описание изображения

Результаты (округленные):

  • NRMSE(f, g1) = 0,96
  • NRMSE(f, g2) = 0,88
  • NRMSE (f, ч) = 0,63
  • SSIM(f, g1) = 0,98
  • SSIM(f, g2) = 0,81
  • SSIM(f, ч) = 0,55

В некотором смысле обе метрики хорошо обрабатывали модификации, но SSIM оказался более разумным, сообщая о меньшем сходстве, когда изображения были фактически визуально различны, и сообщая о более высоком значении, когда изображения были визуально очень похожи. В следующем примере рассматривается цветное изображение (f = исходное изображение, а g = JPEG с качеством 5%).

введите здесь описание изображениявведите здесь описание изображения

  • NRMSE (f, г) = 0,92
  • SSIM(f,g) = 0,61

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

Теперь метрики. То, что я назвал NRMSE, это просто 1 — [RMSE / (maxvalminval)]. Где maxval — максимальная интенсивность двух сравниваемых изображений и, соответственно, одинаковая для minval. RMSE задается квадратным корнем MSE: sqrt[(sum(A - B) ** 2) / |A|], где |A| означает количество элементов в A. При этом максимальное значение, заданное RMSE, равно maxval. Если вы хотите лучше понять значение MSE в изображениях, см., например, https://ece.uwaterloo.ca/~z70wang/publications/SPM09.pdf. Метрика SSIM (Structural SIMilarity) более сложная, и вы можете найти подробности в приведенной ранее ссылке. Чтобы легко применять метрики, рассмотрим следующий код:

import numpy
from scipy.signal import fftconvolve

def ssim(im1, im2, window, k=(0.01, 0.03), l=255):
    """See https://ece.uwaterloo.ca/~z70wang/research/ssim/"""
    # Check if the window is smaller than the images.
    for a, b in zip(window.shape, im1.shape):
        if a > b:
            return None, None
    # Values in k must be positive according to the base implementation.
    for ki in k:
        if ki < 0:
            return None, None

    c1 = (k[0] * l) ** 2
    c2 = (k[1] * l) ** 2
    window = window/numpy.sum(window)

    mu1 = fftconvolve(im1, window, mode='valid')
    mu2 = fftconvolve(im2, window, mode='valid')
    mu1_sq = mu1 * mu1
    mu2_sq = mu2 * mu2
    mu1_mu2 = mu1 * mu2
    sigma1_sq = fftconvolve(im1 * im1, window, mode='valid') - mu1_sq
    sigma2_sq = fftconvolve(im2 * im2, window, mode='valid') - mu2_sq
    sigma12 = fftconvolve(im1 * im2, window, mode='valid') - mu1_mu2

    if c1 > 0 and c2 > 0:
        num = (2 * mu1_mu2 + c1) * (2 * sigma12 + c2)
        den = (mu1_sq + mu2_sq + c1) * (sigma1_sq + sigma2_sq + c2)
        ssim_map = num / den
    else:
        num1 = 2 * mu1_mu2 + c1
        num2 = 2 * sigma12 + c2
        den1 = mu1_sq + mu2_sq + c1
        den2 = sigma1_sq + sigma2_sq + c2
        ssim_map = numpy.ones(numpy.shape(mu1))
        index = (den1 * den2) > 0
        ssim_map[index] = (num1[index] * num2[index]) / (den1[index] * den2[index])
        index = (den1 != 0) & (den2 == 0)
        ssim_map[index] = num1[index] / den1[index]

    mssim = ssim_map.mean()
    return mssim, ssim_map


def nrmse(im1, im2):
    a, b = im1.shape
    rmse = numpy.sqrt(numpy.sum((im2 - im1) ** 2) / float(a * b))
    max_val = max(numpy.max(im1), numpy.max(im2))
    min_val = min(numpy.min(im1), numpy.min(im2))
    return 1 - (rmse / (max_val - min_val))


if __name__ == "__main__":
    import sys
    from scipy.signal import gaussian
    from PIL import Image

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

    if img1.size != img2.size:
        print "Error: images size differ"
        raise SystemExit

    # Create a 2d gaussian for the window parameter
    win = numpy.array([gaussian(11, 1.5)])
    win2d = win * (win.T)

    num_metrics = 2
    sim_index = [2 for _ in xrange(num_metrics)]
    for band1, band2 in zip(img1.split(), img2.split()):
        b1 = numpy.asarray(band1, dtype=numpy.double)
        b2 = numpy.asarray(band2, dtype=numpy.double)
        # SSIM
        res, smap = ssim(b1, b2, win2d)

        m = [res, nrmse(b1, b2)]
        for i in xrange(num_metrics):
            sim_index[i] = min(m[i], sim_index[i])

    print "Result:", sim_index

Обратите внимание, что ssim отказывается сравнивать изображения, когда заданное window больше их. window обычно очень маленький, по умолчанию 11x11, поэтому, если ваши изображения меньше этого размера, нет особой «структуры» (из названия метрики) для сравнения, и вам следует использовать что-то еще (например, другую функцию nrmse) . Вероятно, есть лучший способ реализовать ssim, так как в Matlab это работает намного быстрее.

person mmgp    schedule 14.12.2012
comment
Этот ответ объясняет, как использовать метод compare_ssim библиотеки scikit-image stackoverflow.com/a/52207748/3337089 - person Nagabhushan S N; 21.11.2018

Вы можете провести собственное сравнение, используя квадратную разницу. Затем вы установите порог, например 95%, и если они так похожи, вам не нужно его загружать. Это устраняет проблему сжатия

person Bartlomiej Lewandowski    schedule 14.12.2012
comment
Есть ли у вас примеры (мне лично интересно!) - person fish2000; 14.12.2012
comment
Я попытался вычислить среднеквадратичную разницу, но это привело к разнице в 2000 между гистограммами изображения, загруженного с URL-адреса и из файловой системы. - person Oskari Kantoniemi; 14.12.2012
comment
попробуйте загрузить то же изображение, а затем изменить несколько пикселей, чтобы увидеть разницу. - person Bartlomiej Lewandowski; 14.12.2012

В соответствии с предложением Bartlomiej Lewandowski я бы рекомендовал сравнить энтропию гистограммы, которую легко и относительно быстро вычислить:

def histogram_entropy(im):
    """ Calculate the entropy of an images' histogram.
    Used for "smart cropping" in easy-thumbnails;
    see also https://raw.github.com/SmileyChris/easy-thumbnails/master/easy_thumbnails/utils.py
    """
    if not isinstance(im, Image.Image):
        return 0  # Fall back to a constant entropy.

    histogram = im.histogram()
    hist_ceil = float(sum(histogram))
    histonorm = [histocol / hist_ceil for histocol in histogram]

... Эту функцию я использую в фильтр auto-square-crop, который я построил, но вы можете использовать значение энтропии для сравнения любых двух изображений (даже разного размера).

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

person fish2000    schedule 14.12.2012
comment
Ну, эта функция, кажется, возвращает что-то полезное. Я получил значение 5.74535571765 из файла на диске и значение 4.85352821002 из файла, прочитанного с URL-адреса. Как я должен сравнить эти значения и когда решить, похожи ли файлы или нет? - person Oskari Kantoniemi; 14.12.2012
comment
Проблема с этим сравнением гистограмм заключается в том, что я могу иметь совершенно разные изображения (например, просто отрицать оригинал) и получать точно такое же значение. - person mmgp; 14.12.2012
comment
@mmgp да, но, по всей вероятности, такое столкновение не будет проблемой в этом контексте. - person fish2000; 15.12.2012
comment
@fish2000 мой комментарий был просто упрощением о том, что метод не может правильно различать различия, что в некоторых случаях дает бессмысленные результаты. Это может иметь место для любой метрики, но я пытался указать, что эта конкретная более подвержена проблемам, не пытаясь никого обидеть. - person mmgp; 15.12.2012