Подтвердить что ты не робот

Алгоритм сравнения изображений

Я пытаюсь сравнить изображения друг с другом, чтобы узнать, отличаются ли они. Сначала я попытался сделать корреляцию Pearson значений RGB, которая также неплохо работает, если фотографии не сдвинуты. Поэтому, если у вас есть 100% идентичные изображения, но одно немного перемещено, я получаю плохое значение корреляции.

Любые предложения для лучшего алгоритма?

Кстати, я собираюсь сравнить тысячу imgages...

Изменить: Вот пример моих фотографий (микроскопический):

im1:

enter image description here

im2:

enter image description here

im3:

enter image description here

im1 и im2 одинаковы, но немного сдвинуты/обрезаны, im3 следует признать совершенно разными...

Edit: Проблема решена с предложениями Питера Хансена! Работает очень хорошо! Спасибо всем! Некоторые результаты можно найти здесь http://labtools.ipk-gatersleben.de/image%20comparison/image%20comparision.pdf

4b9b3361

Ответ 1

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

Есть также ссылки на еще более ранние вопросы, которые содержат еще больше ссылок и хороших ответов.

Здесь реализована реализация некоторых идей с помощью Scipy, используя ваши три изображения (сохраненные как im1.jpg, im2.jpg, im3.jpg, соответственно). Конечный результат показывает im1 по сравнению с самим собой, в качестве базовой линии, а затем каждое изображение по сравнению с другими.

>>> import scipy as sp
>>> from scipy.misc import imread
>>> from scipy.signal.signaltools import correlate2d as c2d
>>>
>>> def get(i):
...     # get JPG image as Scipy array, RGB (3 layer)
...     data = imread('im%s.jpg' % i)
...     # convert to grey-scale using W3C luminance calc
...     data = sp.inner(data, [299, 587, 114]) / 1000.0
...     # normalize per http://en.wikipedia.org/wiki/Cross-correlation
...     return (data - data.mean()) / data.std()
...
>>> im1 = get(1)
>>> im2 = get(2)
>>> im3 = get(3)
>>> im1.shape
(105, 401)
>>> im2.shape
(109, 373)
>>> im3.shape
(121, 457)
>>> c11 = c2d(im1, im1, mode='same')  # baseline
>>> c12 = c2d(im1, im2, mode='same')
>>> c13 = c2d(im1, im3, mode='same')
>>> c23 = c2d(im2, im3, mode='same')
>>> c11.max(), c12.max(), c13.max(), c23.max()
(42105.00000000259, 39898.103896795357, 16482.883608327804, 15873.465425120798)

Итак, обратите внимание, что im1 по сравнению с самим собой дает оценку 42105, im2 по сравнению с im1 не за горами, но im3 по сравнению с любым из других дает значительно меньше половины этого значения. Вам нужно будет поэкспериментировать с другими изображениями, чтобы увидеть, насколько хорошо это может произойти, и как вы можете его улучшить.

Время работы длительное... несколько минут на моей машине. Я бы попробовал некоторую предварительную фильтрацию, чтобы избежать траты времени, сравнивая очень разнородные изображения, возможно, с трюком "сравнить размер jpg файла", упомянутым в ответах на другой вопрос, или с пикселизацией. Тот факт, что у вас есть изображения разных размеров, усложняет ситуацию, но вы не указали достаточную информацию о степени отсечения, которую можно было бы ожидать, поэтому трудно дать конкретный ответ, который учитывает это.

Ответ 2

Я сделал это с помощью сравнения гистограмм изображения. Моим основным алгоритмом было следующее:

  • Разделить изображение на красный, зеленый и синий.
  • Создайте нормализованные гистограммы для красного, зеленого и синего каналов и соедините их с вектором (r0...rn, g0...gn, b0...bn), где n - количество "ведер", 256 должно быть достаточно
  • вычесть эту гистограмму из гистограммы другого изображения и рассчитать расстояние

вот какой-то код с numpy и pil

r = numpy.asarray(im.convert( "RGB", (1,0,0,0, 1,0,0,0, 1,0,0,0) ))
g = numpy.asarray(im.convert( "RGB", (0,1,0,0, 0,1,0,0, 0,1,0,0) ))
b = numpy.asarray(im.convert( "RGB", (0,0,1,0, 0,0,1,0, 0,0,1,0) ))
hr, h_bins = numpy.histogram(r, bins=256, new=True, normed=True)
hg, h_bins = numpy.histogram(g, bins=256, new=True, normed=True)
hb, h_bins = numpy.histogram(b, bins=256, new=True, normed=True)
hist = numpy.array([hr, hg, hb]).ravel()

