Как я могу оценить разницу между двумя изображениями?
вот что я хотел бы сделать:
Я делаю снимки с веб-камеры через регулярные промежутки времени. Что-то вроде временной задержки. Однако, если ничего действительно не изменилось, то есть картина в значительной степени выглядит то же самое, я не хочу хранить последний снимок.
Я полагаю, что есть какой-то способ количественной оценки разницы, и мне придется эмпирически определить порог.
Я ищу простоту, а не совершенствование. Я использую python.
20 ответов:
общая идея
Вариант 1: Загрузите оба изображения в виде массивов (
scipy.misc.imread
) и вычислить по элементам (пиксель за пикселем) разницу. Рассчитайте норму разности.Вариант 2: Загрузите оба изображения. Вычислите некоторый вектор объектов для каждого из них (например, гистограмму). Вычислите расстояние между векторами объектов, а не между изображениями.
однако, есть некоторые решения, чтобы сделать в первую очередь.
вопросы
вы должны ответить эти вопросы сначала:
изображения одинаковой формы и размера?
если нет, вам может понадобиться изменить размер или обрезать их. Библиотека PIL поможет сделать это в Python.
если они взяты с теми же настройками и тем же устройством, они, вероятно, одинаковы.
образы совпадают?
если нет, вы можете сначала запустить перекрестную корреляцию, чтобы найти лучшее выравнивание. Составляющей имеет функции, чтобы сделать это.
если камера и сцена неподвижны, изображения, вероятно, будут хорошо выровнены.
экспозиция изображений всегда одинаково? (Легкость / контраст одинаковы?)
если нет, то вы можете хотеть нормализовать картинки.
но будьте осторожны, в некоторых ситуациях это может принести больше плохого, чем хорошего. Например, один яркий пиксель на темном фоне будет очень нормализованного изображения отличающийся.
важна ли информация о цвете?
если вы хотите заметить изменения цвета, у вас будет вектор значений цвета на точку, а не скалярное значение, как в изображении серой шкалы. Вам нужно больше внимания при написании такого кода.
есть ли четкие края в изображении? Они, вероятно, переедут?
если да, то вы можете сначала применить алгоритм обнаружения края (например, вычислить градиент с помощью Sobel или Пруитт преобразовывать, применять определенный порог), а затем сравнить кромки на первое изображение к краям на второй.
есть ли шум на фотографии?
все датчики загрязняют изображение с некоторым количеством шума. Недорогие датчики имеют больше шума. Возможно, вы захотите применить некоторое шумоподавление перед сравнением изображений. Размытие является самым простым (но не лучшим) подходом здесь.
какие изменения вы хотите заметила?
это может повлиять на выбор нормы для использования для разницы между изображениями.
рассмотрите возможность использования Манхэттенской нормы (сумма абсолютных значений) или нулевой нормы (количество элементов, не равное нулю) для измерения того, насколько изменилось изображение. Первый скажет вам, сколько изображение выключено, последний скажет только, сколько пикселей отличается.
пример
я предполагаю, что ваши изображения хорошо выровнены, тот же размер и форма, возможно, с разной экспозицией. Для простоты я конвертирую их в оттенки серого, даже если они являются цветными (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
P.S. весь compare.py скрипт.
обновление: актуальные техники
поскольку речь идет о видеоряде, где кадры, скорее всего, будут почти одинаковыми, и вы ищете что-то необычное, я хотел бы упомянуть некоторые альтернативные подходы, которые могут быть актуальны:
- фон вычитание и сегментация (для обнаружения объектов переднего плана)
- разреженный оптический поток (для обнаружения движения)
- сравнение гистограмм или некоторых других статистических данных вместо изображений
я настоятельно рекомендую взглянуть на книгу "Learning OpenCV", главы 9 (части изображения и сегментация) и 10 (отслеживание и движение). Первый учит использовать метод вычитания фона, последний дает некоторую информацию о методах оптического потока. Все методы реализованы в библиотеке OpenCV. Если вы используете Python, я предлагаю использовать OpenCV ≥ 2.3, а его
cv2
модуль Python.самый простой вариант вычитания фона:
- узнать среднее значение μ и стандартным отклонением σ для каждого пикселя фона
- сравнение текущих значений пикселей в интервале (μ-2σ,μ+2σ) или (μ-σ,μ+σ)
более продвинутые версии делают учет временных рядов для каждого пикселя и ручки нестатические сцены (например, движущиеся деревья или трава).
идея оптического потока состоит в том, чтобы взять два или более кадров и назначить вектор скорости каждому пикселю (плотный оптический поток) или некоторым из них (разреженный оптический поток). Для оценки разреженного оптического потока можно использовать Лукас-канадский метод (он также реализован в OpenCV). Очевидно, что если есть много потока (высокое среднее значение по максимальным значениям поля скоростей), то что-то движется в кадре и последующее изображения более разные.
сравнение гистограмм может помочь обнаружить внезапные изменения между последовательными кадрами. Этот подход был использован в Courbon et al, 2010:
сходство последовательных кадров. измеряется расстояние между двумя последовательными кадрами. Если он слишком высок, это означает, что второй кадр поврежден и, таким образом, изображение удаляется. Элемент Kullback-Leibler расстояние, или взаимной энтропия, на гистограммах двух кадров:
здесь p и q используются гистограммы кадров. Порог зафиксирован на 0,2.
простое решение:
кодировать изображение как jpeg и искать существенные изменения в размер.
я реализовал нечто подобное с миниатюрами видео, и имел большой успех и масштабируемость.
Вы можете сравнить два изображения с помощью функции пил.
import Image import ImageChops im1 = Image.open("splash.png") im2 = Image.open("splash2.png") diff = ImageChops.difference(im2, im1)
объект diff-это изображение, в котором каждый пиксель является результатом вычитания значений цвета этого пикселя во втором изображении из первого изображения. Используя изображение diff, вы можете сделать несколько вещей. Самый простой из них-это
два популярных и относительно простых метода: (а) уже предложенное евклидово расстояние или (б) нормированная взаимная корреляция. Нормализованная взаимная корреляция имеет тенденцию быть заметно более устойчивой к изменениям освещения, чем простая взаимная корреляция. Википедия дает формулу для нормализованная взаимная корреляция. Существуют и более сложные методы, но они требуют гораздо больше работы.
используя 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
являются 2D-массивы изображений в оттенках серого.
тривиальная вещь, чтобы попробовать:
пересчитайте оба изображения в небольшие эскизы (например, 64 x 64) и сравните эскизы пиксель за пикселем с определенным порогом. Если исходные изображения почти одинаковы, пересчитанные миниатюры будут очень похожи или даже точно такие же. Этот метод заботится о шуме, который может возникнуть особенно в условиях низкой освещенности сцены. Может быть даже лучше, если вы перейдете в оттенки серого.
Я обращаюсь конкретно к вопросу о том, как вычислить, если они "достаточно разные". Я предполагаю, что вы можете выяснить, как вычесть пиксели один за другим.
во-первых, я бы взял кучу картинок с ничего изменение и узнайте максимальную величину, которую любой пиксель изменяет только из-за изменений в захвате, шума в системе обработки изображений, артефактов сжатия JPEG и мгновенных изменений освещения. Возможно, вы найдете, что 1 или 2 бит различия следует ожидать даже тогда, когда ничто не движется.
тогда для" реального " теста вам нужен такой критерий:
- то же самое, если до p пикселей отличаются не более чем на E.
Так что, возможно, если E = 0.02, P = 1000, это будет означать (приблизительно), что он будет "другим", если какой-либо один пиксель изменится более чем на ~5 единиц (предполагая 8-битные изображения), или если более 1000 пикселей имели какие-либо ошибки вообще.
это предназначено главным образом как хороший метод "сортировки", чтобы быстро идентифицировать изображения, которые достаточно близки, чтобы не нуждаться в дальнейшем рассмотрении. Изображения, которые "терпят неудачу", могут тогда больше к более сложной/дорогой технике, которая не имела бы ложных срабатываний, если бы камера трясла бит, например, или была более устойчивой к изменениям освещения.
Я запускаю проект с открытым исходным кодом, OpenImageIO, который содержит утилиту под названием "idiff", которая сравнивает различия с порогами, подобными этому (еще более сложный, фактически.) Даже если вы не хотите использовать это программное обеспечение, вы можете посмотреть на источник, чтобы увидеть, как мы это сделали. Он используется в коммерческих целях совсем немного, и этот метод порогового значения был разработан таким образом, чтобы мы могли иметь набор тестов для рендеринга и обработки изображений с "эталонными изображениями", которые могут иметь небольшие различия от платформы к платформе или Поскольку мы сделали незначительные настройки для алгоритмов tha, поэтому мы хотели операцию "соответствие в пределах допуска".
большинство ответов, данных не будет иметь дело с уровнями освещения.
Я бы сначала нормализовал изображение до стандартного уровня освещенности, прежде чем делать сравнение.
видели алгоритм поиска похожих изображений вопрос? Проверьте это, чтобы увидеть предложения.
Я бы предложил вейвлет-преобразование ваших кадров (я написал расширение C для этого, используя преобразование Хаара); затем, сравнивая индексы самых больших (пропорционально) вейвлет-факторов между двумя изображениями, вы должны получить численное приближение подобия.
У меня была аналогичная проблема на работе, я переписывал нашу конечную точку преобразования изображения, и я хотел проверить, что новая версия производит тот же или почти тот же результат, что и старая версия. Поэтому я написал следующее:
https://github.com/nicolashahn/diffimg
который работает на изображениях одинакового размера и на уровне пикселей, измеряет разницу в значениях на каждом канале: R, G, B (, A), принимает среднюю разницу этих каналов, а затем усредняет разницу по всем пикселям и возвращает отношение.
например, с изображением 10x10 белых пикселей и тем же изображением, но один пиксель изменился на красный, разница в этом пикселе составляет 1/3 или 0,33... (RGB 0,0,0 vs 255,0,0) и у всех остальных пикселей равен 0. С 100 пикселами итого, 0,33.../100 = разница в изображении ~0.33%.
Я считаю, что это будет отлично работать для проекта OP (я понимаю, что это очень старый пост сейчас, но размещение для будущих StackOverflowers кто также хочет сравнить изображения в python).
Я извиняюсь, если это слишком поздно, чтобы ответить, но так как я делал что-то подобное, я думал, что могу как-то помочь.
возможно, с OpenCV вы могли бы использовать сопоставление шаблонов. Предполагая, что вы используете веб-камеру, как вы сказали:
- упростить изображения (порог может быть?)
- применить шаблон соответствия и проверить 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
надеюсь, что это помогает.
расстояние землеройных машин может быть именно то, что вам нужно. Это может быть abit тяжело реализовать в режиме реального времени, хотя.
насчет расчета Расстояние До Манхэттена из двух изображений. Это дает вам n * n значений. Затем вы можете сделать что-то вроде среднего значения строки, чтобы уменьшить до n значений и функции над этим, чтобы получить одно единственное значение.
Мне очень повезло с jpg-изображениями, сделанными с той же камерой на штативе (1) значительно упрощение (например, от 3000 пикселей в ширину до 100 пикселей в ширину или даже меньше) (2) сглаживание каждого массива jpg в один вектор (3) попарная корреляция последовательных изображений с помощью простого алгоритма корреляции для получения коэффициента корреляции (4) возведение в квадрат коэффициента корреляции для получения R-квадрата (т. е. доли изменчивости в одном изображении, объясненной вариацией в следующем) (5) в целом в моем приложении, если r-квадрат
Это надежный и быстрый в моей реализации (Mathematica 7)
стоит поиграть с той частью изображения, которая вас интересует, и сосредоточиться на этом, обрезав все изображения в эту небольшую область, иначе будет пропущено удаленное от камеры, но важное изменение.
Я не знаю, как использовать Python, но уверен, что он делает корреляций тоже нет?
вы можете вычислить гистограмму обоих изображений, а затем вычислить Коэффициент Бхаттачариа, это очень быстрый алгоритм, и я использовал его для обнаружения изменений выстрела в крикетном видео (в C с помощью openCV)
проверьте, как вейвлеты Хаара реализуются isk-daemon. Вы можете использовать его imgdb C++ код для расчета разницы между изображениями на лету:
isk-daemon-это сервер баз данных с открытым исходным кодом, способный добавлять поиск изображений на основе контента (визуальный) на любой веб-сайт или программное обеспечение, связанное с изображениями.
эта технология позволяет пользователям любого веб-сайта или программного обеспечения, связанного с изображениями, рисовать на виджете, какое изображение они хотят найти и иметь сайт отвечает на них самыми похожими изображениями или просто запрашивает больше похожих фотографий на каждой странице деталей изображения.
У меня была та же проблема, и я написал простой модуль python, который сравнивает два изображения одинакового размера с помощью ImageChops подушки для создания черно-белого изображения diff и суммирует значения гистограммы.
вы можете получить либо этот балл напрямую, либо процентное значение по сравнению с полной черной и белой разницей.
Он также содержит простую функцию is_equal, с возможностью подачи нечеткого порога под (и в том числе) изображение проходит как равное.
в подход не очень сложный, но, возможно, имеет смысл для других там борется с той же проблемой.
несколько более принципиальным подходом является использование глобального дескриптора для сравнения изображений, таких как GIST или CENTRIST. Хэш-функция, как описано здесь, также обеспечивает аналогичное решение.
еще один хороший, простой способ измерить сходство между двумя изображениями:
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)
если другие заинтересованы в более мощном способе сравнения сходства изображений, я собрал учебник и web app для измерения и визуализации подобных изображений с помощью Tensorflow.
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()