Перейти к содержанию

FALPRS

Этот проект является заменой старого. Основные отличия:

  • В качестве СУБД используется PostgreSQL.
  • Проект использует userver — асинхронный фреймворк с открытым исходным кодом.
  • Строгое соблюдение типов данных в API запросах. Например, если ожидается числовое поле, то его нельзя заключать в кавычки.
  • Добавлена система распознавания автомобильных номеров (license plate recognition system — LPRS).

Содержание

LPRS

Система предназначена для распознавания автомобильных номеров и обнаружения специального транспорта с мигалками: скорая помощь, МЧС, полиция и т.п. На данный момент поддерживаются российские регистрационные знаки типа 1 (ГОСТ Р 50577-93) и 1А (ГОСТ Р 50577-2018):

Используемые модели нейронных сетей

Сервис работает с пятью нейронными сетями: VDNet, VCNet, LPDNet, LPCNet и LPRNet. VDNet предназначена для поиска транспортных средств по полученному снимку с видео камеры. VCNet определяет, является ли каждое найденное транспортное средство специальным. LPDNet предназначена для поиска автомобильных номеров. LPCNet делает классификацию найденных номеров. LPRNet предназначена для распознавания номеров из полученных LPDNet данных. Модели VDNet, LPDNet и LPRNet обучены с помощью Ultralytics. Модели VCNet и LPCNet получены путем "дообучения" (transfer learning with fine-tuning). За основу взята эта модель. Для инференса нейронных сетей используется NVIDIA Triton Inference Server.

Общая схема взаимодействия с LPRS

Для взаимодействия с сервисом используется API, подробное описание которого находится в репозитории в файле docs/openapi.yaml. Сначала нужно зарегистрировать видео потоки, с которыми предстоит работать системе. Для этого используется API метод addStream. Основные параметры:

  • streamId — внутренний для вашего бэкенда (внешний для LPRS) идентификатор видео потока;
  • config — конфигурационные параметры. Пример тела запроса:
{
  "streamId": "1234",
  "config": {
    "screenshot-url": "https://my.video.stream/capture",
    "callback-url": "https://my.host/callback?streamId=1"
  }
}

Теперь LPRS знает, что захват кадров надо делать с помощью указанного параметра screenshot-url, а при обнаружении специального транспорта или распознавании автомобильных номеров отправлять краткую информацию HTTP методом POST на callback-url. Чтобы LPRS стала обрабатывать кадры, нужно вызвать API метод startWorkflow. Пример тела запроса:

{
  "streamId": "1234"
}

Система начнёт делать цикличный процесс: получение кадра, его обработка с помощью нейронных сетей, отправление, если требуется, информации на callback-url. Пауза на некоторое время и вновь получение кадра, обработка и т.д. Для остановки процесса нужно вызвать API метод stopWorkflow. Пример тела запроса:

{
  "streamId": "1234"
}

Чтобы снизить нагрузку на систему, мы рекомендуем вызывать startWorkflow и stopWorkflow, например, в соответствии с видеоаналитикой камеры: детектор движения, пересечение линий, обнаружение вторжений и т.п. При получении краткой информации о распознанных номерах, ваш бэкенд должен её обработать. Например, cверить полученные номера со списком разрешённых и открыть ворота или просто проигнорировать. События распознавания номеров некоторое время хранятся в LPRS. Если требуется полная информация о событии, то нужно использовать API метод getEventData. Для удаления видео потока используется API метод removeStream. С помощью listStreams можно получить информацию о зарегистрированных в LPRS видео потоках.

Автоматический бан номеров

Для предотвращения спама запросов на callback-url используется двухэтапный бан номеров. Если система видит номер впервые, то после обработки он окажется на первом этапе бана: на некоторое время (парметр конфигурации ban-duration) номер игнорируется вне зависимости от его расположения в кадре. Если система снова увидит этот номер на первой стадии, то время бана продлевается. По окончании первого этапа номер попадёт в следующий. На втором этапе бана (параметр конфигурации ban-duration-area) номер игнорируется до тех пор, пока не поменяет свое местоположение в кадре. При смене местоположения (параметр конфигурации ban-iou-threshold) номер будет обработан и снова попадёт на первый этап. По окончании второго этапа номер удаляется из бана. Если вы не хотите, чтобы применялся автоматический бан номеров, то необходимо установить значение конфигурационного параметра ban-duration или ban-duration-area в нулевое значение, например 0s (0 секунд).

