Для своих приложений компьютерного зрения у меня нет значительных вычислительных мощностей. Поэтому приходится довольствоваться малым и использовать простые, но эффективные методы.
Здесь мы рассмотрим один из таких методов для выделения движущихся объектов на фоне видео-сцены статически установленной камеры.
Этот сценарий не редкость. Например, подобные используют камеры видеонаблюдения ГИБДД.
Временная Медианная Фильтрация
Чтобы понять суть идеи, которую мы собираемся описать, рассмотрим более простую задачу в одномерном пространстве 1D.
Проведем эксперимент — каждые 10 миллисекунд мы что-то измеряем (скажем, температуру в комнате).
Допустим, температура в помещении составляет 70°F по Фаренгейту.
Хороший термометр, слева, показывает 70°F с некоторым гауссовским шумом. Чтобы получить более точную оценку температуры, мы можем просто усреднить значения в течение нескольких секунд. Поскольку шум является гауссовым с положительными и отрицательными значениями, то при вычислении среднего произойдет взаимная компенсация. Действительно, среднее значение в данном конкретном случае составляет 70,01°F.
С другой стороны, плохой термометр большую часть времени ведет себя как хороший, но иногда цифры совершенно неверны.
На самом деле, если мы вычислим среднее показаний плохого термометра, то получим 71,07°F. Это явно завышенная оценка.
А мы сможем получить хорошую оценку температуры по этим показаниям?
Мой ответ — ДА. Когда данные содержат выбросы, медиана является более надежной оценкой значения, которое нам надо.
Медиана (статистика)એ — это значение в середине ряда отсортированных в порядке возрастания или убывания данных.
Медиана наших показаний составляет 70,05°F, что гораздо лучше, чем 71,07°F.
Единственным недостатком является то, что вычисление медианы является более медленным алгоритмом в сравнении с вычислением средне-взвешенного значения.
Использование медианы для оценки фона
Теперь давайте вернемся к проблеме оценки фона, когда камера установлена статично и не меняет своего положения.
Мы можем предположить, что большую часть времени каждый пиксель принадлежит одному и тому же фрагменту фона в силу неизменности положения камеры. Таким образом, автомобиль или другой движущийся объект заходит на камеру спереди и затемняет фон.
Для видеоряда мы можем произвольно выбрать несколько кадров (скажем, 25 кадров).
Другими словами, для каждого пикселя мы теперь имеем 25 оценок его принадлежности к фону. До тех пор, пока пиксель не будет накрыт автомобилем или другим движущимся объектом более чем в 50% случаев, медиана пикселя над этими 25 кадрами даст хорошую оценку принадлежности фону в этом фрагменте.
Мы можем повторить это для каждого пикселя и, таким образом, восстановить весь фон.
import numpy as np import cv2 from skimage import data, filters # Open Video cap = cv2.VideoCapture('video.mp4') # Randomly select 25 frames frameIds = cap.get(cv2.CAP_PROP_FRAME_COUNT) * np.random.uniform(size=25) # Store selected frames in an array frames = [] for fid in frameIds: cap.set(cv2.CAP_PROP_POS_FRAMES, fid) ret, frame = cap.read() frames.append(frame) # Calculate the median along the time axis medianFrame = np.median(frames, axis=0).astype(dtype=np.uint8) # Display median frame cv2.imshow('frame', medianFrame) cv2.waitKey(0)
Как вы можете видеть, мы случайным образом выбираем 25 кадров и вычисляем медиану каждого пикселя над 25 кадрами. Этот медианный кадр является хорошей оценкой фона, если по крайней мере в 50% случаев пиксель принадлежащий фону не перекрыт.
Результат показан ниже
Разделение кадров
Следующий очевидный вопрос заключается в том, можем ли мы создать маску для каждого кадра, которая выделит движущиеся части изображения.
Это достигается за несколько шагов.
- Преобразуем кадр в оттенки серого.
- По циклу всех кадров в видео. Извлекаем текущий кадр и преобразуйте его в оттенки серого.
- Вычисляем абсолютную разницу между текущим кадром и медианным кадром.
- Вычисляем пороговое значение приведенного выше изображение для удаления шума и приводим выходной сигнала к бинарному виду.
Посмотрим код.
# Reset frame number to 0 cap.set(cv2.CAP_PROP_POS_FRAMES, 0) # Convert background to grayscale grayMedianFrame = cv2.cvtColor(medianFrame, cv2.COLOR_BGR2GRAY) # Loop over all frames ret = True while(ret): # Read frame ret, frame = cap.read() # Convert current frame to grayscale frame = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY) # Calculate absolute difference of current frame and # the median frame dframe = cv2.absdiff(frame, grayMedianFrame) # Treshold to binarize th, dframe = cv2.threshold(dframe, 30, 255, cv2.THRESH_BINARY) # Display image cv2.imshow('frame', dframe) cv2.waitKey(20) # Release video object cap.release() # Destroy all windows cv2.destroyAllWindows()
Результаты
На видео ниже показан вывод оценки фона и дифференцирования кадров.
Источник вдохновения:
Фильтрация фона видео с OpenCV, опубликовано К ВВ, лицензия — Creative Commons Attribution-NonCommercial 4.0 International.
Респект и уважуха