Если у вас две гистограммы, вы можете получить расстояние следующим образом:

diff = hist1 - hist2
distance = numpy.sqrt(numpy.dot(diff, diff))

Если оба изображения идентичны, расстояние равно 0, чем больше они расходятся, тем больше расстояние.

Для моих фотографий это работало очень хорошо, но не получилось с графикой, как тексты и логотипы.

Ответ 3

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

БПФ должно быть в порядке (numpy имеет реализацию для 2D-матриц), но я всегда слышу, что Wavelets лучше для этого вид задач ^ _ ^

О производительности, если все изображения имеют одинаковый размер, если я хорошо помню, пакет FFTW создавал специализированную функцию для каждого входного размера FFT, поэтому вы можете получить хороший прирост производительности, повторно используя один и тот же код... Я не знаю, основан ли numpy на FFTW, но если это не возможно, вы можете попытаться немного расследовать там.

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

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.fft.fft2(numpy.array([p[band_index] for p in img1.getdata()]).reshape(*img1.size))
        m2 = numpy.fft.fft2(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())

Другим способом продолжения может быть размытие изображений, а затем вычитание значений пикселей из двух изображений. Если разница не равна нулю, то вы можете перенести одно из изображений 1 px в каждом направлении и сравнить снова, если разница ниже, чем на предыдущем шаге, вы можете повторить смещение в направлении градиента и вычесть до разницы ниже определенного порога или снова увеличивается. Это должно работать, если радиус размытия ядра больше, чем смещение изображений.

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

Ответ 4

Вам действительно нужно лучше задать вопрос, но, глядя на эти 5 изображений, все организмы кажутся ориентированными одинаково. Если это всегда так, вы можете попробовать сделать нормализованную взаимную корреляцию между двумя изображениями и взять максимальное значение в качестве степени сходства. Я не знаю о нормированной функции взаимной корреляции в Python, но есть функция fftconvolve(), и вы можете сделать круговой крест -корреляция:

a = asarray(Image.open('c603225337.jpg').convert('L'))
b = asarray(Image.open('9b78f22f42.jpg').convert('L'))
f1 = rfftn(a)
f2 = rfftn(b)
g =  f1 * f2
c = irfftn(g)

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

Расположение пикового значения выхода указывает смещение между двумя изображениями, а величина пика указывает на сходство. Должен быть способ взвешивания/нормализации, чтобы вы могли отличить хорошее совпадение и плохой матч.

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

Ответ 5

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

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

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

Я действительно не могу показать код, как это было давно, и я использовал Khoros/Cantata для этого курса.

Ответ 6

Во-первых, корреляция - очень интенсивная, очень неточная мера для сходства. Почему бы просто не пойти на сумму квадратов, если различия между отдельными пикселями?

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

Если вы хотите использовать некоторые усовершенствования методов обработки изображений, я предлагаю вам посмотреть SIFT, это очень мощный метод, который (теоретически в любом случае ) может правильно соответствовать элементам в изображениях, не зависящих от перевода, вращения и масштаба.

Ответ 7

Я думаю, вы могли бы сделать что-то вроде этого:

  • оценка вертикальное/горизонтальное смещение опорного изображения против изображения сравнения. простой SAD (сумма абсолютной разности) с векторами движения.

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

  • вычислить корреляцию pearson, которую вы пытались сделать

Измерение сдвига не составляет труда.

  • Возьмите область (скажем, около 32x32) в сравнении.
  • Сдвиньте его на x пикселей в горизонтальном и y пикселях в вертикальном направлении.
  • Вычислить SAD (сумма абсолютной разности) w.r.t. оригинальное изображение
  • Сделайте это для нескольких значений x и y в небольшом диапазоне (-10, +10)
  • Найдите место, где минимальная разница
  • Выберите это значение как вектор движения сдвига

Примечание:

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

Ответ 8

Чтобы импорт корректно работал на моем Ubuntu 16.04 (по состоянию на апрель 2017 года), я установил python 2.7, и они:

sudo apt-get install python-dev
sudo apt-get install libtiff5-dev libjpeg8-dev zlib1g-dev libfreetype6-dev liblcms2-dev libwebp-dev tcl8.6-dev tk8.6-dev python-tk
sudo apt-get install python-scipy
sudo pip install pillow

Затем я изменил импорт снежинок:

import scipy as sp
from scipy.ndimage import imread
from scipy.signal.signaltools import correlate2d as c2d

Как удивительно, что сценарий Снежинки работал у меня 8 лет спустя!

Ответ 9

Я предлагаю решение на основе индекса подобия Жакара на гистограммах изображений. Смотрите: https://en.wikipedia.org/wiki/Jaccard_index#Weighted_Jaccard_simility_and_distance

Вы можете вычислить разницу в распределении цветов пикселей. Это действительно довольно инвариантно для переводов.

from PIL.Image import Image
from typing import List

def jaccard_similarity(im1: Image, im2: Image) -> float:
    """Compute the similarity between two images.
    First, for each image an histogram of the pixels distribution is extracted.
    Then, the similarity between the histograms is compared using the weighted Jaccard index of similarity, defined as:
    Jsimilarity = sum(min(b1_i, b2_i)) / sum(max(b1_i, b2_i)
    where b1_i, and b2_i are the ith histogram bin of images 1 and 2, respectively.

    The two images must have same resolution and number of channels (depth).

    See: https://en.wikipedia.org/wiki/Jaccard_index
    Where it is also called Ruzicka similarity."""

    if im1.size != im2.size:
        raise Exception("Images must have the same size. Found {} and {}".format(im1.size, im2.size))

    n_channels_1 = len(im1.getbands())
    n_channels_2 = len(im2.getbands())
    if n_channels_1 != n_channels_2:
        raise Exception("Images must have the same number of channels. Found {} and {}".format(n_channels_1, n_channels_2))

    assert n_channels_1 == n_channels_2

    sum_mins = 0
    sum_maxs = 0

    hi1 = im1.histogram()  # type: List[int]
    hi2 = im2.histogram()  # type: List[int]

    # Since the two images have the same amount of channels, they must have the same amount of bins in the histogram.
    assert len(hi1) == len(hi2)

    for b1, b2 in zip(hi1, hi2):
        min_b = min(b1, b2)
        sum_mins += min_b
        max_b = max(b1, b2)
        sum_maxs += max_b

    jaccard_index = sum_mins / sum_maxs

    return jaccard_index

Что касается среднеквадратичной ошибки, индекс Жакара всегда лежит в диапазоне [0,1], что позволяет сравнивать изображения разных размеров.

Затем вы можете сравнить два изображения, но после изменения масштаба до одинакового размера! Или количество пикселей должно быть как-то нормализовано. Я использовал это:

import sys

from skincare.common.utils import jaccard_similarity

import PIL.Image
from PIL.Image import Image

file1 = sys.argv[1]
file2 = sys.argv[2]

im1 = PIL.Image.open(file1)  # type: Image
im2 = PIL.Image.open(file2)  # type: Image

print("Image 1: mode={}, size={}".format(im1.mode, im1.size))
print("Image 2: mode={}, size={}".format(im2.mode, im2.size))

if im1.size != im2.size:
    print("Resizing image 2 to {}".format(im1.size))
    im2 = im2.resize(im1.size, resample=PIL.Image.BILINEAR)

j = jaccard_similarity(im1, im2)
print("Jaccard similarity index = {}".format(j))

Тестирование на ваших изображениях:

$ python CompareTwoImages.py im1.jpg im2.jpg
Image 1: mode=RGB, size=(401, 105)
Image 2: mode=RGB, size=(373, 109)
Resizing image 2 to (401, 105)
Jaccard similarity index = 0.7238955686269157
$ python CompareTwoImages.py im1.jpg im3.jpg 
Image 1: mode=RGB, size=(401, 105)
Image 2: mode=RGB, size=(457, 121)
Resizing image 2 to (401, 105)
Jaccard similarity index = 0.22785529941822316
$ python CompareTwoImages.py im2.jpg im3.jpg 
Image 1: mode=RGB, size=(373, 109)
Image 2: mode=RGB, size=(457, 121)
Resizing image 2 to (373, 109)
Jaccard similarity index = 0.29066426814105445

Вы также можете поэкспериментировать с различными фильтрами передискретизации (такими как NEAREST или LANCZOS), поскольку они, конечно, изменяют распределение цвета при изменении размера.

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

В конце концов, кадрирование может лучше подойти для вашего случая, чем для масштабирования.