FRS

Система предназначена для распознавания лиц.

Используемые модели нейронных сетей

На данный момент FRS использует в работе три модели:

  • scrfd — предназначена для поиска лиц на изображении. Ссылка на проект.
  • genet — предназначена для определения наличия на лице маски или тёмных очков. За основу взят этот проект. Модель получена путем "дообучения" (transfer learning with fine-tuning) на трех классах: открытое лицо, лицо в маске, лицо в тёмных очках.
  • arcface — предназначена для вычисления биометрического шаблона лица. Ссылка на проект.

Общая схема взаимодействия с FRS

Для взаимодействия с сервисом используется API, подробное описание которого находится в репозитории в файле docs/openapi.yaml. Первым делом, ваш бэкенд с помощью вызова API метода addStream регистрирует видео потоки. Основными параметрами метода являются:

  • streamId — внутренний для бэкенда (внешний для FRS) идентификатор видео потока;
  • url — это URL для захвата кадра с видео потока. FRS не декодирует видео, а работает с отдельными кадрами (скриншотами). Например, URL может выглядеть как http://<имя хоста>/cgi-bin/images_cgi?channel=0&user=admin&pwd=<пароль>
  • callback — это URL, который FRS будет использовать, когда распознает зарегистрированное лицо. Например, http://<адрес-бэкенда>/face_recognized?stream_id=1

Для регистрации лиц используется метод registerFace. Основные параметры метода:

  • streamId — идентификатор видео потока;
  • url — это URL с изображением лица, которое нужно зарегистрировать. Например, http://<адрес-бэкенда>/image_to_register.jpg В случае успешной регистрации возвращается внутренний для FRS (внешний для бэкенда) уникальный идентификатор зарегистрированного лица — faceId.

Для старта и окончания обработки кадров используется метод motionDetection. Основная идея заключается в том, чтобы FRS обрабатывала кадры только тогда, когда зафиксировано движение перед видео камерой. Параметры метода:

  • streamId — идентификатор видео потока;
  • start — признак начала или окончания движения. Если start=true, то FRS начинает через каждую секунду (задаётся параметром delay-between-frames) обрабатывать кадр с видео потока. Если start=false, то FRS прекращает обработку.

Под обработкой кадра подразумевается следующая цепочка действий:

  1. Поиск лиц с помощью нейронной сети scrfd.
  2. Если лица найдены, то каждое проверяется на "размытость" и "фронтальность".
  3. Если лицо не размыто (чёткое изображение) и фронтально, то с помощью нейронной сети genet определяется наличие маски и тёмных очков.
  4. Для каждого лица без маски и без тёмных очков, с помощью нейронной сети arcface, вычисляется биометрический шаблон лица, он же дескриптор. Математически, дескриптор представляет собой 512-ти мерный вектор.
  5. Далее каждый такой дескриптор попарно сравнивается с зарегистрированными в системе. Сравнение делается путём вычисления косинуса угла между векторами-дескрипторами: чем ближе значение к единице, тем больше похоже лицо на зарегистрированное. Если самое максимальное значение косинуса больше порогового значения (параметр tolerance), то лицо считается распознанным и FRS вызывает callback (событие распознавания лица) и в качестве параметров указывает идентификатор дескриптора (faceId) и внутренний для FRS (внешний для бэкенда) идентификатор события (eventId). Если в одном кадре окажется несколько распознанных лиц, то callback будет вызван для самого "качественного" ("лучшего") лица (параметр blur).
  6. Каждое найденное неразмытое, фронтальное, без маски и без тёмных очков, лучшее лицо временно хранится в журнале FRS.

С помощью метода bestQuality у FRS можно запрашивать "лучший" кадр из журнала. Например, незнакомый системе человек подошёл к домофону и открыл дверь ключом. Бэкенд знает время открытия ключом (date) и запрашивает лучший кадр у FRS. FRS ищет у себя в журнале кадр с максимальным blur из диапазона времени [date - best-quality-interval-before; date + best-quality-interval-after] и выдаёт его в качестве результата. Такой кадр — хороший кандидат для регистрации лица с помощью метода registerFace. Как правило, для хорошего распознавания необходимо зарегистрировать несколько лиц одного человека, в том числе с кадров, сделанных в тёмное время суток, когда камера переходит в инфракрасный режим работы.

