Главная идея
Вариант 1. Загрузите оба изображения как массивы (scipy.misc.imread
) и вычислите поэлементную (попиксельную) разницу. Рассчитайте норму разницы.
Вариант 2: Загрузите оба изображения. Вычислите некоторый вектор признаков для каждого из них (например, гистограмму). Вычисляйте расстояние между векторами признаков, а не изображениями.
Однако сначала нужно принять некоторые решения.
Вопросы
Сначала вам следует ответить на эти вопросы:
Изображения одинаковой формы и размеров?
В противном случае вам может потребоваться изменить их размер или обрезать их. Библиотека PIL поможет сделать это на Python.
Если они сделаны с одинаковыми настройками и одним и тем же устройством, вероятно, они одинаковы.
Хорошо ли выровнены изображения?
В противном случае вы можете сначала запустить кросс-корреляцию, чтобы сначала найти наилучшее выравнивание. У SciPy есть функции для этого.
Если камера и сцена неподвижны, изображения, скорее всего, будут хорошо совмещены.
Всегда ли экспозиция изображений одинакова? (Яркость / контраст одинаковы?)
В противном случае вы можете захотеть нормализовать изображения.
Но будьте осторожны, в некоторых ситуациях это может принести больше вреда, чем пользы. Например, один яркий пиксель на темном фоне сильно изменит нормализованное изображение.
Важна ли информация о цвете?
Если вы хотите заметить изменения цвета, у вас будет вектор значений цвета на точку, а не скалярное значение, как в полутоновом изображении. При написании такого кода вам нужно больше внимания.
Есть ли на изображении четкие края? Вероятны ли они переехать?
Если да, вы можете сначала применить алгоритм обнаружения краев (например, вычислить градиент с помощью преобразования Собела или Превитта, применить некоторый порог), а затем сравнить края на первом изображении с краями на втором.
Есть ли на изображении шум?
Все датчики загрязняют изображение некоторым количеством шумов. У недорогих датчиков больше шума. Возможно, вы захотите применить некоторое шумоподавление, прежде чем сравнивать изображения. Размытие - самый простой (но не лучший) подход.
Какие изменения вы хотите заметить?
Это может повлиять на выбор нормы для разницы между изображениями.
Рассмотрите возможность использования нормы Манхэттена (сумма абсолютных значений) или нулевой нормы (количество элементов, не равных нулю), чтобы измерить, насколько изменилось изображение. Первый сообщит вам, насколько сильно изображение выключено, второй - только на сколько пикселей различается.
Пример
Я предполагаю, что ваши изображения хорошо выровнены, одинакового размера и формы, возможно, с разной экспозицией. Для простоты я конвертирую их в оттенки серого, даже если это цветные (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()
Теперь вы можете поместить все это в сценарий и запустить с двумя изображениями. Если сравнить изображение с собой, разницы нет:
$ 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.
Обновление: актуальные методы
Поскольку вопрос касается видеопоследовательности, где кадры, вероятно, будут почти одинаковыми, и вы ищете что-то необычное, я хотел бы упомянуть некоторые альтернативные подходы, которые могут быть актуальны:
- вычитание и сегментация фона (для обнаружения объектов переднего плана)
- разреженный оптический поток (для обнаружения движения)
- сравнение гистограмм или другой статистики вместо изображений
Я настоятельно рекомендую взглянуть на книгу «Изучение OpenCV», главы 9 (части изображения и сегментация) и 10 (отслеживание и движение). Первый учит использовать метод вычитания фона, второй дает некоторую информацию о методах оптического потока. Все методы реализованы в библиотеке OpenCV. Если вы используете Python, я предлагаю использовать OpenCV ≥ 2.3 и его cv2
модуль Python.
Самый простой вариант вычитания фона:
- узнать среднее значение μ и стандартное отклонение σ для каждого пикселя фона
- сравнить текущие значения пикселей с диапазоном (μ-2σ, μ + 2σ) или (μ-σ, μ + σ)
Более продвинутые версии учитывают временные ряды для каждого пикселя и обрабатывают нестатические сцены (например, движущиеся деревья или траву).
Идея оптического потока состоит в том, чтобы взять два или более кадров и назначить вектор скорости каждому пикселю (плотный оптический поток) или некоторым из них (разреженный оптический поток). Чтобы оценить разреженный оптический поток, вы можете использовать метод Лукаса-Канаде (it также реализован в OpenCV). Очевидно, что если есть большой поток (высокое среднее значение по максимальным значениям поля скорости), то что-то движется в кадре, и последующие изображения будут более разными.
Сравнение гистограмм может помочь обнаружить внезапные изменения между последовательными кадрами. Этот подход использовался в Courbon et al, 2010:
Сходство последовательных кадров. Измеряется расстояние между двумя последовательными кадрами. Если он слишком высокий, это означает, что второй кадр поврежден и, таким образом, изображение удалено. расстояние Кульбака – Лейблера или взаимная энтропия на гистограммах два кадра:
где p и q - гистограммы используемых кадров. Порог зафиксирован на 0,2.
person
sastanin
schedule
14.10.2010