revert: undo chained/seed-grown track 5 frames, back to b71f1d4
Reverts1530bbcand9c95a4a. Track 5 Frames sends the drawn box unchanged to all five frames - no chaining, no growth. Both were rejected: chaining let a wrong read on one frame poison the next, and growth was an attempt to follow a moving object that the user did not want. REQ-189, ui-spec and tasks.md return to theb71f1d4wording.
This commit is contained in:
1 parent
9c95a4a759
commit
f6b0335c93
4 files changed
+27
-83
No files matched your search
+7
-14
@@ -170,20 +170,13 @@ changes.
|
|||||||
one object and the model produces its shape.
|
one object and the model produces its shape.
|
||||||
- **REQ-189** — In the review editor, the user can push the **selected shape's** box forward
|
- **REQ-189** — In the review editor, the user can push the **selected shape's** box forward
|
||||||
with *Track 5 Frames* (`[T]`): the next five frames each get their own REQ-043 box-assist
|
with *Track 5 Frames* (`[T]`): the next five frames each get their own REQ-043 box-assist
|
||||||
run, and the shape is written as `source=manual`. Both geometry types work — a bbox shape
|
run, seeded with that box, and the shape is written as `source=manual`. Both geometry types
|
||||||
supplies its own box, a polygon is reduced to its bounding box. Runs are **independent**:
|
work — a bbox shape supplies its own box, a polygon is reduced to its bounding box. Each
|
||||||
every frame is seeded from the drawn shape and never from another run's result, so a wrong
|
frame is an **independent** run: a frame SAM3 refuses (nothing inside the box) is reported
|
||||||
read on one frame cannot carry into the ones after it. The first frame uses the drawn box
|
and the rest still run; nothing selected, an unusable shape, or the last frame says so
|
||||||
untouched; frames 2–5 use it grown about its centre by ×1.5, giving a moving object room
|
instead of doing nothing. This is one-shot hand-off across the next frames, **not**
|
||||||
to be inside the seed window. A frame SAM3 refuses (nothing inside the box) is reported and
|
propagation of drawn exemplars (which stays a non-goal): nothing is carried frame to frame,
|
||||||
the remaining frames still run. Nothing selected, an unusable shape, or the last frame says
|
and the runs do not depend on each other.
|
||||||
so instead of doing nothing. Consequence accepted: without chaining there is no size or
|
|
||||||
position readjustment between frames, so a fast-moving object can still leave the seed
|
|
||||||
window — denser extraction (higher fps) or a video-propagation requirement (not yet
|
|
||||||
written) is the fix. This is
|
|
||||||
one-shot hand-off across the next frames, **not** propagation of drawn exemplars (which
|
|
||||||
stays a non-goal): nothing is tracked frame-to-frame with a motion model, and the runs
|
|
||||||
share no state between invocations.
|
|
||||||
- **REQ-044** — All annotations and review statuses are **persistent** — they survive a
|
- **REQ-044** — All annotations and review statuses are **persistent** — they survive a
|
||||||
server restart, unlike today's in-memory sessions.
|
server restart, unlike today's in-memory sessions.
|
||||||
- **REQ-045** — Review progress is visible (e.g. "120/300 reviewed"), and a batch can only
|
- **REQ-045** — Review progress is visible (e.g. "120/300 reviewed"), and a batch can only
|
||||||
|
|||||||
@@ -1405,23 +1405,6 @@ read it.
|
|||||||
(accent border + glow) — no CSS added; **browser eyeball still owed by the user**
|
(accent border + glow) — no CSS added; **browser eyeball still owed by the user**
|
||||||
(375 px 2-col wrap, long class names).
|
(375 px 2-col wrap, long class names).
|
||||||
|
|
||||||
## Task — Track 5 Frames follows a moving object (REQ-189) `[DONE]`
|
|
||||||
|
|
||||||
1. Root cause: `trackForward` sent the **same box** to all 5 frames, and batch 19 was
|
|
||||||
extracted at **fps 0.5** (one frame every 2 s, `batches.fps`) — a truck entering frame
|
|
||||||
outruns a static seed. First attempt chained each run's result into the next frame's seed;
|
|
||||||
that let a wrong read on one frame carry into the following ones, which the user rejected
|
|
||||||
as poisoning. Final shape: runs are **independent again**, every frame seeded from the
|
|
||||||
drawn shape, frames 2–5 grown ×1.5 about its centre (`SEED_GROWTH`, `growBox`) for motion
|
|
||||||
tolerance. Geometry handling moved to module-level `shapeBox()`.
|
|
||||||
→ verify: **[DONE]** `npm run build` green; no frame's seed derives from a previous result
|
|
||||||
(`seed` reassignment removed, grep confirms `firstBox` is the only seed source); first frame
|
|
||||||
un-grown so the stationary case keeps exact precision; growth ×1.5 total size (not 2×);
|
|
||||||
busy guard still blocks double-writes. **Moving-object behaviour needs the user's GPU
|
|
||||||
run** — and without chaining, a fast-moving truck may still leave the seed window; fix is
|
|
||||||
re-extract at 2 fps (*Trim → frames per second*) or a video-propagation requirement
|
|
||||||
(not yet written; `sam3_video_inference.py` already has `propagate_in_video`).
|
|
||||||
|
|
||||||
## Task — Track 5 Frames works at all (REQ-189) `[DONE]`
|
## Task — Track 5 Frames works at all (REQ-189) `[DONE]`
|
||||||
|
|
||||||
1. `ReviewPage.jsx:trackForward` read `geometry.coordinates` — a key the backend never emits
|
1. `ReviewPage.jsx:trackForward` read `geometry.coordinates` — a key the backend never emits
|
||||||
|
|||||||
+2
-10
@@ -989,21 +989,13 @@ previous geometry** and shows the error. Same optimistic-with-rollback pattern f
|
|||||||
- *Copy Prev* copies every annotation from frame `n-1` onto this frame (one POST per shape);
|
- *Copy Prev* copies every annotation from frame `n-1` onto this frame (one POST per shape);
|
||||||
disabled at index 0 or when the previous frame is empty.
|
disabled at index 0 or when the previous frame is empty.
|
||||||
- *Track 5 Frames* takes the selected shape's bounding box and runs SAM3 assist on the next 5
|
- *Track 5 Frames* takes the selected shape's bounding box and runs SAM3 assist on the next 5
|
||||||
frames with it — the first with that box exactly, the rest with it grown (see below). Works
|
frames with it. Works for both geometry types: a bbox shape supplies its own box, a polygon
|
||||||
for both geometry types: a bbox shape supplies its own box, a polygon
|
|
||||||
is reduced to its bounding box. Nothing selected, an unusable shape, or the last frame
|
is reduced to its bounding box. Nothing selected, an unusable shape, or the last frame
|
||||||
says so in the error slot instead of silently doing nothing; a frame SAM3 refuses (nothing
|
says so in the error slot instead of silently doing nothing; a frame SAM3 refuses (nothing
|
||||||
inside the box) does not stop the rest — the banner then **starts** with `Tracked N of M`
|
inside the box) does not stop the rest — the banner then **starts** with `Tracked N of M`
|
||||||
(M = frames actually ahead, ≤ 5) followed by the per-frame failures. A second press while
|
(M = frames actually ahead, ≤ 5) followed by the per-frame failures. A second press while
|
||||||
a run is in flight is ignored. Shapes are written `source=manual`, exactly as a hand-drawn
|
a run is in flight is ignored. Shapes are written `source=manual`, exactly as a hand-drawn
|
||||||
one.
|
one (REQ-189).
|
||||||
Runs are **independent**: every frame is seeded from the drawn shape, never from another
|
|
||||||
run's result, so a wrong read on one frame cannot poison the ones after it. The first frame
|
|
||||||
uses the drawn box exactly; frames 2–5 use it grown ×1.5 about its centre so a moving object
|
|
||||||
still fits the seed window. A refused frame does not stop the rest. Because nothing carries
|
|
||||||
between frames there is no size/position readjustment — a fast-moving object can still
|
|
||||||
outrun the seed; only denser frame extraction (higher fps) or real video propagation fixes
|
|
||||||
that (REQ-189).
|
|
||||||
|
|
||||||
**Quick reclass bar** appears whenever a shape is selected: one button per class
|
**Quick reclass bar** appears whenever a shape is selected: one button per class
|
||||||
(`[n] name`, class-coloured) plus *Delete [Del]*.
|
(`[n] name`, class-coloured) plus *Delete [Del]*.
|
||||||
|
|||||||
@@ -9,38 +9,6 @@ import ReviewSidebar from '../components/ReviewSidebar'
|
|||||||
import ExemplarFilterPanel from '../components/ExemplarFilterPanel'
|
import ExemplarFilterPanel from '../components/ExemplarFilterPanel'
|
||||||
import useExemplarPool from '../hooks/useExemplarPool'
|
import useExemplarPool from '../hooks/useExemplarPool'
|
||||||
|
|
||||||
// How much bigger the seed box gets for each frame after the first, so the
|
|
||||||
// object has room to have moved. Hand-tuned, not learned (REQ-189). The box
|
|
||||||
// always derives from the drawn shape — never from a previous run's result.
|
|
||||||
const SEED_GROWTH = 1.5
|
|
||||||
|
|
||||||
// SAM3 assist wants one normalized [x0, y0, x1, y1] box (REQ-043). A bbox
|
|
||||||
// shape already is that; a polygon is reduced to its bounding box.
|
|
||||||
function shapeBox(geometry) {
|
|
||||||
const pts = geometry?.points
|
|
||||||
if (geometry?.type === 'bbox' && pts?.length === 4) return pts
|
|
||||||
if (geometry?.type === 'polygon' && pts?.length) {
|
|
||||||
let minX = 1, minY = 1, maxX = 0, maxY = 0
|
|
||||||
for (const [x, y] of pts) {
|
|
||||||
if (x < minX) minX = x
|
|
||||||
if (x > maxX) maxX = x
|
|
||||||
if (y < minY) minY = y
|
|
||||||
if (y > maxY) maxY = y
|
|
||||||
}
|
|
||||||
return [minX, minY, maxX, maxY]
|
|
||||||
}
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
function growBox([x0, y0, x1, y1], k) {
|
|
||||||
const cx = (x0 + x1) / 2
|
|
||||||
const cy = (y0 + y1) / 2
|
|
||||||
const hw = ((x1 - x0) * k) / 2
|
|
||||||
const hh = ((y1 - y0) * k) / 2
|
|
||||||
const clamp = (v) => Math.min(1, Math.max(0, v))
|
|
||||||
return [clamp(cx - hw), clamp(cy - hh), clamp(cx + hw), clamp(cy + hh)]
|
|
||||||
}
|
|
||||||
|
|
||||||
export default function ReviewPage({ batchId: rawBatchId, projectId, onProject }) {
|
export default function ReviewPage({ batchId: rawBatchId, projectId, onProject }) {
|
||||||
const [batch, setBatch] = useState(null)
|
const [batch, setBatch] = useState(null)
|
||||||
const [project, setProject] = useState(null)
|
const [project, setProject] = useState(null)
|
||||||
@@ -399,8 +367,23 @@ export default function ReviewPage({ batchId: rawBatchId, projectId, onProject }
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
const firstBox = shapeBox(current.geometry)
|
// SAM3 assist wants one normalized [x0, y0, x1, y1] box (REQ-043). A bbox
|
||||||
if (!firstBox) {
|
// shape already is that; a polygon is reduced to its bounding box.
|
||||||
|
const geometry = current.geometry
|
||||||
|
let box = null
|
||||||
|
if (geometry?.type === 'bbox' && geometry.points?.length === 4) {
|
||||||
|
box = geometry.points
|
||||||
|
} else if (geometry?.type === 'polygon' && geometry.points?.length) {
|
||||||
|
let minX = 1, minY = 1, maxX = 0, maxY = 0
|
||||||
|
for (const [x, y] of geometry.points) {
|
||||||
|
if (x < minX) minX = x
|
||||||
|
if (x > maxX) maxX = x
|
||||||
|
if (y < minY) minY = y
|
||||||
|
if (y > maxY) maxY = y
|
||||||
|
}
|
||||||
|
box = [minX, minY, maxX, maxY]
|
||||||
|
}
|
||||||
|
if (!box) {
|
||||||
setError('That shape has no usable box — Track 5 Frames needs one.')
|
setError('That shape has no usable box — Track 5 Frames needs one.')
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -417,15 +400,8 @@ export default function ReviewPage({ batchId: rawBatchId, projectId, onProject }
|
|||||||
const failures = []
|
const failures = []
|
||||||
for (let i = 0; i < ahead.length; i++) {
|
for (let i = 0; i < ahead.length; i++) {
|
||||||
const targetFrame = ahead[i]
|
const targetFrame = ahead[i]
|
||||||
// Every frame is seeded from the drawn box, never from a previous
|
|
||||||
// result, so a bad read on one frame cannot carry into the next.
|
|
||||||
// Frames 2-5 get it grown about its centre — the object has had
|
|
||||||
// time to move by then.
|
|
||||||
const seedBox = i === 0 ? firstBox : growBox(firstBox, SEED_GROWTH)
|
|
||||||
try {
|
try {
|
||||||
await api.assist(targetFrame.id, {
|
await api.assist(targetFrame.id, { box, class_id: current.class_id })
|
||||||
box: seedBox, class_id: current.class_id,
|
|
||||||
})
|
|
||||||
tracked++
|
tracked++
|
||||||
patchFrameLocally(targetFrame.id, { annotation_count: (targetFrame.annotation_count ?? 0) + 1 })
|
patchFrameLocally(targetFrame.id, { annotation_count: (targetFrame.annotation_count ?? 0) + 1 })
|
||||||
} catch (exc) {
|
} catch (exc) {
|
||||||
|
|||||||
Reference in new issue
Block a user