Сборка и настройка проекта

Системные требования

  • CPU с AVX-инструкциями.
  • NVIDIA GPU с параметром Compute Capability большим или равным 6.0 и памятью 4 Гб или больше. Подробности можно посмотреть, например, здесь.
  • СУБД PostgreSQL 14 или выше.

Для получения исходного кода нужен git. Если не установлен, то выполнить команду:

sudo apt-get install -y git

Получение исходного кода проекта:

cd ~
git clone --recurse-submodules https://github.com/rosteleset/falprs.git

Установка драйверов NVIDIA

Если в системе уже установлены свежие драйвера NVIDIA, то пропустите этот пункт. Для установки можно использовать скрипт scripts/setup_nvidia_drivers.sh. Основные команды взяты отсюда.

sudo ~/falprs/scripts/setup_nvidia_drivers.sh

После установки необходимо перезагрузить операционную систему:

sudo reboot

Установка Docker Engine

Если в системе уже установлен Docker Engine, то пропустите этот пункт. Для установки можно использовать скрипт scripts/setup_docker.sh. Основные команды взяты отсюда.

sudo ~/falprs/scripts/setup_docker.sh

Установка NVIDIA Container Toolkit

Если в системе уже установлен NVIDIA Container Toolkit, то пропустите этот пункт. Для установки можно использовать скрипт scripts/setup_nvidia_container_toolkit.sh. Основные команды взяты отсюда.

sudo ~/falprs/scripts/setup_nvidia_container_toolkit.sh

После установки предлагается перезапустить docker:

sudo systemctl restart docker

Установка PostgreSQL

Если PostgreSQL не установлен, то запустите команду:

sudo apt-get install -y postgresql

Запустите psql:

sudo -u postgres psql

Выполните SQL команды, указав ваш пароль вместо "123":

drop user if exists falprs;
create user falprs with encrypted password '123';
create database frs;
grant all on database frs to falprs;
alter database frs owner to falprs;
create database lprs;
grant all on database lprs to falprs;
alter database lprs owner to falprs;
\q

Переменные окружения

В директории scripts сделайте копию файла .env.example и назовите .env Укажите актуальные значения для вашей системы. Мажорная версия PostgreSQL задаётся переменной PG_VERSION. Установленную версию PostgreSQL можно узнать командой:

psql --version
psql (PostgreSQL) 16.14 (Ubuntu 16.14-0ubuntu0.24.04.1)

Значит, PG_VERSION=16 Остальные переменные с префиксом PG_ определяют подключение к PostgreSQL. Не забудьте указать пароль, который вы использовали при выполнении SQL команд на предыдущем шаге. Версия контейнера с Triton Inference Server задаётся переменной TRITON_VERSION. Определяется вычислительной способностью (Compute Capabilty) — версией аппаратной архитектуры NVIDIA GPU, которая определяет набор доступных инструкций, поддерживаемые технологии (например, Tensor-ядра) и физические ограничения графического процессора. Она необходима для CUDA-компилятора и ПО, чтобы понимать, как эффективно выполнять код на видеокарте. Узнать Compute Capability можно командой:

nvidia-smi --query-gpu=compute_cap --format=csv

Таблица поддержки Compute Capability и версии контейнера

Compute Capability Архитектура GPU Версия контейнера
< 6.0 Maxwell, Kepler, Fermi Не поддерживается
6.x Pascal 24.04
7.0 Volta 24.09
>= 7.5 Turing, Ampere, Ada Lovelace, Blackwell, Hopper Самая последняя
Например, у GPU GTX 1080 Ti архитектура Pascal, значит, TRITON_VERSION=24.04

Рабочая директория проекта задаётся переменной FALPRS_WORKDIR (значение по-умолчанию /opt/falprs). Важно: если вы делаете миграцию с устаревшего проекта FRS, то необходимо с помощью утилиты sha1sum указать хеш-сумму ONNX файла, который вы использовали для создания TensorRT плана старой модели arcface. Это значение указывается в переменной ARCFACE_SHA1. Например, подсчёт хеш-суммы для старого файла glint_r50.onnx:

sha1sum glint_r50.onnx
4fd7dce20b6987ba89910eda8614a33eb3593216  glint_r50.onnx

Тогда ARCFACE_SHA1=4fd7dce20b6987ba89910eda8614a33eb3593216

Сборка проекта

Для сборки проекта используйте скрипт scripts/build_falprs.sh:

sudo ~/falprs/scripts/build_falprs.sh

Создание TensorRT планов моделей нейронных сетей

Для инференса используются TensorRT планы, которые могут быть получены из моделей нейронных сетей в формате ONNX (Open Neural Network Exchange). Для создания планов в рабочей директории используйте скрипт scripts/tensorrt_plans.sh:

sudo ~/falprs/scripts/tensorrt_plans.sh

Настройка проекта

Для первоначального заполнения баз данных выполните команды:

~/falprs/scripts/sql_frs.sh
~/falprs/scripts/sql_lprs.sh

В файле /opt/falprs/config.yaml находится конфигурация проекта. У основных параметров есть описание в комментариях. Для выполнения API методов с обязательной авторизацией, необходимо значение атрибута allow-group-id-without-auth сделать равным нулю в секциях lprs-workflow и frs-workflow. Также возможна работа совместно с отдельным HTTP сервером. В этом случае необходимо заменить значения соответствующих атрибутов локальных путей и префиксов URL в секциях lprs-workflow и frs-workflow.

Для запуска контейнера с Triton Inference Server выполните команду:

sudo ~/falprs/scripts/triton_service.sh

Для создания сервиса falprs и ротации логов выполните команду:

sudo ~/falprs/scripts/falprs_service.sh

Для запуска сервиса falprs выполните команду:

sudo systemctl start falprs.service

Обновление проекта

Перед обновлением убедитесь, что у вас есть актуальный бэкап баз данных проекта. Переменные окружения берутся из упомянутого ваше .env файла. Обновите репозиторий и используйте скрипт scripts/update_falprs.sh:

cd ~/falprs
git pull
git submodule update --init --recursive
sudo ~/falprs/scripts/update_falprs.sh

Что делает скрипт:

  • останавливает сервиc falprs
  • останавливает контейнер с Triton Inference Server
  • собирает проект с помощью скрипта scripts/build_falprs.sh
  • создаёт TensorRT планы моделей нейронных сетей с помощью скрипта scripts/build_falprs.sh
  • запускает контейнер с Triton Inference Server
  • обновляет схемы и данные в БД (старые данные не затираются)
  • запускает сервис falprs

Управление группами видео потоков

Каждый видео поток принадлежит какой-то одной группе. При заполнении первоначальными данными автоматически создаётся группа с названием default. При вызове API методов для этой группы можно не указывать токен авторизации. Для просмотра, добавления и удаления групп можно использовать скрипт utils/vstream_groups.py Устанавливаем зависимости:

sudo apt-get install -y python3-psycopg2 python3-prettytable

Показать список команд:

python3 ~/falprs/utils/vstream_groups.py -h

Пример добавления новой группы в FRS:

python3 ~/falprs/utils/vstream_groups.py -t frs -a "My new group"

Показать список групп в FRS:

python3 ~/falprs/utils/vstream_groups.py -t frs -l

Пример вывода:

+----------+--------------+--------------------------------------+
| id_group |  group_name  |              auth_token              |
+----------+--------------+--------------------------------------+
|    1     |   default    | 4b05cce8-d29e-4e7f-a1fa-247a91f3fd46 |
|    2     | My new group | 74c0e0f0-ea70-47fb-b715-8932baf7e049 |
+----------+--------------+--------------------------------------+

При вызове API методов для новых групп необходимо указывать токен авторизации. Если вы хотите, чтобы токен авторизации стал обязательным для группы default, то установите значение 0 для параметров allow-group-id-without-auth в соответствующих секциях конфигурационного файла проекта (/opt/falprs/config.yaml) и перезапустите сервис.

Примеры

Устанавливаем зависимости:

sudo apt-get install -y nodejs npm libcairo2-dev libpango1.0-dev libjpeg-dev libgif-dev librsvg2-dev

LPRS

Заходим в директорию с примерами:

cd ~/falprs/examples/lprs

Устанавливаем зависимости:

npm i

Запускаем тестовый сервис для обработки колбэков:

node lprs_backend.js

Добавляем "тестовый" видео поток. В новой консоли:

cd ~/falprs/examples/lprs
./test_add_stream.sh

