Как я могу количественно определить разницу между двумя изображениями?

Вот что я хотел бы сделать:

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

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

Я ищу простоту, а не совершенство. Я использую python.

Ответы

Ответ 1

Общая идея

Вариант 1. Загрузите оба изображения в виде массивов (scipy.misc.imread) и вычислите разницу по элементам (по пикселям). Вычислите норму разницы.

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

Однако сначала необходимо принять некоторые решения.

Вопросы

Сначала вы должны ответить на эти вопросы:

  • Имеются ли изображения одинаковой формы и размера?

    Если нет, вам может потребоваться изменить размер или обрезать их. Библиотека PIL поможет сделать это на Python.

    Если они взяты с одинаковыми настройками и одним и тем же устройством, они, вероятно, совпадают.

  • Правильно ли выровнены изображения?

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

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

  • Является ли экспозиция изображений одинаковой? (Является ли легкость/контраст одинаковой?)

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

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

  • Важная информация о цвете?

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

  • Существуют ли четкие ребра на изображении? Скорее всего, они будут двигаться?

    Если да, вы можете сначала применить алгоритм обнаружения края (например, рассчитать градиент с преобразованием Sobel или Prewitt, применить некоторый порог), а затем сравнить ребра на первом изображении с ребрами на втором.

  • Есть ли шум в изображении?

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

  • Какие изменения вы хотите заметить?

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

    Рассмотрим, используя норму Манхэттена (сумму абсолютных значений) или нулевую норму (число элементов, не равных нулю), чтобы измерить, насколько изменилось изображение. Первый расскажет вам, сколько изображений выключено, последнее будет показывать только, сколько пикселей отличается.

Пример

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

Вам понадобится импорт:

import sys

from scipy.misc import imread
from scipy.linalg import norm
from scipy import sum, average

Основная функция, чтение двух изображений, преобразование в оттенки серого, сравнение и печать результатов:

def main():
    file1, file2 = sys.argv[1:1+2]
    # read images as 2D arrays (convert to grayscale for simplicity)
    img1 = to_grayscale(imread(file1).astype(float))
    img2 = to_grayscale(imread(file2).astype(float))
    # compare
    n_m, n_0 = compare_images(img1, img2)
    print "Manhattan norm:", n_m, "/ per pixel:", n_m/img1.size
    print "Zero norm:", n_0, "/ per pixel:", n_0*1.0/img1.size

Как сравнить. img1 и img2 являются массивами 2D SciPy:

def compare_images(img1, img2):
    # normalize to compensate for exposure difference, this may be unnecessary
    # consider disabling it
    img1 = normalize(img1)
    img2 = normalize(img2)
    # calculate the difference and its norms
    diff = img1 - img2  # elementwise for scipy arrays
    m_norm = sum(abs(diff))  # Manhattan norm
    z_norm = norm(diff.ravel(), 0)  # Zero norm
    return (m_norm, z_norm)

Если файл является цветным изображением, imread возвращает трехмерный массив, средний канал RGB (последняя ось массива), чтобы получить интенсивность. Нет необходимости делать это для изображений в оттенках серого (например, .pgm):

def to_grayscale(arr):
    "If arr is a color image (3D array), convert it to grayscale (2D array)."
    if len(arr.shape) == 3:
        return average(arr, -1)  # average over the last axis (color channels)
    else:
        return arr

Нормализация тривиальная, вы можете выбрать нормализовать значение [0,1] вместо [0,255]. arr - это массив SciPy здесь, поэтому все операции по элементам:

def normalize(arr):
    rng = arr.max()-arr.min()
    amin = arr.min()
    return (arr-amin)*255/rng

Запустите функцию main:

if __name__ == "__main__":
    main()

Теперь вы можете поместить все это в script и выполнить против двух изображений. Если мы сравним изображение с самим собой, нет никакой разницы:

$ python compare.py one.jpg one.jpg
Manhattan norm: 0.0 / per pixel: 0.0
Zero norm: 0 / per pixel: 0.0

Если мы размываем изображение и сравниваем его с оригиналом, есть разница:

$ python compare.py one.jpg one-blurred.jpg 
Manhattan norm: 92605183.67 / per pixel: 13.4210411116
Zero norm: 6900000 / per pixel: 1.0

P.S. Целый compare.py script.

Обновление: соответствующие методы

В качестве вопроса о видеопоследовательности, где кадры, вероятно, будут почти одинаковыми, и вы ищете что-то необычное, я хотел бы упомянуть некоторые альтернативные подходы, которые могут иметь значение:

  • вычитание и сегментация фона (для обнаружения объектов переднего плана)
  • разреженный оптический поток (для обнаружения движения)
  • сравнение гистограмм или другой статистики вместо изображений

