feat: per-class max box fraction (REQ-188)
- labeling/preview/autolabel: max_box_frac + per-class max_box_fracs ceiling filter (0=none, 1=off) before NMS, mirrors min_box_frac - exemplar review-assist: ceiling before max_detections truncation; ExemplarLabelRequest + filter panel 'Max box size' slider - ClassParamsTable: MaxBox column; copy line now REQ-186 amended order: class <name> conf <v> iou <v> minbox <v> maxbox <v> container - docs: design/ui-spec/tasks updated (incl. stale 4-slider narrative)
This commit is contained in:
1 parent
dee58e4ae5
commit
d3a6aa49b6
13 files changed
+90
-25
No files matched your search
+7
-7
@@ -181,7 +181,7 @@ GET /api/batches/{id} # status + review progress (REQ-
|
||||
GET /api/batches/{id}/frames # frames + statuses
|
||||
POST /api/batches/{id}/autolabel # {threshold, class_params} → job (REQ-030,032,034,181)
|
||||
POST /api/batches/{id}/preview # one frame, run now, nothing written;
|
||||
# +class_params {name: {threshold?, iou_threshold?, min_box_frac?}}
|
||||
# +class_params {name: {threshold?, iou_threshold?, min_box_frac?, max_box_frac?}}
|
||||
# empty override = global value, keys are class names (REQ-181)
|
||||
# +exemplars[] {box:[cx,cy,w,h], positive} — the touched class's own pool only
|
||||
# +exemplar_class_name, +target_class_names; per-class, accumulating
|
||||
@@ -249,10 +249,10 @@ updated. Range and fps live on the batch, so one video can be used repeatedly.
|
||||
**autolabel (REQ-030…034).** Per frame: one `set_image`, then loop each class's prompt (see
|
||||
the domain invariants in `../AGENTS.md`), cross-class greedy NMS (REQ-031), write `annotations` rows with
|
||||
`source='auto'`. `class_params` (REQ-181) is consulted per class: a numeric
|
||||
`threshold` / `iou_threshold` / `min_box_frac` entry replaces the job's global for that class
|
||||
`threshold` / `iou_threshold` / `min_box_frac` / `max_box_frac` entry replaces the job's global for that class
|
||||
only; classes without an entry — and a run with `class_params` empty or absent — take the
|
||||
exact global path. The YOLO confidence floor uses `min([global] + overrides)`, the SAM3
|
||||
threshold/dedup/min-box lists are built only from classes that actually override that key,
|
||||
threshold/dedup/min-box/max-box lists are built only from classes that actually override that key,
|
||||
and `/preview` applies the same overrides so the preview and the job cannot disagree.
|
||||
The job and `/preview` also read `project_classes.container` per class when building the
|
||||
container-id set for the NMS containment carve-out — same stored flag for both (REQ-184,
|
||||
@@ -363,7 +363,7 @@ per-class overrides table also carries a **Container** checkbox (REQ-184) that t
|
||||
`project_classes.container` through `PATCH /api/projects/{id} { containers: … }`. The same
|
||||
table carries a **Copy** button (REQ-186): one click puts every selected class's effective
|
||||
settings (per-class override where set, else the global slider; container = the checkbox
|
||||
state) on the clipboard as short lines `class <name> conf … iou … minbox … container
|
||||
state) on the clipboard as short lines `class <name> conf … iou … minbox … maxbox … container
|
||||
true|false`, through `clipboard.js`'s `copyText` (Clipboard API with an `execCommand`
|
||||
fallback for insecure contexts) — read-only, no network, no setting changed. Because
|
||||
`ClassParamsTable` is shared, the mass modal carries the button too.
|
||||
@@ -394,7 +394,7 @@ with the shortened pool.
|
||||
The run is a **dry run by default** (REQ-175). `ExemplarFilterPanel.jsx` is passed to
|
||||
`ReviewSidebar` and rendered above the class list — not over the frame, which is where its
|
||||
proposals are drawn. It is mounted only while a run is undecided (`pool.active`): the first
|
||||
drag opens it, Apply and Discard close it, and the pool outlives it. Its four sliders
|
||||
drag opens it, Apply and Discard close it, and the pool outlives it. Its five sliders
|
||||
re-preview on a 250 ms debounce. While a preview is up the canvas **hides the stored shapes
|
||||
of the class under review** — the run replaces them wholesale, and leaving them on screen made
|
||||
a rejected detection look like it had never gone; other classes stay, dimmed
|
||||
@@ -419,7 +419,7 @@ POST /api/frames/{id}/exemplar-label
|
||||
{ "exemplars": [{"box": [x0,y0,x1,y1], "positive": true}, …], # normalized xyxy
|
||||
"class_id": 0,
|
||||
"threshold": 0.5, "iou_threshold": 0.8, # the panel (REQ-175)
|
||||
"min_box_frac": 0.002, "max_detections": 100,
|
||||
"min_box_frac": 0.002, "max_box_frac": 1.0, "max_detections": 100,
|
||||
"apply": false }
|
||||
→ { "shapes": [{"geometry": …, "score": …, "source": "manual"|"auto"}, …],
|
||||
"applied": false, "redetected": true, "message": null,
|
||||
@@ -440,7 +440,7 @@ then rewrites the frame **for that class only**:
|
||||
while the drawn shapes survive.
|
||||
|
||||
The panel's filters run before any of that, in the order the batch job uses them: area floor,
|
||||
then NMS (`labeling.deduplicate`), then the cap on how many survive.
|
||||
then ceiling (REQ-188), then NMS (`labeling.deduplicate`), then the cap on how many survive.
|
||||
|
||||
The delete-and-reinsert happens in one `db.cursor()` transaction, so the frame is never
|
||||
briefly empty. Other classes on the frame are never touched. The GPU lock is taken with a
|
||||
|
||||
Reference in new issue
Block a user