Технологии и ограничения:
- Языки программирования: Java, Python, C++;
- Дополнительные библиотеки: OpenCV;
Постановка задачи:
-
Реализовать программный модуль опроса видеокамеры. Для теста можно использовать видеокамеру Axis 214 192.168.217.103. При этом допускается использование библиотеки Opencv.
-
Разработать два варианта детектора движения для выделения объектов переднего плана:
- детектор движения с использование функционала библиотеки opencv;
- детектор движения без использования сторонних библиотек;
- С помощью оптического потока разработать модуль сопровождения (трекинга) объекта, выделенного при реализации п.2. При этом отобразить сглаженную траекторию движения объекта.
Проект реализован на языке Python
с использованием следующих библиотек:
numpy
(Ссылка на страницу с инструкцией по установке и документацией): хранение массивов и некоторые модули для работы с несколькими массивами (операция сверткиnumpy.convolve2d()
, поиск среднего значенияnumpy.mean()
)
Установить библиотеку можно с использованием pip3 следующей командой:pip3 install numpy
opencv-python
(Ссылка на страницу с инструкцией по установке): считывание видео и кадров, использование функционала для детекции движения, трекинга выделенного объекта алгоритмом оптического потока.
Установить библиотеку можно с использованием pip3 следующей командой:pip3 install opencv-python
Перед запуском необходимо скачать проект. Это можно сделать с помощью команды https://github.com/AlexanderKamynin/motion-detection.git
.
Запуск можно выполнить из корневой папки проекта с использованием команды python3 main.py
.
Для детекции движения с использованием функционала библиотеки OpenCV реализован модуль detectionCV.py
с классом MotionDetectionCV
. В классе определен метод detect()
, принимающий на вход два соседних по времени черно-белых изображения, а на выход выдающий словарь, в котором ключ - id выделенного объекта, значение - список с двумя кортежами, характеризующими левый верхний и правый нижний углы прямоугольника, описанного вокруг объекта.
В самом начале вычисляется разница между двумя кадрами с помощью cv2.absdiff()
, после чего к данной разности применяется фильтр Гаусса cv2.GaussianBlur()
, размывающий изображение, что позволяет уменьшить шум.
Размытое изображение передается в метод cv2.threshold()
, который все пиксели, значение которых в черно-белом изображении меньше заданного значение порога threshold
, устанавливает в значение 0 (черный цвет), иначе в 255 (белый цвет). К полученному результату применяется операция дилатации cv2.dilate()
: расширение белых пикселей на изображении, благодаря чему контуры становятся более явными.
Обработанное по итогу работы дилатации черно-белое изображение содержит в себе движимые объекты из белых пикселей, и фон черного цвета. Для того, чтобы убрать шум, который из-за дилатации мог увеличиться в размере, рассматриваются последние N (max_frames
в коде модуля) результатов дилатации и усредняются между собой с помощью np.mean()
. Полученное значение передается в метод cv2.findContours()
, выдающим краевые точки обнаруженного контура объекта.
Методом cv2.contourArea()
вычисляется площадь выделенных объектов. Те контуры, площадь которых меньше заданного порога min_area
, убираются из рассмотрения. Далее удаляются все контуры, которые полностью оказались внутри любых других контуров.
Выделенным объектам присваиваются id, и их контуры (описанные прямоугольники) сохраняются в словаре detected_objects
. Те id, центры которых не находятся ни в одном из выделенных на текущей итерации прямоугольниках, удаляются. Те контуры, которым не соответствует ни один выделенный на прошлых итерациях объект, добавляются как новый объект с новым id.
Для детекции движения реализован модуль detectionCustom.py
с классом MotionDetectionCustom
. В классе определен метод detect()
, принимающий на вход два соседних по времени черно-белых изображения, а на выход выдающий словарь, в котором ключ - id выделенного объекта, значение - список с двумя кортежами, характеризующими левый верхний и правый нижний углы прямоугольника, описанного вокруг объекта.
В самом начале вычисляется разница между двумя кадрами по модулю, после чего к данной разности применяется фильтр Гаусса, размывающий изображение, что позволяет уменьшить шум. Работа фильтра Гаусса заключается в последовательном перемножении ядра на окрестность пикселей изображения. Формула для вычисления ядра Гаусса размера N x N (N - нечетное) выглядит следующим образом:
convolve2d()
из библиотеки scipy.signal
.
Для размытого изображения все пиксели, значение которых меньше заданного значения порога threshold
, устанавливаются в значение 0, иначе в 255. К полученному результату применяется операция дилатации: расширение белых пикселей на изображении, благодаря чему контуры становятся более явными (проход по всему изображению и присваивание пикселю максимального значения в рамках задаваемого размера окна). Так как в задаче необходимо получить координаты объектов (описанных вокруг них прямоугольников), то для сокращения числа операций при операции дилатации изображение уменьшается в задаваемое число раз.
Обработанное по итогу работы дилатации черно-белое изображение содержит в себе движимые объекты из белых пикселей, и фон черного цвета. Для того, чтобы убрать шум, который из-за дилатации мог увеличиться в размере, рассматриваются последние N (max_frames
в коде модуля) результатов дилатации и усредняются между собой с помощью np.mean()
.
Для поиска контуров применяется BFS, расматривающий окрестность Фон Неймана для первого найденного непосещенного белого пикселя (из-за усреднения вводится дополнительный порог bounding_threshold
, с высоким значением, например, 200, значения больше которого считаются контуром) и обновляющий информацию об крайних точках контура (слева, справа, снизу и сверху). Полученные крайние точки являются описанным вокруг движимого объекта прямоугольником. Если площадь этого прямоугольника меньше значения min_area
, то данный контур не рассматривается. Так как при операции дилатации изображения могло быть уменьшено, то для для восстановления координат контура в исходном кадре они домножаются на коэффициент уменьшения.
Выделенным объектам присваиваются id, и их контуры (описанные прямоугольники) сохраняются в словаре detected_objects
. Те id, центры которых не находятся ни в одном из выделенных на текущей итерации прямоугольниках, удаляются. Те контуры, которым не соответствует ни один выделенный на прошлых итерациях объект, добавляются как новый объект с новым id.
Для трекинга движения реализован модуль tracker.py
с классом Tracker
. В классе определен метод track()
, принимающий на вход два соседних по времени черно-белых изображения, а также словарь, в котором ключ - id выделенного объекта, значение - список с двумя кортежами, характеризующими левый верхний и правый нижний углы прямоугольника, описанного вокруг объекта. Метод возвращает маску (изображение) с линиями смещения центров объектов из одного кадра в другой.
Для трекинга используется метод cv2.calcOpticalFlowPyrLK()
, в котором реализован пирамидальный алгоритм Лукаса-Канаде вычисления оптического потока (описание алгоритма). Метод возвращает новые координаты центров, обнаруженных на следующем кадре. Все центры объектов сохраняются в словаре, где ключ - id объекта, а значение - список координат центров объектов на изображении в разный момент времени. Новые линии из предыдущего центра объекта в новый отрсовываюстя на маске. Если число точек для какого-либо объекта превышает заданный порог max_point
, то удаляются самые ранние.
Результат работы с реализацией OpenCV:
cv_result.mp4
Результат работы без использования реализации OpenCV: