FALPRS
Этот проект является заменой старого. Основные отличия:
- В качестве СУБД используется PostgreSQL.
- Проект использует userver — асинхронный фреймворк с открытым исходным кодом.
- Строгое соблюдение типов данных в API запросах. Например, если ожидается числовое поле, то его нельзя заключать в кавычки.
- Добавлена система распознавания автомобильных номеров (license plate recognition system - LPRS).
Содержание
- LPRS
- Используемые модели нейронных сетей
- Общая схема взаимодействия с LPRS
- Автоматический бан номеров
- FRS
- Используемые модели нейронных сетей
- Общая схема взаимодействия с FRS
- Сборка и настройка проекта
- Системные требования
- Установка драйверов NVIDIA
- Установка Docker Engine
- Установка NVIDIA Container Toolkit
- Установка PostgreSQL
- Сборка проекта
- Создание TensorRT планов моделей нейронных сетей
- Настройка проекта
- Управление группами видео потоков
- Примеры
- LPRS
- FRS
- Тесты
- LPRS
- FRS
- Синхронизация данных со старым проектом FRS
LPRS
Система предназначена для распознавания автомобильных номеров и обнаружения специального транспорта с мигалками: скорая помощь, МЧС, полиция и т.п. На данный момент поддерживаются российские регистрационные знаки типа 1 (ГОСТ Р 50577-93) и 1А (ГОСТ Р 50577-2018):

Используемые модели нейронных сетей
Сервис работает с четырмя нейронными сетями: VDNet, VCNet, LPDNet и LPRNet. VDNet предназначена для поиска транспортных средств по полученному снимку с видео камеры. VCNet определяет, является ли каждое найденное транспортное средство специальным. LPDNet предназначена для поиска автомобильных номеров. LPRNet предназначена для распознавания номеров из полученных LPDNet данных. Модели VDNet, LPDNet и LPRNet обучены с помощью Ultralytics. Модель VCNet получена путем "дообучения" (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 прекращает обработку.
Под обработкой кадра подразумевается следующая цепочка действий:
- Поиск лиц с помощью нейронной сети scrfd.
- Если лица найдены, то каждое проверяется на "размытость" и "фронтальность".
- Если лицо не размыто (чёткое изображение) и фронтально, то с помощью нейронной сети genet определяется наличие маски и тёмных очков.
- Для каждого лица без маски и без тёмных очков, с помощью нейронной сети arcface, вычисляется биометрический шаблон лица, он же дескриптор. Математически, дескриптор представляет собой 512-ти мерный вектор.
- Далее каждый такой дескриптор попарно сравнивается с зарегистрированными в системе. Сравнение делается путём вычисления косинуса угла между векторами-дескрипторами: чем ближе значение к единице, тем больше похоже лицо на зарегистрированное. Если самое максимальное значение косинуса больше порогового значения (параметр tolerance), то лицо считается распознанным и FRS вызывает callback (событие распознавания лица) и в качестве параметров указывает идентификатор дескриптора (faceId) и внутренний для FRS (внешний для бэкенда) идентификатор события (eventId). Если в одном кадре окажется несколько распознанных лиц, то callback будет вызван для самого "качественного" ("лучшего") лица (параметр blur).
- Каждое найденное неразмытое, фронтальное, без маски и без тёмных очков, лучшее лицо временно хранится в журнале 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/build_falprs.sh. Мажорная версия PostgreSQL задаётся переменной PG_VERSION. Установленную версию PostgreSQL можно узнать командой:
psql --version
Рабочая директория проекта задаётся переменной FALPRS_WORKDIR (значение по-умолчанию /opt/falprs), версия контейнера с Triton Inference Server задаётся переменной TRITON_VERSION.
Таблица поддержки Compute Capability и последней версии контейнера
| Compute Capability | Архитектура GPU | Версия контейнера | TensorRT |
|---|---|---|---|
| 6.x | Pascal | 24.04 | 8.6.3 |
| 7.0 | Volta | 24.09 | 10.4.0.26 |
Посмотреть Compute Capability:
nvidia-smi --query-gpu=compute_cap --format=csv
Если у вас GPU с Compute Capability 7.5 или выше, то вы можете использовать самую последнюю версию контейнера. Например, сборка для Ubuntu 24.04 и GPU с архитектурой Volta:
sudo PG_VERSION=16 TRITON_VERSION=24.09 ~/falprs/scripts/build_falprs.sh
Создание TensorRT планов моделей нейронных сетей
Для инференса используются TensorRT планы, которые могут быть получены из моделей нейронных сетей в формате ONNX (Open Neural Network Exchange). Для создания планов в рабочей директории можно использовать скрипт scripts/tensorrt_plans.py. Рабочая директория проекта задаётся переменной FALPRS_WORKDIR (значение по-умолчанию /opt/falprs), версия Triton Inference Server задаётся переменной TRITON_VERSION. Важно: если вы делаете миграцию со старого проекта, то ещё необходимо с помощью утилиты sha1sum указать хеш-сумму ONNX файла, который вы использовали для создания TensorRT плана модели arcface. Это значение указывается в переменной ARCFACE_SHA1. Пример выполнения без миграции:
sudo TRITON_VERSION=24.09 python3 ~/falprs/scripts/tensorrt_plans.py
Примеры выполнения с миграцией для старого файла glint_r50.onnx. Подсчёт хеш-суммы:
sha1sum glint_r50.onnx
4fd7dce20b6987ba89910eda8614a33eb3593216 glint_r50.onnx
Создание TensorRT планов:
sudo TRITON_VERSION=24.09 ARCFACE_SHA1=4fd7dce20b6987ba89910eda8614a33eb3593216 python3 ~/falprs/scripts/tensorrt_plans.py
Настройка проекта
Для первоначального заполнения баз данных выполните команды, указав значения переменных с префиксом pg_ (пароль "123" замените на указанный вами при создании пользователя PostgreSQL):
pg_user=falprs pg_passwd=123 pg_host=localhost pg_port=5432 pg_db=frs ~/falprs/scripts/sql_frs.sh
pg_user=falprs pg_passwd=123 pg_host=localhost pg_port=5432 pg_db=lprs ~/falprs/scripts/sql_lprs.sh
Конфигурация проекта находится в файле /opt/falprs/config.yaml У основных параметров есть описание в комментариях. Некоторые значения нужно заменить.
- В секциях components_manager -> components -> lprs-postgresql-database и components_manager -> components -> frs-postgresql-database в значениях атрибутов dbconnection замените пароль "123" на указанный вами при создании пользователя PostgreSQL , а также другие реквизиты доступа, если они отличаются.
- В секции components_manager -> task_processors -> main-task-processor замените значение worker_threads на количество ядер CPU вашего сервера. Количество можно посмотреть, например, с помощью команды:
cat /proc/cpuinfo | grep processor | wc -l
- В секции components_manager -> task_processors -> fs-task-processor замените значение worker_threads на количество CPU вашего сервера, умноженное на 4.
Для запуска контейнера с Triton Inference Server выполните команду:
sudo TRITON_VERSION=24.09 ~/falprs/scripts/triton_service.sh
Для создания сервиса falprs и ротации логов выполните команду:
sudo ~/falprs/scripts/falprs_service.sh
Для запуска сервиса falprs выполните команду:
sudo systemctl start falprs.service
Управление группами видео потоков
Каждый видео поток принадлежит какой-то одной группе. При заполнении первоначальными данными автоматически создаётся группа с названием 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"
Показать список групп в LPRS:
python3 ~/falprs/utils/vstream_groups.py -t lprs -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