Проверяем, что поток попал в базу данных:

./test_list_streams.sh | jq

В ответе должен быть json-файл. Ждём минуту, чтобы новые данные попали в кэш FALPRS, выполняем команду:

./test_workflow.sh

В результате выполнения этой команды в консоли с тестовым сервисом lprs_backend.js мы должны увидеть строки вида:

[2024-10-16 12:37:16.108] Callback: {"streamId":"test001","date":"2024-10-16T09:37:16.103891056+00:00","eventId":1,"plates":["O588OA68"],"hasSpecial":false}
[2024-10-16 12:37:16.108] Matched numbers: O588OA68
[2024-10-16 12:37:16.162] Save image to file: /tmp/lprs_backend/screenshots/49b507ac-9b7d-43a6-94f0-18f647fc1f5c.jpg

А в директории /tmp/lprs_backend/screenshots должен появиться файл с изображением:

Удаляем "тестовый" видео поток:

./test_remove_stream.sh

Переходим в консоль, из которой запускали тестовый сервис для обработки колбэков и останавливаем его.

FRS

Заходим в директорию с примерами:

cd ~/falprs/examples/frs

Устанавливаем зависимости:

npm i

Запускаем тестовый сервис для обработки колбэков:

node frs_backend.js

Добавляем "тестовый" видео поток. В новой консоли:

cd ~/falprs/examples/frs
./test_add_stream.sh

Проверяем, что поток попал в базу данных:

./test_list_streams.sh | jq

В ответе должен быть json-файл. Ждём 10 секунд, чтобы новые данные попали в кэш FALPRS, выполняем команду регистрации лица:

./test_register_face.sh

Проверяем, что лицо попало в базу данных:

./test_list_all_faces.sh | jq

В ответе должен быть json-файл. Ждём 10 секунд, чтобы новые данные попали в кэш FALPRS, выполняем команду:

./test_workflow.sh

В результате выполнения этой команды в консоли с тестовым сервисом frs_backend.js мы должны увидеть строки вида:

[2024-10-21 15:34:29.180] Callback: {"faceId":1,"eventId":1}
[2024-10-21 15:34:29.180] This is Albert Einstein
[2024-10-21 15:34:29.220] Save image to file: /tmp/frs_backend/screenshots/492d7722d038434d9e2676648991e65e.jpg

А в директории /tmp/frs_backend/screenshots должен появиться файл с изображением:

Удаляем "тестовый" видео поток:

./test_remove_stream.sh

Переходим в консоль, из которой запускали тестовый сервис для обработки колбэков и останавливаем его.

Тесты

Устанавливаем зависимости:

sudo apt-get install -y python3-requests python3-pytest python3-pytest-order

Создаём директории и тестовые базы данных:

cd ~/falprs/tests
./test_prepare.sh

LPRS

pytest -v -s test_api_lprs.py

FRS

pytest -v -s test_api_frs.py

После окончания тестов удаляем директории и тестовые базы данных:

./test_clean.sh

Синхронизация данных со старым проектом FRS

Скрипт sync_data.py из директории utils данного репозитория делает полную синхронизацию: в новой базе удаляет лишние записи и добавляет отсутствующие. Перед запуском скрипта убедитесь, что у вас остановлен старый сервис, а новый правильно настроен и тоже остановлен. Устанавливаем зависимости:

sudo apt-get install -y pip python3-virtualenv

Заходим в директорию со скриптом:

cd ~/falprs/utils

Копируем файл:

cp config.sample.py config.py

В файле config.py замените значения переменных в соответствии с вашей конфигурцией старого и нового сервиса. Переменные вида mysql_ и *old относятся к старому проекту, pg и *_new — к новому. Выполняем команды:

virtualenv venv
source venv/bin/activate
pip install -r requirements.txt
python3 sync_data.py

После работы скрипта должно появиться сообщение об успешном завершении. Удаляем виртуальную среду:

deactivate
rm -rf ./__pycache__/
rm -rf venv

Примеры графиков нагрузки на CPU и GPU

Ниже представлены данные по нагрузке для кластера из двух неодинаковых серверов с разными долями обработки видео потоков. В данном случае — это камеры домофонов, которые установлены в многоквартирных домах, а обработка ведётся в соответствии с детекцией движения.

Первый сервер

Второй сервер