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>
This commit is contained in:
@@ -17,21 +17,25 @@
|
||||
#include "../source.h"
|
||||
#include "cell.hpp"
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace cfc {
|
||||
|
||||
class CameraCell : public Cell {
|
||||
public:
|
||||
CameraCell(const Rect& geom, cfc_source_t* source)
|
||||
: Cell(geom), source_(source) {}
|
||||
CameraCell(const Rect& geom, cfc_source_t* source, std::string source_key = {})
|
||||
: Cell(geom), source_(source), source_key_(std::move(source_key)) {}
|
||||
|
||||
void set_source(cfc_source_t* src) noexcept { source_ = src; }
|
||||
cfc_source_t* source() const noexcept { return source_; }
|
||||
const std::string& source_key() const noexcept { return source_key_; }
|
||||
|
||||
protected:
|
||||
void draw_content(CUstream stream, NV12Ref& dst) override;
|
||||
|
||||
private:
|
||||
cfc_source_t* source_; /* non-owning — pool владеет */
|
||||
std::string source_key_;
|
||||
};
|
||||
|
||||
} // namespace cfc
|
||||
|
||||
@@ -47,6 +47,11 @@ public:
|
||||
const std::string& name() const noexcept { return current_name_; }
|
||||
int cell_count() const noexcept { return static_cast<int>(cells_.size()); }
|
||||
|
||||
/* Найти текущий pixel-rect для камеры с заданным cuframes-key. NULL
|
||||
* если этой камеры в layout сейчас нет. Используется detbox-overlay'ями
|
||||
* для пересчёта bbox при смене layout. */
|
||||
const Rect* find_camera_cell_rect(const std::string& source_key) const;
|
||||
|
||||
private:
|
||||
std::vector<std::unique_ptr<Cell>> cells_;
|
||||
std::string current_name_;
|
||||
|
||||
@@ -73,6 +73,7 @@ public:
|
||||
int size() const { return static_cast<int>(entries_.size()); }
|
||||
PoolEntry* by_index(int i) { return i >= 0 && i < size() ? entries_[i].get() : nullptr; }
|
||||
PoolEntry* by_key(const std::string& key);
|
||||
PoolEntry* by_frigate_camera(const std::string& frigate_camera);
|
||||
|
||||
/* Уведомить о motion (вызывается из Frigate MQTT subscriber'а через
|
||||
* C-shim). Если zone-filter задан — проверяет пересечение. */
|
||||
|
||||
@@ -176,6 +176,13 @@ int cfc_overlay_create_detection_boxes(
|
||||
* правильный 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 (объект вне зон)
|
||||
|
||||
@@ -362,6 +362,26 @@ NV12Ref Composer::compose_frame()
|
||||
/* Backward-compat overlays (CLI text/icon, detbox) — поверх Layout. */
|
||||
for (auto* ov : overlays_) {
|
||||
if (!ov) continue;
|
||||
|
||||
/* Detection box рисуется в координатах cell камеры. Cell может
|
||||
* перемещаться по экрану при смене layout — синхронизируем cell-geom
|
||||
* перед каждым draw. */
|
||||
if (cfc_overlay_get_type(ov) == CFC_OVERLAY_DETECTION_BOXES) {
|
||||
const char* fcam = cfc_overlay_detbox_camera_key(ov);
|
||||
if (fcam) {
|
||||
PoolEntry* e = pool_.by_frigate_camera(fcam);
|
||||
if (e) {
|
||||
const Rect* r = layout_.find_camera_cell_rect(e->cuframes_key);
|
||||
if (r) {
|
||||
cfc_overlay_detbox_set_cell_geom(ov, r->x, r->y, r->w, r->h);
|
||||
} else {
|
||||
/* Камеры нет в текущем layout — скрываем рамки. */
|
||||
cfc_overlay_detbox_set_cell_geom(ov, 0, 0, 0, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
cfc_overlay_draw(ov, reinterpret_cast<CUstream>(stream_),
|
||||
y, pitch_y_, uv, pitch_uv_,
|
||||
cfg_.width, cfg_.height);
|
||||
|
||||
+13
-1
@@ -45,7 +45,8 @@ void Layout::apply(const LayoutTemplate& tpl,
|
||||
for (std::size_t i = 0; i < camera_templates.size(); ++i) {
|
||||
Rect r = to_pixels(*camera_templates[i], frame_w, frame_h);
|
||||
if (i < active_sorted.size() && active_sorted[i] && active_sorted[i]->source) {
|
||||
auto cell = std::make_unique<CameraCell>(r, active_sorted[i]->source);
|
||||
auto cell = std::make_unique<CameraCell>(r, active_sorted[i]->source,
|
||||
active_sorted[i]->cuframes_key);
|
||||
cell->add_decoration(std::make_unique<BorderDecoration>(border_style));
|
||||
/* Label с именем камеры и приоритетом. */
|
||||
char label_buf[96];
|
||||
@@ -79,4 +80,15 @@ void Layout::render(CUstream stream, NV12Ref& dst)
|
||||
for (auto& c : cells_) c->draw(stream, dst);
|
||||
}
|
||||
|
||||
const Rect* Layout::find_camera_cell_rect(const std::string& source_key) const
|
||||
{
|
||||
for (auto& c : cells_) {
|
||||
auto* cc = dynamic_cast<const CameraCell*>(c.get());
|
||||
if (cc && cc->source_key() == source_key) {
|
||||
return &cc->geometry();
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
} // namespace cfc
|
||||
|
||||
@@ -77,6 +77,14 @@ PoolEntry* SourcePool::by_key(const std::string& key)
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
PoolEntry* SourcePool::by_frigate_camera(const std::string& fcam)
|
||||
{
|
||||
for (auto& e : entries_) {
|
||||
if (e->frigate_camera == fcam) return e.get();
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void SourcePool::motion_pulse(const std::string& frigate_camera,
|
||||
const std::vector<std::string>& current_zones)
|
||||
{
|
||||
|
||||
@@ -647,6 +647,21 @@ const char *cfc_overlay_detbox_camera_key(cfc_overlay_t *ov)
|
||||
return ov->u.detbox.camera_key;
|
||||
}
|
||||
|
||||
int cfc_overlay_detbox_set_cell_geom(cfc_overlay_t *ov,
|
||||
int cell_x, int cell_y,
|
||||
int cell_w, int cell_h)
|
||||
{
|
||||
if (!ov || ov->type != CFC_OVERLAY_DETECTION_BOXES) return -1;
|
||||
detbox_data_t *d = &ov->u.detbox;
|
||||
pthread_mutex_lock(&d->mu);
|
||||
d->cfg.cell_x = cell_x;
|
||||
d->cfg.cell_y = cell_y;
|
||||
d->cfg.cell_w = cell_w;
|
||||
d->cfg.cell_h = cell_h;
|
||||
pthread_mutex_unlock(&d->mu);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int cfc_overlay_detbox_match_zones(cfc_overlay_t *ov,
|
||||
const char *const *current_zones,
|
||||
int n)
|
||||
|
||||
Reference in New Issue
Block a user