Пишем программу для автоматического распознавания объектов с веб-камер

Пишем программу для автоматического распознавания объектов с веб-камер

В OpenCV существует множество вариантов для трансляции видеопотока. Можно использовать один из них – IP-камеры, но с ними бывает довольно трудно работать. Так, некоторые IP-камеры не позволяют получить доступ к RTSP-потоку (англ. Real Time Streaming Protocol). Другие IP-камеры не работают с функцией OpenCV cv2.VideoCapture . В конце концов, такой вариант может быть слишком дорогостоящим для ваших задач, особенно, если вы хотите построить сеть из нескольких камер.

Как отправлять видеопоток со стандартной веб-камеры с помощью OpenCV? Одним из удобных способов является использование протоколов передачи сообщений и соответствующих библиотек ZMQ и ImageZMQ.

Поэтому сначала мы кратко обсудим транспорт видеопотока вместе с ZMQ, библиотекой для асинхронной передачи сообщений в распределенных системах. Далее, мы реализуем два скрипта на Python:

  1. Клиент, который будет захватывать кадры с простой веб-камеры.
  2. Сервер, принимающий кадры и ищущий на них выбранные типы объектов (например, людей, собак и автомобили).

Для демонстрации работы узлов применяются четыре платы 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

Начнем с реализации клиента. Что он будет делать:

  1. Захватывать видеопоток с камеры (USB или RPi-модуль).
  2. Отправлять кадры по сети через 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-протокола:

Проверьте, что на всех машинах установлены импортируемые клиентом или сервером библиотеки. Первым нужно запускать сервер. Сделать это можно следующей командой:

Далее запускаем клиенты, следуя инструкции (будьте внимательны, в вашей системе имена и адреса могут отличаться):

  1. Откройте SSH-соединение с клиентом: ssh [email protected]
  2. Запустите экран клиента: screen
  3. Перейдите к профилю: source

Ниже представлено демо-видео панели с процессом стриминга и распознавания объектов с четырех камер на Raspberry Pi.

Аналогичные решения из кластера камер и сервера можно использовать и для других задач, например: