Skip to content

feat(gt): per-entity activity-state ground-truth channel (opt-in sidecar) - #95

Merged
bamdadd merged 2 commits into
bamdadd:mainfrom
Nitjsefnie-OSC:feat/65-activity-state-gt-channel
Jul 31, 2026
Merged

feat(gt): per-entity activity-state ground-truth channel (opt-in sidecar)#95
bamdadd merged 2 commits into
bamdadd:mainfrom
Nitjsefnie-OSC:feat/65-activity-state-gt-channel

Conversation

@Nitjsefnie

Copy link
Copy Markdown
Contributor

Summary

Adds a per-entity, per-frame activity-state ground-truth channel (standing / crouching / reaching) so an activity-recognition head has a target to score against. Opt-in and additive — the byte-golden manifests are unchanged.

Related Issues and Pull Requests

Closes #65

Changes

  • src/multicam_sim/activity.py (new): ActivityState(StrEnum), ActivitySegment (half-open [start, end)), ActivityTimeline with state_at_frame, and write_activity_json.
  • src/multicam_sim/scene.py: optional additive Scene.activity field.
  • src/multicam_sim/dsl/builder.py: SceneBuilder.activity(...) opt-in hook; touches neither motion nor the manifest.
  • src/multicam_sim/__init__.py: exports.
  • tests/test_activity.py: 10 tests.

It deliberately mirrors possession.py / order.py one-to-one rather than introducing a second idiom — same validators, same overlap rejection, same (entity_id, start_frame) sort key, same to_json / write_*_json shape, and state_at_frame mirrors holder_at_frame including its early break. Per #29's note, this is only the labeled GT channel; it generates no motion and does not depend on #29.

Testing

uv run ruff check ., uv run ruff format --check ., uv run mypy src all clean; plain uv run pytest (not -q, which addopts would turn into -qq and silence) → 294 passed, 4 skipped, against a 284 passed, 4 skipped baseline on main under the same plain uv sync. The +10 is exactly the new test module.

Byte-golden verified by hashing rather than asserted: the smoke, mtmc and assembly manifests built at main and on this branch without opting in are byte-identical (same sha256 and same byte length on all three), and a test pins manifest equality with and without opt-in. build_manifest reads only topology/cameras/entities/occluders/fps/num_frames, so neither activity nor possession is on the manifest path at all.

Boundary behaviour is tested at the exact half-open edges (frames 3/4, 6/7, 9/10 handovers), not at interval midpoints, since an off-by-one at an edge is the obvious failure mode here.

Follow-ups / Known Limitations

One precision point on "open to more without a schema fork", which is worth your call. Adding a state is genuinely additive producer-side — a new enum member serialises as a plain string, and existing files with known states keep deserialising fine. But a consumer using the shipped typed model does not accept a state it has never heard of: ActivityTimeline.model_validate_json(...) with "state": "walking" raises ValidationError. So forward-compatibility holds for raw-JSON consumers, not for typed ones.

I have matched the merged OrderStatus precedent exactly here, which has the identical property by construction, rather than making this one module lenient and creating a second idiom for the same concept. If you would rather the typed models accept unknown states, that is a repo-wide decision affecting OrderStatus too and I am happy to do it as a separate PR — it did not feel like mine to make inside this one.

Generated by Claude Opus 5 (brief, review), Kimi K3 (implementation)

Nitjsefnie and others added 2 commits July 31, 2026 08:59
Add multicam_sim.activity: a typed ActivityState StrEnum (standing /
crouching / reaching) labeling an entity over half-open frame intervals,
an ActivityTimeline with per-entity per-frame state_at_frame queries, and
write_activity_json — following the order.py / possession.py sidecar
precedent. Attached to Scene via an optional additive field, so the
byte-golden manifest is unchanged when the channel is absent.

Co-Authored-By: Kimi K3 <[email protected]>
SceneBuilder.activity(entity_id, state, start, end) records an
ActivitySegment into the scene's activity sidecar without touching motion
or geometry. Tests cover enum string serialisation, segment/timeline
validation, half-open per-frame queries, builder validation, sidecar
round-trips, and byte-identical manifests with/without opt-in.

Co-Authored-By: Kimi K3 <[email protected]>
@bamdadd
bamdadd merged commit d04d04d into bamdadd:main Jul 31, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Per-entity posture / activity-state channel as labeled ground truth for activity recognition

2 participants