Загальна ідея
Варіант 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
повертає 3D-масив, середні 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 Весь скрипт сравнение.py .
Оновлення: відповідні методи
Оскільки питання стосується відео послідовності, де кадри, ймовірно, будуть майже однаковими, і ви шукаєте щось незвичне, я хотів би зазначити кілька альтернативних підходів, які можуть бути актуальними:
- віднімання та сегментація фону (для виявлення об'єктів переднього плану)
- розріджений оптичний потік (для виявлення руху)
- порівняння гістограм або іншої статистики замість зображень
Я настійно рекомендую переглянути книгу «Навчання OpenCV», глави 9 (частини зображення та сегментація) та 10 (відстеження та рух). Перший вчить використовувати метод віднімання фону, другий дає деяку інформацію про методи оптичного потоку. Усі методи реалізовані в бібліотеці OpenCV. Якщо ви використовуєте Python, я пропоную використовувати OpenCV ≥ 2.3 та його cv2
модуль Python.
Найпростіша версія віднімання фону:
- дізнатися середнє значення μ та стандартне відхилення σ для кожного пікселя фону
- порівняти значення поточних пікселів до діапазону (μ-2σ, μ + 2σ) або (μ-σ, μ + σ)
Більш вдосконалені версії враховують часовий ряд для кожного пікселя та обробляють нестатичні сцени (наприклад, переміщення дерев чи траву).
Ідея оптичного потоку полягає у прийнятті двох або більше кадрів і призначенні вектора швидкості кожному пікселю (щільний оптичний потік) або деякому з них (розріджений оптичний потік). Для оцінки розрідженого оптичного потоку можна скористатися методом Лукаса-Канаде (він також реалізований у OpenCV). Очевидно, якщо велика кількість потоку (висока середня величина над максимальними значеннями поля швидкості), то в кадрі щось рухається, і наступні зображення є більш різними.
Порівняння гістограм може допомогти виявити раптові зміни між послідовними кадрами. Цей підхід був використаний у Courbon et al, 2010 :
Подібність послідовних кадрів. Вимірюється відстань між двома послідовними кадрами. Якщо вона занадто висока, це означає, що другий кадр пошкоджений і таким чином зображення усувається. Відстань Куллбека - Лейблера або взаємна ентропія на гістограмах двох кадрів:
де p і q використовуються гістограми кадрів. Поріг фіксується на 0,2.