Загрузка данных
Раз на сервере нет дискретной видеокарты, упор нужно делать на **алгоритмические фильтры (отсечение мусора)** и **оптимизацию работы процессора (CPU)**. Когда нейросеть пытается распознать размытое, слишком маленькое, повернутое или плохо освещенное лицо на процессоре, она не только тратит много времени, но и генерирует те самые ложные срабатывания (False Positives), так как вектор («хэш») такого лица получается сильно искаженным.
Вот конкретные архитектурные и программные шаги, как доработать ваш проект `ccsc_legacy_backup.py` для CPU, чтобы свести ложные распознавания к минимуму.
---
### 1. Фильтрация лиц по качеству (Face Quality Blur Assessment)
Самый эффективный способ убрать ложные срабатывания — запретить нейросети распознавать смазанные кадры (когда человек идет, и камера не успевает сфокусироваться).
Перед тем как отправлять лицо на сравнение с базой данных, проверьте его на размытость с помощью простого и очень быстрого метода **Лапласиана** (он работает на CPU мгновенно):
```python
import cv2
def is_blur(face_img, threshold=100):
"""
Возвращает True, если изображение размыто.
threshold подбирается экспериментально (обычно от 70 до 150).
Чем меньше значение, тем более размытые кадры будут пропускаться.
"""
gray = cv2.cvtColor(face_img, cv2.COLOR_BGR2GRAY)
variance = cv2.Laplacian(gray, cv2.CV_64F).var()
return variance < threshold
```
**Куда вставить в ваш код:** В цикле обработки лиц (внутри `ai_worker`), как только детектор определил координаты лица (`bbox`), вырежьте этот кусочек из кадра: `face_crop = frame[y1:y2, x1:x2]`. Если `is_blur(face_crop)` возвращает `True` — просто делайте `continue` и пропускайте этот кадр.
---
### 2. Фильтрация по углам поворота головы (Pose Estimation)
InsightFace при детекции выдает не только рамку (`bbox`), но и ключевые точки лица (`landm` или `kps` — глаза, нос, уголки рта). Если человек повернулся к камере боком или сильно наклонил голову, модель `buffalo_l` всё равно попытается извлечь вектор, но он будет ошибочным, что приведет к ложному совпадению с кем-то из базы.
В объекте `face`, который возвращает `app.get()`, уже есть готовые углы поворота головы (аттрибут `pose`: *pitch* (наклон вверх/вниз), *yaw* (поворот влево/вправо), *roll* (наклон к плечу)).
* **Добавьте жесткий фильтр в `ai_worker`:**
```python
# Проверка, что человек смотрит относительно прямо в камеру
if hasattr(face, 'pose'):
pitch, yaw, roll = face.pose
if abs(yaw) > 30 or abs(pitch) > 25:
continue # Пропускаем профили и наклоненные головы
```
```
---
### 3. Метод «Голосования» по N кадрам (Важнейший шаг для видеопотока)
В вашей текущей архитектуре скрипт, скорее всего, принимает решение по одному единственному кадру: совпало расстояние — выдаем лог/уведомление. На CPU из-за шума матрицы камеры один кадр может «скакнуть» и показаться похожим на человека из базы.
**Решение:** Сделайте буфер распознавания внутри `ai_worker`.
* Вместо мгновенного срабатывания, запоминайте ID распознанного человека для конкретного трека (или в течение последних 2-3 секунд).
* Отправляйте лог/уведомление только тогда, когда один и тот же ID определился, например, **3 раза за последние 5 кадров**. Если это была случайная ложная детекция — она проскочит на 1 кадре и исчезнет, не создав ложного лога.
---
### 4. Оптимизация ONNX Runtime под CPU
Чтобы процессор не захлебывался при увеличении разрешения, нужно правильно настроить параметры `providers` в ONNX. По умолчанию он может использовать только одно ядро или, наоборот, пытаться хаотично задействовать все потоки, вызывая микрофризы.
Измените инициализацию во всех местах (`api_upload_person`, `api_db_mass_upload` и главном потоке):
```python
import onnxruntime as ort
# Настройка опций для CPU
opts = ort.SessionOptions()
opts.intra_op_num_threads = 4 # Задайте жестко количество ядер (например, 4)
opts.inter_op_num_threads = 2
opts.execution_mode = ort.ExecutionMode.ORT_SEQUENTIAL
# Передаем опции в InsightFace
temp_app = FaceAnalysis(name='buffalo_l', allowed_modules=['detection', 'recognition'], providers=['CPUExecutionProvider'])
# В зависимости от версии insightface, опции сессии можно прокинуть через провайдеры:
# providers=[('CPUExecutionProvider', {'intra_op_num_threads': '4'})]
```
---
### 5. Использование каскадного изменения `det_size`
В первом ответе я советовал поднять размер до `1280x1280`, но на CPU это может сильно снизить FPS. Чтобы найти баланс между точностью детекции мелких (дальних) лиц и скоростью процессора:
* Установите компромиссный размер: `det_size=(960, 960)`. Это значительно точнее, чем `640x640`, но процессор справится с этим гораздо легче, чем с FullHD-картинкой.
---
### Резюме: Идеальный пайплайн для CPU внутри вашего `ai_worker`
Каждый кадр из RTSP-потока должен проходить следующий жесткий фильтр перед тем, как вы начнете вычислять косинусное расстояние (`np.linalg.norm`):
```
[Кадр из RTSP]
│
▼
[Детектор InsightFace (960x960)]
│
▼
[Размер лица > 80x80px?] ──(Нет)──> Пропустить кадр
│ (Да)
▼
[Поворот головы (Yaw/Pitch) < 30°?] ──(Нет)──> Пропустить кадр
│ (Да)
▼
[Размытость (Лапласиан) < порога?] ──(Да)──> Пропустить кадр
│ (Нет, кадр четкий)
▼
[Извлечение 512-мерного вектора (Embedding)]
│
▼
[Сравнение с базой (Порог строгости 0.52)]
│
▼
[Проверка через буфер: Повторилось 3 раза?] ──(Нет)──> Ждем еще кадры
│ (Да)
▼
[ЗАПИСЬ В БАЗУ / СРАБАТЫВАНИЕ ТЕЛЕГРАМА]
```
Внедрение даже двух фильтров (по размеру лица и по размытости Лапласиана) уберет до 90% ложных срабатываний, так как нейросеть перестанет анализировать "кашу" на заднем плане кадра. Порог косинусного расстояния при этом держите в районе **0.50–0.53** (для ArcFace/Buffalo).