Улучшаем систему видеонаблюдения, ч.2

от автора

Вариант детекции обьектов с помощью CodeProject.AI работал хорошо, но пришлось отдать под него отдельный, хоть и старый, ноутбук, который требовал отдельного питания, заметно грелся, жужжал вентилятором.
Поэтому, с появлением компактного девайса с arm64 и 4 Гб ОЗУ, захотелось перенести всё на него.

К счастью, оказалось что есть готовый Docker и для arm64, достаточно только при создании указать codeproject/ai-server:arm64.

Всё установилось и заработало, причем даже чуть побыстрее чем на старом ноутбуке, и всё в маленькой бесшумной коробочке с питанием от USB.
Но хотелось что-то улучшить…

Стандартно, там внутри для распознавания обьектов используется нечто под названием YOLO 6.5. (да, я понятия не имел что это такое. Работает и хорошо, а что?)
При этом через пользовательский интерфейс система сообщает, что можно обновить до более свежей версии, но при попытке обновить штатным образом, нажатием кнопочек на экране, всё только портится и работать перестает: что-то не удалось найти, что-то не удалось загрузить и так далее.

При этом сама система — это как бы универсальный AI-сервер, на котором можно запускать разные модули, из которых по сути нужен только один.
А что если найти и запустить его отдельно?

Так выяснилось, что YOLO — это довольно известная штука от Ultralytics https://github.com/ultralytics/ultralytics, и актуальная версия там уже 11, а не 6.5.
И что всё это можно запустить под python.
Правда, есть нюанс: я не знаю python, но когда это кого останавливало?

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

docker run -ti —name t1 -v /tmp:/tmp -p 2222:22 -p 5000:5000 debian

За основу берем Дебиан, подключая туда системный /tmp, пробрасывая порт ssh 22 как 2222 и 5000 для веб-доступа.
В данном случае /tmp проброшен был ради упрощения обмена файлами, но как оказалось это была отличная идея, которая затем пригодится.

apt update
apt install vim openssh-server

vim /etc/startup

#!/bin/sh  mkdir -p /var/run/sshd /usr/sbin/sshd -D

chmod 755 /etc/startup

Устанавливаем пароль — заходить и работать удаленно:
passwd

Разрешаем логин рутом (всё равно это тест и сюда посторонние не ходят)
vim /etc/ssh/ssdh_config

... PermitRootLogin yes ...

Теперь можно спокойно ставить всё что хочется:
apt install python3-pip
apt install python3.11-venv

Найденные в интернетах инструкции советуют установить окружение:

python3 -m venv yolo
cd yolo
. bin/activate

Устанавливаю пакет от Ultralytics и отладочный вебсервер Flask
pip install ultralytics
pip install Flask

И пишу свою первую программу на python:

from flask import Flask, request, jsonify, make_response  app = Flask(__name__)  import ultralytics import os  model = ultralytics.YOLO('yolo11n.pt')  @app.route('/detect', methods=['POST']) def predict():      file = request.files['file']     filename = file.filename     file.save(os.path.join('/tmp/', file.filename))      results = model.predict(os.path.join('/tmp/', file.filename))      os.remove(os.path.join('/tmp/', file.filename))     for result in results:         #result.show()  # display to screen         #result.save(os.path.join('/tmp/', "result_"+file.filename))         # Return response         response = make_response(result.to_json())         response.headers["Access-Control-Allow-Origin"] = "*"         return response 

В данном случае создаем примитивный веб-сервер, умеющий принимать файл по URL /detect, запускающий обработку на модели yolo11n.pt.
Файл в процессе сохраняется в каталоге /tmp, который отображен из основной ОС и по факту является ОЗУ (tmpfs), то есть работает максимально быстро.
Результат обработки вроде как мог бы быть выведен на экран — но у нас сервер, поэтому эта функция просто закомментирована.
Также, результатом мог бы быть файл с отрисованными рамками — но я уже их рисую отдельно, и так как мне нужно было — поэтому эта функция тоже закомментирована.

Остается собственно список обьектов, который выводится в формате JSON. А заголовок «Access-Control-Allow-Origin=*» позволит обойти ограничения на кросс-доменные ссылки.

Пробный запуск:
flask —app detect run —host 0.0.0.0

По 5000 порту удается подключить и передать картинку, а в ответ получить список обьектов — всё работает.
Но в документации пишут, что Flask — это отладочный сервер, а нужно запустить что-то более серьезное.

pip install uwsgi
uwsgi —http :5000 —wsgi-file detect.py —callable app —processes 4 —threads 4 —stats :9191

Вот теперь то же самое, но несколько процессов сразу, для возможных одновременных подключений.
Остается сделать скрипт запуска:

#!/bin/sh PATH=/root/yolo8/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin; export PATH  cd /root/yolo  . bin/activate  setsid uwsgi --http :5000 --wsgi-file detect.py \         --callable app --processes 4 --threads 4 \         --stats :9191 > /dev/null 2>/var/log/yolo_error.log & 

И вызывать его из файла /etc/startup

Перезапускаем контейнер:
docker commit t1 yolo
docker stop t1
docker rm t1
docker run -d —name yolo -v /tmp:/tmp -p 2222:22 -p 5000:5000 yolo /etc/startup

Теперь по порту 5000 доступен детектор изображений, а по порту 2222 можно подключиться по ssh и поправить что-то, если потребуется.

Немного изменился формат JSON-ответа, поэтому вносим изменения в модуль MyImgAI.pm, примерно так:

my $d = from_json($1); -- if($d->{count}){ ++ if(defined $d){  my $cnt = 0; -- foreach my $x (@{$d->{predictions}}){ ++ foreach my $x (@$d){  -- my $px_max   = $x->{x_max}; -- my $px_min   = $x->{x_min}; -- my $py_max   = $x->{y_max}; -- my $py_min   = $x->{y_min}; ++ my $px_max   = $x->{box}->{x1}; ++ my $px_min   = $x->{box}->{x2}; ++ my $py_max   = $x->{box}->{y1}; ++ my $py_min   = $x->{box}->{y2};  -- my $key = $ch.'_'.$x->{label}.'_'.$px_max.'_'.$px_min.'_'.$py_max.'_'.$py_min; ++ my $key = $ch.'_'.$x->{name}.'_'.$px_max.'_'.$px_min.'_'.$py_max.'_'.$py_min;  -- if($x->{label} eq 'person'){ -- $im->rectangle($x->{x_min},$x->{y_min},$x->{x_max},$x->{y_max},$red); ++ if($x->{name} eq 'person'){ ++ $im->rectangle($x->{box}->{x1},$x->{box}->{y1},$x->{box}->{x2},$x->{box}->{y2},$red);  -- elsif($x->{label} eq 'car'){ -- $im->rectangle($x->{x_min},$x->{y_min},$x->{x_max},$x->{y_max},$blue); ++ elsif($x->{name} eq 'car'){ ++ $im->rectangle($x->{box}->{x1},$x->{box}->{y1},$x->{box}->{x2},$x->{box}->{y2},$blue);  -- $im->rectangle($x->{x_min},$x->{y_min},$x->{x_max},$x->{y_max},$green); ++ $im->rectangle($x->{box}->{x1},$x->{box}->{y1},$x->{box}->{x2},$x->{box}->{y2},$green);  

Теперь все работает почти как раньше, только без оболочки в виде дополнительного сервера с не очень понятными ошибками обновления.
И чуть быстрее.


ссылка на оригинал статьи https://habr.com/ru/articles/864746/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *