Files
cuframes-composer/include/cuframes_composer/overlay.h
T
gx d8e69c6392 Phase 11b: detection-box bbox следует за камерой при смене layout
User: "при движении объект оборачивается в рамку, но функционал не
учитывает что сетки могут переключаться и координаты ячеек меняются".

Был баг: detbox-overlay хранил cell_x/y/w/h из --detection-cell CLI
(заданы при старте), при смене layout рамки рисовались по старым
координатам — мимо камеры.

Изменения:
  - overlay.h/.c: новый API cfc_overlay_detbox_set_cell_geom(ov,x,y,w,h).
    Mutex-защищённое обновление detbox config'а — composer вызывает
    перед каждым draw.
  - CameraCell: добавлено поле source_key (хранит cuframes-key камеры,
    рендерящейся в этом cell). Layout::apply передаёт его из pool entry.
  - Layout::find_camera_cell_rect(key) — возвращает Rect текущей cell
    для камеры с заданным cuframes-key (или nullptr если её нет в layout).
  - SourcePool::by_frigate_camera(name) — lookup pool-entry по
    Frigate-camera-key (frigate event'ы приходят с этим именем).
  - Composer::compose_frame: перед draw каждого DETECTION_BOXES overlay'я
    — lookup frigate→cuframes_key→layout cell rect, обновляет detbox geom.
    Если камера не в layout сейчас — cell_w/h=0, detbox draw skip'ает.

Теперь bbox от Frigate переезжает за камерой:
  - tpl_1 → bbox в full screen 1920×1080
  - tpl_3 → bbox в main 1440×810
  - tpl_4 → bbox в quad ячейке 960×540
  - камера не в layout → bbox скрыт

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-04 10:25:25 +01:00

235 lines
13 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/* cuframes-composer — overlay'и (border, png-иконка, текст) поверх
* композиции.
*
* Архитектура:
* cfc_overlay_t — opaque объект одного overlay'я (border, png, text).
* composer ведёт список overlays + порядок (z-order); на каждом compose'е
* проходит по списку и рисует их поверх NV12 grid'а.
*
* Типы overlay'ев:
* BORDER — цветная рамка вокруг rect'а (Phase 3a). Реализован через 4
* fill_nv12 операции (top/bottom/left/right). Полезно для:
* - визуальной отбивки ячеек grid'а
* - подсветки активных/неактивных источников (красная при DEAD)
* PNG — RGBA-изображение (Phase 3b). Decode-раз через stb_image,
* upload в VRAM, на каждом кадре alpha-blit через
* cfc_kern_alpha_blit_rgba_*. Для статичных иконок (NO SIGNAL,
* offline, recording dot).
* TEXT — динамический рендер строки (Phase 3c). Требует FreeType +
* font atlas. Сделать позже когда FreeType добавлен как зависимость.
*
* Жизненный цикл:
* cfc_overlay_create_*() — выделяет ресурсы (для PNG/TEXT — VRAM-атлас).
* cfc_overlay_update_*() — обновляет параметры (положение, видимость, текст).
* cfc_overlay_draw() — вызывается composer'ом на каждом кадре.
* cfc_overlay_destroy() — освобождает VRAM.
*
* Phase 3a лимит: только BORDER через fill_nv12.
*
* Лицензия: LGPL-2.1+
*/
#ifndef CUFRAMES_COMPOSER_OVERLAY_H
#define CUFRAMES_COMPOSER_OVERLAY_H
#include <cuda.h>
#include <stdint.h>
#ifdef __cplusplus
extern "C" {
#endif
typedef enum cfc_overlay_type {
CFC_OVERLAY_BORDER = 0,
CFC_OVERLAY_PNG = 1, /* Phase 3b */
CFC_OVERLAY_TEXT = 2, /* Phase 3c */
CFC_OVERLAY_DETECTION_BOXES = 3, /* Phase 7 — Frigate-driven rectangles */
} cfc_overlay_type_t;
typedef struct cfc_overlay cfc_overlay_t;
/* Назначить overlay'ю короткий ID для lookup через control plane (Phase 4).
* ID копируется внутрь overlay'я (макс 31 символ). Без ID overlay тоже работает,
* но управлять им runtime'ом нельзя. Можно вызвать после create. */
int cfc_overlay_set_id(cfc_overlay_t *ov, const char *id);
/* Получить ID overlay'я (NULL если не задан). */
const char *cfc_overlay_get_id(const cfc_overlay_t *ov);
/* Получить тип overlay'я. */
cfc_overlay_type_t cfc_overlay_get_type(const cfc_overlay_t *ov);
/* Параметры BORDER overlay'я. */
typedef struct cfc_overlay_border_config {
int x, y, w, h; /* прямоугольник в full-res пикселях */
int thickness; /* толщина рамки в пикселях */
int color_y, color_u, color_v; /* цвет в BT.709 (Y:16-235, UV:16-240) */
int alpha; /* 0..255; 0 = invisible */
int visible; /* 0/1 — выводить ли вообще */
} cfc_overlay_border_config_t;
/* Создать BORDER overlay. */
int cfc_overlay_create_border(const cfc_overlay_border_config_t *cfg,
cfc_overlay_t **out);
/* Параметры PNG overlay'я. */
typedef struct cfc_overlay_png_config {
const char *path; /* путь к PNG-файлу (декод один раз) */
int x, y; /* позиция top-left на output буфере (чётные) */
int extra_alpha; /* 0..255 общий множитель прозрачности */
int visible; /* 0/1 — выводить ли */
} cfc_overlay_png_config_t;
/* Создать PNG overlay. Декодирует файл через libpng, аллоцирует RGBA-атлас
* в VRAM (cuMemAlloc), копирует туда декодированные пиксели один раз.
*
* Размер изображения берётся из PNG header'а — caller узнаёт width/height
* через cfc_overlay_png_size после create. */
int cfc_overlay_create_png(const cfc_overlay_png_config_t *cfg,
cfc_overlay_t **out);
/* Получить реальный размер декодированного PNG. После create — стабильны
* до destroy. */
int cfc_overlay_png_size(cfc_overlay_t *ov, int *width, int *height);
/* Обновить параметры PNG overlay'я (без re-decode). */
int cfc_overlay_update_png(cfc_overlay_t *ov,
const cfc_overlay_png_config_t *cfg);
/* Параметры TEXT overlay'я (Phase 3c). */
typedef struct cfc_overlay_text_config {
const char *font_path; /* путь к .ttf / .otf */
const char *text; /* UTF-8 строка для рендера */
int pixel_size; /* высота glyph'а в пикселях (10..200) */
int x, y; /* top-left на output буфере (чётные) */
int r, g, b; /* sRGB цвет 0..255 */
int extra_alpha; /* 0..255 общий множитель прозрачности */
int visible; /* 0/1 — выводить ли */
/* Опциональный полупрозрачный фон (подложка) под текстом.
* bg_alpha = 0 → без фона (default)
* bg_alpha > 0 → fill rect (atlas_w + 2*bg_pad) × (atlas_h + 2*bg_pad)
* с цветом bg_y/u/v перед blit'ом текста.
* bg_pad чётный, default 8 если bg_alpha>0 и bg_pad==0. */
int bg_alpha; /* 0..255 (0 = отключено) */
int bg_y, bg_u, bg_v; /* BT.709 limited (Y=16..235, UV=16..240) */
int bg_pad; /* px padding вокруг текста */
} cfc_overlay_text_config_t;
/* Создать TEXT overlay. Открывает font через FreeType, рендерит строку
* в RGBA-атлас на CPU (alpha-channel из glyph bitmap'ов anti-aliased),
* заливает в VRAM. */
int cfc_overlay_create_text(const cfc_overlay_text_config_t *cfg,
cfc_overlay_t **out);
/* Обновить TEXT overlay. Если text изменился — re-render atlas (VRAM
* перевыделяется). font_path и pixel_size менять нельзя — заведите новый
* overlay (face и связанные ресурсы пере-init'ить дорого). */
int cfc_overlay_update_text(cfc_overlay_t *ov,
const cfc_overlay_text_config_t *cfg);
/* Получить ширину/высоту текущего рендеренного текста (в пикселях). */
int cfc_overlay_text_size(cfc_overlay_t *ov, int *width, int *height);
/* ── DETECTION_BOXES (Phase 7) ─────────────────────────────────────────
* Managed-список border'ов для bbox'ов от Frigate. Один overlay соответствует
* одной композитор-ячейке (одной камере). MQTT-subscriber (отдельный thread)
* вызывает _upsert при каждом frigate/events update и _end при event завершён.
* draw_detection_boxes итерирует по active box'ам, проверяет TTL, рисует
* рамку с координатным mapping'ом detect → cell.
*
* Координаты Frigate отдаются в detect-разрешении камеры (640×480 для
* parking_overview), composer ячейка обычно 960×540 на 1080p output → нужен
* линейный scale. Mapping вынесен внутрь draw, не кэшируется — layout switch
* безопасен. */
typedef struct cfc_overlay_detbox_config {
/* Какая Frigate-камера → какая ячейка composer'а. */
const char *camera_key; /* "parking_overview" — для lookup'а */
int detect_w, detect_h; /* Frigate detect.{width,height} */
int cell_x, cell_y, cell_w, cell_h; /* куда мапится на output frame */
/* Стиль рамки. */
int thickness; /* px (4-8 рекомендуется) */
int color_y, color_u, color_v; /* BT.709 limited (Y=16-235, UV=16-240) */
int alpha; /* 0..255 */
/* TTL — независимая страховка от потери MQTT events.
* Если последний update был > stale_ms назад — рамка пропадает.
* Рекомендуется 5000-8000 мс (Frigate publish раз в минуту для stationary,
* меньше при движении). */
int stale_ms;
/* Список разрешённых zones — если задан, MQTT subscriber пропускает
* события у которых after.current_zones пусто либо не пересекается с
* этим списком. Это робастный фильтр когда Frigate 0.17 schema не даёт
* native objects.filters.<obj>.required_zones (см. commit 1726137).
* NULL или пустой массив → принимать все события. */
const char *const *required_zones; /* массив строк */
int required_zones_count;
} cfc_overlay_detbox_config_t;
int cfc_overlay_create_detection_boxes(
const cfc_overlay_detbox_config_t *cfg,
cfc_overlay_t **out
);
/* Получить camera_key из overlay'я — для MQTT subscriber'а чтобы найти
* правильный overlay по incoming event'у. */
const char *cfc_overlay_detbox_camera_key(cfc_overlay_t *ov);
/* Обновить cell-геометрию runtime (при смене layout композитора). Композитор
* вызывает перед draw каждым detbox-overlay'ем — пересчитывает положение
* рамки под текущую позицию камеры. */
int cfc_overlay_detbox_set_cell_geom(cfc_overlay_t *ov,
int cell_x, int cell_y,
int cell_w, int cell_h);
/* Проверить пересечение current_zones события с required_zones overlay'я.
* - если required_zones пуст → всегда 1 (filter off)
* - если current_zones пуст → 0 (объект вне зон)
* - иначе 1 если хотя бы одна current ∈ required, 0 иначе
* Использует overlay subscriber для отсева street-флуда (см. commit 1726137). */
int cfc_overlay_detbox_match_zones(cfc_overlay_t *ov,
const char *const *current_zones,
int n);
/* Upsert одного active детекта.
* event_id — идентификатор Frigate event'а (для трекинга/end).
* label — "car", "person", и т.п. (для будущего цветового кодирования).
* x1/y1/x2/y2 — bbox в detect-разрешении (raw Frigate coords).
* frame_time_ms — Frigate frame_time для TTL.
* Thread-safe (mutex внутри). */
int cfc_overlay_detbox_upsert(
cfc_overlay_t *ov,
const char *event_id,
const char *label,
int x1, int y1, int x2, int y2,
int64_t frame_time_ms
);
/* End event'а — удалить рамку (вызывается при `type == "end"` в Frigate). */
int cfc_overlay_detbox_end(cfc_overlay_t *ov, const char *event_id);
/* Обновить параметры BORDER overlay'я (можно переключить visible,
* сменить цвет, изменить позицию). Thread-safe? Нет — caller должен сам
* заботиться о том, чтобы update не пересекался с draw. В рамках одного
* compose-thread'а это естественно. */
int cfc_overlay_update_border(cfc_overlay_t *ov,
const cfc_overlay_border_config_t *cfg);
/* Нарисовать overlay поверх NV12 кадра. Вызывается из cfc_composer_compose.
* Возвращает 0 при успехе. */
int cfc_overlay_draw(cfc_overlay_t *ov,
CUstream stream,
CUdeviceptr dst_y, int pitch_y,
CUdeviceptr dst_uv, int pitch_uv,
int frame_w, int frame_h);
/* Уничтожить overlay (освободить VRAM если был). */
int cfc_overlay_destroy(cfc_overlay_t *ov);
#ifdef __cplusplus
}
#endif
#endif /* CUFRAMES_COMPOSER_OVERLAY_H */