№ 8620 В разделе
Programming
от March 21st, 2018,
В подшивках: Computer Vision, OpenCV, Python
Встала задача отслеживания движения рядом с конкретной областью. Камера хоть и умеет сама детектить движение, но делает это очень топорно и в последствии теряя записи. Было принято решение написать программу, которая обрабатывает видео файлы и находит движение в указанных мной областях. Выбор пал на библиотеку OpenCV, позволяющую очень удобно работать с видео и графикой, она универсальная, имеет биндинг для python и работает под Linux и Windows.
В программу вложены функции установки тонких параметров: минимальная яркость передвигающегося элемента, количество найденных изменений после которых срабатывает тревога и количество пропущенных фреймов. Чем больше пропускаете фреймов, тем менее вероятно обнаружить движение ведь если объект передвигается слишком быстро он может попасть в неучитываемый отрезок времени. Тоже самое касается и минимальной яркости, чем больше, тем хуже. Также на обнаружение влияет и размер передвигающегося объекта, который напрямую влияет на количество измененных пикселов и чем выше это значение, тем крупее объект для обнаружения.
Здесь задается область обнаружения и параметры поиска. Координаты области задаются двумя точками [x1, y1, x2, y2] по которым вырезается прямоугольник. weightAlarm
задает минимальный размер объекта, а lightingAlarm
минимальную яркость в разности фреймов.
coord = [366, 790, 462, 884] weightAlarm = 250 lightingAlarm = 30 skipFrames = 6
Весь алгоритм строится на сравнении 3 изображений, хранящихся в t0, t1 и t2.
def findDifference(t0, t1, t2): d1 = cv2.absdiff(t2, t1) d2 = cv2.absdiff(t1, t0) return cv2.bitwise_and(d1, d2)
Каждый следующий фрейм перемещается по переменным в поиске различий. Цикл проходит по всему видеофайлу фрейм за фреймом, попутно пропуская обработку ненужных фреймов и при нахождении различий сохраняя полностью фрейм в отдельный файл.
while(cap.isOpened()): frameNum += 1 if frameNum >= framesCount: break else: ret, frame = cap.read() if frameSkip <= skipFrames: frameSkip += 1 continue else: frameSkip = 0 imgROI = cropRegionOfInterest(frame, coord) t0 = t1 t1 = t2 t2 = cv2.cvtColor(imgROI, cv2.COLOR_BGR2GRAY) diffImage = findDifference(t0, t1, t2) cv2.imshow('Movement', diffImage) if getWeight(diffImage, lightingAlarm) > weightAlarm: print('I found! Frame {}'.format(frameNum)) name = "out/frame%d.jpg" % frameNum cv2.imwrite(name, frame) if cv2.waitKey(1) & 0xFF == ord('q'): break
Вызов imshow
не обязателен и если его убрать это сохранит достаточно много процессорного времени. Обработка видео длительностью 2 часа 10 минут заняла чуть более 10 минут при размере области 96х94 пикселя со скважностью 6. Не знаю как вам, а я считаю результат неплохим и его можно улучшить.
Тестовое видео
Область обнаружения:
Результат:
weightAlarm = 250
lightingAlarm = 30
skipFrames = 6
UPDATE! Сегодня класс начал поддерживать несколько охранных зон.
Проход по 2 охранным зонам:
Processing: /media/Documents/tmp/detect_motion/20180320112608-20180320160500_2.mp4 FPS: 25 Frames count: 7848 Length: 00:05:14 Movement! Frame 856, time 00:00:34, zone zone 1 Movement! Frame 864, time 00:00:35, zone zone 1 Movement! Frame 872, time 00:00:35, zone zone 1 Movement! Frame 880, time 00:00:35, zone zone 1 Movement! Frame 888, time 00:00:36, zone zone 1 Movement! Frame 896, time 00:00:36, zone zone 1 Movement! Frame 904, time 00:00:36, zone zone 1 Movement! Frame 6632, time 00:04:25, zone zone 1 Movement! Frame 6640, time 00:04:26, zone zone 1 Movement! Frame 6648, time 00:04:26, zone zone 1 Movement! Frame 7840, time 00:05:14, zone zone 2 real 0m31,290s
А еще было бы круто распараллелить обработку на несколько ядер процессора, а не молотить все на одном.
Вот вам репозиторий с полным кодом https://git.blindage.org/21h/detect-motion
Fortune cookie: Astrology... just a bunch of Taurus.
Leave a Reply