Я настоятельно рекомендую взглянуть на книгу "Обучение OpenCV", главы 9 (части изображения и сегментация) и 10 (отслеживание и движение). Первый учит использовать метод вычитания фона, последний дает некоторую информацию о методах оптического потока. Все методы реализованы в библиотеке OpenCV. Если вы используете Python, я предлагаю использовать OpenCV ≥ 2.3 и его модуль cv2 Python.

Самая простая версия вычитания фона:

  • узнать среднее значение μ и стандартное отклонение σ для каждого пикселя фона
  • сравнить текущие значения пикселей с диапазоном (μ-2σ, μ + 2σ) или (μ-σ, μ + σ)

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

Идея оптического потока состоит в том, чтобы взять два или более кадров и назначить вектор скорости каждому пикселю (плотный оптический поток) или некоторым из них (разреженный оптический поток). Чтобы оценить разреженный оптический поток, вы можете использовать метод Lucas-Kanade (он также реализован в OpenCV). Очевидно, что если есть большой поток (высокий средний по максимальным значениям поля скорости), то что-то движется в кадре, а последующие изображения более разные.

Сравнение гистограмм может помочь обнаружить внезапные изменения между последовательными кадрами. Этот подход использовался в Courbon et al, 2010:

Сходство последовательных кадров. Измеряется расстояние между двумя последовательными кадрами. Если он слишком высок, это означает, что второй кадр поврежден и, следовательно, изображение устранено. расстояние Kullback-Leibler или взаимная энтропия на гистограммах двух кадров:

$$ d(p,q) = \sum_i p(i) \log (p(i)/q(i)) $$

где p и q - гистограммы кадров. Порог зафиксирован на 0.2.

Ответ 2

Простое решение:

Кодировать изображение как jpeg и искать существенное изменение filesize.

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

Ответ 3

Вы можете сравнить два изображения с помощью функций PIL.

import Image
import ImageChops

im1 = Image.open("splash.png")
im2 = Image.open("splash2.png")

diff = ImageChops.difference(im2, im1)

Объект diff - это изображение, в котором каждый пиксель является результатом вычитания значений цвета этого пикселя во втором изображении из первого изображения. Используя diff-образ, вы можете сделать несколько вещей. Самый простой - это функция diff.getbbox(). Он скажет вам минимальный прямоугольник, содержащий все изменения между двумя вашими изображениями.

Возможно, вы, возможно, реализуете аппроксимации другого материала, упомянутого здесь, также используя функции из PIL.

Ответ 4

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

Используя синтаксис типа numpy,

dist_euclidean = sqrt(sum((i1 - i2)^2)) / i1.size

dist_manhattan = sum(abs(i1 - i2)) / i1.size

dist_ncc = sum( (i1 - mean(i1)) * (i2 - mean(i2)) ) / (
  (i1.size - 1) * stdev(i1) * stdev(i2) )

предполагая, что i1 и i2 представляют собой массивы изображений в градациях серого.

Ответ 5

Тривиальная вещь, которую нужно попробовать:

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

Ответ 6

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

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

Тогда для "реального" теста вам нужен такой критерий:

  • то же самое, если до P пикселей отличаются не более чем на E.

Итак, возможно, если E = 0,02, P = 1000, это будет означать (приблизительно), что он будет "другим", если какой-либо один пиксель изменится более чем на 5 единиц (при условии 8-битных изображений) или если более 1000 пикселей имели какие-либо ошибки.

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

Я запускаю проект с открытым исходным кодом, OpenImageIO, содержащий утилиту с именем "idiff", которая сравнивает различия с такими порогами (даже на самом деле, более подробно). Даже если вы не хотите использовать это программное обеспечение, вы можете посмотреть источник, чтобы узнать, как мы это сделали. Он использовался в коммерческих целях совсем немного, и этот метод определения порога был разработан таким образом, что у нас может быть набор тестов для программного обеспечения для обработки изображений и обработки изображений с "опорными изображениями", которые могут иметь небольшие отличия от платформы к платформе или, поскольку мы сделали небольшие изменения для tha алгоритмов, поэтому мы хотели выполнить операцию "соответствие в пределах допуска".

Ответ 7

Большинство ответов не будут касаться уровней освещения.

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

Ответ 8

Еще один приятный, простой способ измерить сходство двух изображений:

import sys
from skimage.measure import compare_ssim
from skimage.transform import resize
from scipy.ndimage import imread

# get two images - resize both to 1024 x 1024
img_a = resize(imread(sys.argv[1]), (2**10, 2**10))
img_b = resize(imread(sys.argv[2]), (2**10, 2**10))

# score: {-1:1} measure of the structural similarity between the images
score, diff = compare_ssim(img_a, img_b, full=True)
print(score)

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

Ответ 9

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

https://github.com/nicolashahn/diffimg

Он работает с изображениями одинакового размера и на уровне пикселей, измеряет разницу значений в каждом канале: R, G, B (, A), берет среднюю разницу этих каналов и затем усредняет разницу по все пиксели и возвращает соотношение.

Например, для 10х10 изображений с белыми пикселями и того же изображения, но с одним пикселем, он стал красным, разница в этом пикселе составляет 1/3 или 0,33... (RGB 0,0,0 против 255,0,0 ) и для всех остальных пикселей равен 0. При 100 пикселях всего 0,33.../100 = разность изображения ~ 0,33%.

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

Ответ 10

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

Я бы предложил вейвлет-преобразование ваших кадров (я написал расширение C для использования с преобразованием Хаара); то, сравнивая индексы наибольших (пропорционально) вейвлет-коэффициентов между двумя картинками, вы должны получить приближение численного приближения.

Ответ 11

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

Может быть, с OpenCV вы могли бы использовать сопоставление с шаблоном. Предполагая, что вы используете веб-камеру, как вы сказали:

  1. Упростить изображения (возможно, пороговое значение?)
  2. Примените сопоставление шаблона и проверьте max_val с помощью minMaxLoc.

Совет: max_val (или min_val в зависимости от используемого метода) даст вам числа, большие числа. Чтобы получить разницу в процентах, используйте сопоставление шаблонов с тем же изображением - результат будет вашим 100%.

Псевдокод для примера:

previous_screenshot = ...
current_screenshot = ...

# simplify both images somehow

# get the 100% corresponding value
res = matchTemplate(previous_screenshot, previous_screenshot, TM_CCOEFF)
_, hundred_p_val, _, _ = minMaxLoc(res)

# hundred_p_val is now the 100%

res = matchTemplate(previous_screenshot, current_screenshot, TM_CCOEFF)
_, max_val, _, _ = minMaxLoc(res)

difference_percentage = max_val / hundred_p_val

# the tolerance is now up to you

Надеюсь, поможет.

Ответ 12

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

Ответ 13

Как насчет вычисления Manhattan Distance двух изображений. Это дает вам n * n значений. Затем вы можете сделать что-то вроде среднего значения строки, чтобы уменьшить до n значений и функцию, чтобы получить одно значение.

Ответ 15

Мне очень повезло с jpg-изображениями, снятыми с той же камерой на штативе (1) значительно упрощает (например, от 3000 пикселей до 100 пикселей в ширину или даже меньше) (2) сглаживание каждого массива jpg в один вектор (3) парное коррелирование последовательных изображений с помощью простого корреляционного алгоритма для получения коэффициента корреляции (4) квадратичный коэффициент корреляции для получения r-квадрата (т.е. Доля изменчивости на одном изображении объясняется изменением в следующем) (5), как правило, в моей заявке, если r-квадрат < 0.9, я говорю, что два изображения отличаются друг от друга, и что-то произошло между ними.

Это надежное и быстрое в моей реализации (Mathematica 7)

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

Я не знаю, как использовать Python, но уверен, что это тоже корреляции, нет?

Ответ 16

вы можете вычислить гистограмму обоих изображений, а затем вычислить Bhattacharyya Coefficient, это очень быстрый алгоритм, и я использовал его для обнаружение изменений выстрела в крикетном видео (в C с использованием openCV)

Ответ 17

Посмотрите, как Haave Wavelets реализованы isk-daemon. Вы можете использовать код imgdb С++ для вычисления разницы между изображениями "на лету":

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

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

Ответ 18

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

Вы можете получить либо этот счет напрямую, либо процентное значение по сравнению с полным черным против белого.

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

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

https://pypi.python.org/pypi/imgcompare/

Ответ 19

import os
from PIL import Image
from PIL import ImageFile
import imagehash

#just use to the size diferent picture
def compare_image(img_file1, img_file2):
    if img_file1 == img_file2:
        return True
    fp1 = open(img_file1, 'rb')
    fp2 = open(img_file2, 'rb')

    img1 = Image.open(fp1)
    img2 = Image.open(fp2)

    ImageFile.LOAD_TRUNCATED_IMAGES = True
    b = img1 == img2

    fp1.close()
    fp2.close()

    return b





#through picturu hash to compare
def get_hash_dict(dir):
    hash_dict = {}
    image_quantity = 0
    for _, _, files in os.walk(dir):
        for i, fileName in enumerate(files):
            with open(dir + fileName, 'rb') as fp:
                hash_dict[dir + fileName] = imagehash.average_hash(Image.open(fp))
                image_quantity += 1

    return hash_dict, image_quantity

def compare_image_with_hash(image_file_name_1, image_file_name_2, max_dif=0):
    """
    max_dif: The maximum hash difference is allowed, the smaller and more accurate, the minimum is 0.
    recommend to use
    """
    ImageFile.LOAD_TRUNCATED_IMAGES = True
    hash_1 = None
    hash_2 = None
    with open(image_file_name_1, 'rb') as fp:
        hash_1 = imagehash.average_hash(Image.open(fp))
    with open(image_file_name_2, 'rb') as fp:
        hash_2 = imagehash.average_hash(Image.open(fp))
    dif = hash_1 - hash_2
    if dif < 0:
        dif = -dif
    if dif <= max_dif:
        return True
    else:
        return False


def compare_image_dir_with_hash(dir_1, dir_2, max_dif=0):
    """
    max_dif: The maximum hash difference is allowed, the smaller and more accurate, the minimum is 0.

    """
    ImageFile.LOAD_TRUNCATED_IMAGES = True
    hash_dict_1, image_quantity_1 = get_hash_dict(dir_1)
    hash_dict_2, image_quantity_2 = get_hash_dict(dir_2)

    if image_quantity_1 > image_quantity_2:
        tmp = image_quantity_1
        image_quantity_1 = image_quantity_2
        image_quantity_2 = tmp

        tmp = hash_dict_1
        hash_dict_1 = hash_dict_2
        hash_dict_2 = tmp

    result_dict = {}

    for k in hash_dict_1.keys():
        result_dict[k] = None

    for dif_i in range(0, max_dif + 1):
        have_none = False

        for k_1 in result_dict.keys():
            if result_dict.get(k_1) is None:
                have_none = True

        if not have_none:
            return result_dict

        for k_1, v_1 in hash_dict_1.items():
            for k_2, v_2 in hash_dict_2.items():
                sub = (v_1 - v_2)
                if sub < 0:
                    sub = -sub
                if sub == dif_i and result_dict.get(k_1) is None:
                    result_dict[k_1] = k_2
                    break
    return result_dict


def main():
    print(compare_image('image1\\815.jpg', 'image2\\5.jpg'))
    print(compare_image_with_hash('image1\\815.jpg', 'image2\\5.jpg', 6))
    r = compare_image_dir_with_hash('image1\\', image2\\', 10)
    for k in r.keys():
        print(k, r.get(k))


if __name__ == '__main__':
    main()
  • выход:

    Ложь
    Правда
    image2\5.jpg image1\815.jpg
    image2\6.jpg image1\819.jpg
    image2\7.jpg image1\900.jpg
    image2\8.jpg image1\998.jpg
    image2\9.jpg image1\1012.jpg

  • примеры фотографий:

    • 815.jpg
      815.jpg

    • 5.jpg
      5.jpg

Ответ 20

Несколько более принципиальный подход заключается в использовании глобального дескриптора для сравнения изображений, таких как GIST или CENTRIST. Хеш-функция, как описано здесь, также предоставляет аналогичное решение.

Ответ 21

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

Ответ 22

Существует множество метрик для оценки того, как выглядят два изображения/как они выглядят.

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

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

Классические подходы:

Предиктор видимых различий: алгоритм оценки точности изображения (https://www.spiedigitallibrary.org/conference-proceedings-of-spie/1666/0000/Visible-differences-predictor--an-algorithm-for-the-- оценка-из /10.1117/12.135952.short? SSO = 1)

Оценка качества изображения: от видимости ошибок до структурного сходства (http://www.cns.nyu.edu/pub/lcv/wang03-reprint.pdf)

FSIM: индекс сходства функций для оценки качества изображения (https://www4.comp.polyu.edu.hk/~cslzhang/IQA/TIP_IQA_FSIM.pdf)

Среди них SSIM (оценка качества изображения: от видимости ошибки до структурного сходства) является самым простым для вычисления, и его издержки также невелики, как сообщается в другой статье "Оценка качества изображения на основе градиентного сходства" (https://www.semanticscholar.org/paper/Оценка качества изображения на основе градиента Лю-Лин /2b819bef80c02d5d4cb56f27b202535e119df988).

Есть много других подходов. Взгляните на Google Scholar и найдите что-то вроде "визуальной разницы", "оценки качества изображения" и т.д., Если вы заинтересованы/действительно заботитесь об искусстве.

Ответ 23

Существует простое и быстрое решение с использованием numpy путем вычисления среднеквадратичной ошибки:

before = np.array(get_picture())
while True:
    now = np.array(get_picture())
    MSE = np.mean((now - before)**2)

    if  MSE > threshold:
        break

    before = now