Repository navigation
feat(v1): shoulder-midpoint anchor by default; one normalization scheme per study - #8
Merged
Merged
Conversation
Normalization gets two named schemes: shoulder_midpoint (default; hips had visibility 0.005 vs 0.999 for shoulders in run 1) and the spec-faithful hip_midpoint. The scheme is chosen once per study, recorded in every rollout and in study_settings.json, and never switched per frame. GapAnalyzer.fit refuses mixed schemes and compute_gap refuses source/target schemes that differ from each other or from training; the training scheme is saved in checkpoints. Spec 5.2 documents the deviation. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…and-scheme-guard # Conflicts: # CHANGELOG.md
Prominent [Unreleased] and README note: 0.2.0 loaders returned raw coordinates, main normalizes with a shoulder anchor, so the same video yields different states. Guard errors now say how to fix it: re-extract with one scheme, or normalize_rollout() saved unnormalized rollouts, which is exact (per-frame transform; verified on run 1's data, max difference 0). Run 1's stores documented as unnormalized. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
3 of 4 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Follow-ups 2 and 3 after run 1: a shoulder-midpoint pose anchor by default, and a guard that keeps one normalization scheme per study.
Two named schemes (
worldgap.data.normalization.SCHEMES):shoulder_midpoint(default): pose anchored at landmarks 11/12.hip_midpoint: the spec 5.2 anchor (23/24), kept for framings where the hips are visible.Both scale by shoulder width; hands are unchanged (wrist + bounding-box diagonal).
Why shoulders: in run 1's clean recordings MediaPipe's mean visibility was 0.005 for the hips vs 0.999 for the shoulders, so a hip anchor is an extrapolation. Spec 5.2 now records this as a deliberate, documented deviation, with that measurement as the justification.
The scheme is fixed per study, never per frame:
metadata["normalization"]["scheme"]. Nothing switches anchors on visibility;test_anchor_never_switches_on_visibilitypins that.normalization_scheme=.run_v1_real_data.pytakes--normalizationand writes it to a newstudy_settings.jsonnext to the pre-registration, before any score.The guard (same spirit as the temporal-provenance guard):
GapAnalyzer.fit()refuses training rollouts with mixed schemes.compute_gap()refuses source/target rollouts whose schemes differ from each other or from the training scheme. Normalized vs raw counts as different. A gap across anchors includes the anchor change itself.The analyzer gains no modality branch; the check reads metadata only.
Tests:
tests/test_normalization_scheme_guard.py(7). The invariance and reference-point tests intest_normalization.pynow run under both schemes. The smoke test checksstudy_settings.json.compute_gapguard removed, 4 of the 7 guard tests fail. The other three cover the same-scheme path,fit's own guard and unnormalized sets, and pass as expected.Note: re-scoring run 1's recordings under either scheme is a follow-up analysis; its numbers are not comparable with run 1's pre-registered result. This is stated in spec 5.2.
Verification
pytest -q: 155 passedruff check .: cleanscripts/run_v2_actuation.pyre-run (analyzer changed): every value identicalAdded after review
main; the CHANGELOG keeps both sides).[Unreleased]and in the README: 0.2.0's loaders return unnormalized coordinates,mainnormalizes by default with a shoulder anchor, so the same video yields different states. 0.2.0 stores, checkpoints and scores are not comparable, and the guard refuses to mix them.normalize_rollout(r, scheme)to rollouts saved unnormalized. The second is exact, because normalization is per frame. Verified on run 1's real data: normalizing the savedclean/take0store vs re-extracting the video gave a maximum state difference of 0.0 across all 46 windows. A unit test pins that equivalence, and another asserts both refusals name the fix (9 guard tests).docs/v1_first_run_results.mdand spec 5.2 note that run 1's stores are unnormalized, so a re-score must normalize first, and that a re-score is a follow-up analysis not comparable with ρ = −0.224.pytest -q: 157 passed ·ruff check .clean.🤖 Generated with Claude Code