Пишем программу для автоматического распознавания объектов с веб-камер
Разбираемся, как с помощью Python и OpenCV захватывать видео с нескольких веб-камер, передавать материалы на сервер и распознавать объекты.
В OpenCV существует множество вариантов для трансляции видеопотока. Можно использовать один из них – IP-камеры, но с ними бывает довольно трудно работать. Так, некоторые IP-камеры не позволяют получить доступ к RTSP-потоку (англ. Real Time Streaming Protocol). Другие IP-камеры не работают с функцией OpenCV cv2.VideoCapture
. В конце концов, такой вариант может быть слишком дорогостоящим для ваших задач, особенно, если вы хотите построить сеть из нескольких камер.
Как отправлять видеопоток со стандартной веб-камеры с помощью OpenCV? Одним из удобных способов является использование протоколов передачи сообщений и соответствующих библиотек ZMQ и ImageZMQ.
Поэтому сначала мы кратко обсудим транспорт видеопотока вместе с ZMQ, библиотекой для асинхронной передачи сообщений в распределенных системах. Далее, мы реализуем два скрипта на Python:
- Клиент, который будет захватывать кадры с простой веб-камеры.
- Сервер, принимающий кадры и ищущий на них выбранные типы объектов (например, людей, собак и автомобили).
Для демонстрации работы узлов применяются четыре платы Raspberry Pi с подключенными модулями камер. На их примере мы покажем, как использовать дешевое оборудование в создании распределенной сети из камер, способных отправлять кадры на более мощную машину для дополнительной обработки.
Передача сообщений и ZMQ
Передача сообщений – парадигма программирования, традиционно используемая в многопроцессорных распределенных системах. Концепция предполагает, что один процесс может взаимодействовать с другими процессами через посредника – брокера сообщений (англ. message broker). Посредник получает запрос, а затем обрабатывает акт пересылки сообщения другому процессу/процессам. При необходимости брокер сообщений также отправляет ответ исходному процессу.
ZMQ является высокопроизводительной библиотекой для асинхронной передачи сообщений, используемой в распределенных системах. Этот пакет обеспечивает высокую пропускную способность и малую задержку. На основе ZMQ Джефом Бассом создана библиотека ImageZMQ, которую сам Джеф использует для компьютерного зрения на своей ферме вместе с теми же платами Raspberry Pi.
Начнем с того, что настроим клиенты и сервер.
Конфигурирование системы и установка необходимых пакетов
Сначала установим opencv и ZMQ. Чтобы избежать конфликтов, развертывание проведем в виртуальной среде:
Теперь нам нужно клонировать репозиторий с ImageZMQ:
Далее, можно скопировать директорию с исходником или связать ее с вашим виртуальным окружением. Рассмотрим второй вариант:
Библиотеку ImageZMQ нужно установить и на сервер, и на каждый клиент.
Примечание: чтобы быть увереннее в правильности введенного пути, используйте дополнение через табуляцию.
Подготовка клиентов для ImageZMQ
В этом разделе мы осветим важное отличие в настройке клиентов.
Наш код будет использовать имя хоста клиента для его идентификации. Для этого достаточно и IP-адреса, но настройка имени хоста позволяет проще считать назначение клиента.
В нашем примере для определенности мы предполагаем, что вы используете Raspberry Pi с операционной системой Raspbian. Естественно, что клиент может быть построен и на другой ОС.
Чтобы сменить имя хоста, запустите терминал (это можно сделать через SSH-соединение) и введите команду raspi-config
:
Вы увидите следующее окно терминала. Перейдите к пункту 2 Network Options.
На следующем шаге выберите опцию N1 Hostname.
На этом этапе задайте осмысленное имя хоста (например, pi-livingroom, pi-bedroom, pi-garage). Так вам будет легче ориентироваться в клиентах сети и сопоставлять имена и IP-адреса.
Далее, необходимо согласиться с изменениями и перезагрузить систему.
В некоторых сетях вы можете подключиться через SSH, не предоставляя IP-адрес явным образом:
Определение отношений сервер-клиент
Прежде чем реализовать стриминг потокового видео по сети, определим отношения клиентов и сервера. Для начала уточним терминологию:
- Клиент – устройство, отвечающее за захват кадров с веб-камеры с использованием OpenCV, а затем за отправку кадров на сервер.
- Сервер — компьютер, принимающий кадры от всех клиентов.
Конечно, и сервер, и клиент могут и принимать, и отдавать какие-то данные (не только видеопоток), но для нас важно следующее:
- Существует как минимум одна (а скорее всего, несколько) система, отвечающая за захват кадров (клиент).
- Существует только одна система, используемая для получения и обработки этих кадров (сервер).
Структура проекта
Структура проекта будет состоять из следующих файлов:
Два первых файла из списка соответствуют файлам предобученной нейросети Caffe MobileNet SSD для распознавания объектов. В репозитории по ссылке можно найти соответствующие файлы, чьи названия, правда, могут отличаться от приведенных (*.caffemodel
и deploy.prototxt
). Сервер (server.py
) использует эти файлы Caffe в DNN-модуле OpenCV.
Скрипт client.py
будет находиться на каждом устройстве, которое отправляет поток на сервер.
Реализация клиентского стримера на OpenCV
Начнем с реализации клиента. Что он будет делать:
- Захватывать видеопоток с камеры (USB или RPi-модуль).
- Отправлять кадры по сети через ImageZMQ.
Откроем файл client.py
и вставим следующий код:
Назначение импортируемых модулей описано в комментариях. В последних строчках создается объект-отправитель, которому передаются IP-адрес и порт сервера. Указанный порт 5555
обычно не вызывает конфликтов.
Инициализируем видеопоток и начнем отправлять кадры на сервер.
Теперь у нас есть объект VideoStream
, созданный для захвата фреймов с RPi-камеры. Если вы используете USB-камеру, раскомментируйте следующую строку и закомментируйте ту, что активна сейчас.
В этом месте вы также можете установить разрешение камеры. Мы будем использовать максимальное, так что аргумент не передастся. Если вы обнаружите задержку, надо уменьшить разрешение, выбрав одно из доступных значений, представленных в таблице. Например:
Для USB-камеры такой аргумент не предусмотрен. В следующей строке после считывания кадра можно изменить его размер:
В последних строках скрипта происходит захват и отправка кадров на сервер.
Реализация сервера
На стороне сервера необходимо обеспечить:
- Прием кадров от клиентов.
- Детектирование объектов на каждом из входящих кадров.
- Подсчет количества объектов для каждого из кадров.
- Отображение смонтированного кадра (панели), содержащего изображения от всех активных устройств.
Последовательно заполним файл с описанием сервера server.py
:
Библиотека imutils
упрощает работу с изображениями (есть на GitHub и PyPi).
Пять аргументов, обрабатываемых с помощью парсера argparse
:
--prototxt
: путь к файлу прототипа глубокого изучения Caffe.--model
: путь к предообученной модели нейросети Caffe.--confidence
: порог достоверности для фильтрации случаев нечеткого обнаружения.--montageW
: количество столбцов для монтажа общего кадра, состоящего в нашем примере из 2х2 картинок (то есть montageW = 2) . Часть ячеек может быть пустой.--montageH
: аналогично предыдущему пункту — количество строк в общем кадре.
Вначале инициализируем объект ImageHub
для работы с детектором объектов. Последний построен на базе MobileNet Single Shot Detector.
Объект ImageHub
используется сервером для приема подключений от каждой платы Raspberry Pi. По существу, для получения кадров по сети и отправки назад подтверждений здесь используются сокеты и ZMQ .
Предположим, что в системе безопасности мы отслеживаем только три класса подвижных объектов: собаки, люди и автомобили. Эти метки мы запишем в множество CONSIDER
, чтобы отфильтровать прочие неинтересные нам классы (стулья, растения и т. д.).
Кроме того, необходимо следить за активностью клиентов, проверяя время отправки тем или иным клиентом последнего кадра.
Далее необходимо зациклить потоки, поступающие от клиентов и обработку данных на сервере.
Итак, сервер забирает изображение в imageHub
, высылает клиенту сообщение о подтверждении получения. Принятое сервером сообщениеimageHub.recv_image
содержит имя хоста rpiName
и кадр frame
. Остальные строки кода нужны для учета активности клиентов.
Затем мы работаем с кадром, формируя блоб (о функции blobFromImage
читайте подробнее в посте pyimagesearch). Блоб передается нейросети для детектирования объектов.
Замечание: мы продолжаем рассматривать цикл, поэтому здесь и далее будьте внимательны с отступами в коде.
Теперь мы хотим пройтись по детектированным объектам, чтобы посчитать и выделить их цветными рамками:
Далее, аннотируем каждый кадр именем хоста и количеством объектов. Наконец, монтируем из нескольких кадров общую панель:
Остался заключительный блок для проверки последних активностей всех клиентов. Эти операции особенно важны в системах безопасности, чтобы при отключении клиента вы не наблюдали неизменный последний кадр.
Запускаем стриминг видео с камер
Теперь, когда мы реализовали и клиент, и сервер, проверим их. Загрузим клиент на каждую плату Raspberry Pi с помощью SCP-протокола:
Проверьте, что на всех машинах установлены импортируемые клиентом или сервером библиотеки. Первым нужно запускать сервер. Сделать это можно следующей командой:
Далее запускаем клиенты, следуя инструкции (будьте внимательны, в вашей системе имена и адреса могут отличаться):
- Откройте SSH-соединение с клиентом:
ssh pi@192.168.1.10
- Запустите экран клиента:
screen
- Перейдите к профилю:
source ~/.profile
- Активируйте окружение:
workon py3cv4
- Установите ImageZMQ, следуя инструкциям библиотеки по установке
- Запустите клиент:
python client.py --server-ip 192.168.1.5
Ниже представлено демо-видео панели с процессом стриминга и распознавания объектов с четырех камер на Raspberry Pi.
Аналогичные решения из кластера камер и сервера можно использовать и для других задач, например:
- Распознавание лиц. Такую систему можно использовать в школах для обеспечения безопасности и автоматической оценки посещаемости.
- Робототехника. Объединяя несколько камер и компьютерное зрение, вы можете создать прототип системы автопилота.
- Научные исследования. Кластер из множества камер позволяет проводить исследования миграции птиц и животных. При этом можно автоматически обрабатывать фотографии и видео только в случае детектирования конкретного вида, а не просматривать видеопоток целиком.