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