Skip to content

Commit 4e5e2e2

Browse files
committed
docs: native files offer no group or pixel-sized metrics; hash unchanged; flip and recognise notes
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QXpnhJcJvaj5BxqfESMHNp
1 parent 7282d6a commit 4e5e2e2

7 files changed

Lines changed: 41 additions & 30 deletions

File tree

‎CHANGELOG.md‎

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/).
99

1010
### Added
1111

12-
- **Native 3-D files: Anipose and DeepLabCut 3-D.** Two new readers (`anipose` for `pose-3d` CSV, `deeplabcut_3d` for the triangulated tables; both built from the published layouts and tested on synthetic files only, so labelled *synthetic only*) give sessions with an absolute z in the project's length unit. `fps` and `unit` (mm, cm, m, in) are required options and never guessed (Anipose proposes `mm`, DeepLabCut 3-D proposes nothing); `target_unit` (default `cm`), `keypoint`, `likelihood_cutoff` (Anipose only), `vertical_axis` (`x`, `y`, `z`; default `z`), `flip` and `keep_skeleton` are optional. Units are converted when the file is read. IL-16, IL-17 and GL-16 run on them with no water column; the 2-D metrics measure on the horizontal plane. **IL-15** runs once you give the **water column** (Surface z and Floor z on the Calibration screen); positions outside it have no depth and are counted in `frac_outside_extent`, never clipped. A native 3-D project is a 2-D project (leave Analysis type on 2D) with no Views or Fusion step; adding the first file sets Session calibration, the unit label and its confirmation, and later files must use the same target unit (`NATIVE_3D_UNIT_MISMATCH`); native and pixel sessions cannot share a project (`NATIVE_3D_MIXED`); other calibration modes are blocked (`NATIVE_3D_NEEDS_SESSION_CALIBRATION`). Limits: one animal per file (so GL-16, which needs two animals in a session, is empty), no z column in the per-frame table, no zones or background image, no refraction correction, a multi-animal DeepLabCut 3-D table is not detected and the scan gives no notice for it (the old "DeepLabCut 3-D, not readable" scan notice was removed because these files are now read). The run `README.md` gains a **Native 3-D source** section. See the user guide and decision D-043.
13-
- **Note on provenance hashes:** `project_hash` now includes the calibration's (empty) `water_column` field, so the provenance hash of an existing project changes once after this update. Results are not affected.
12+
- **Native 3-D files: Anipose and DeepLabCut 3-D.** Two new readers (`anipose` for `pose-3d` CSV, `deeplabcut_3d` for the triangulated tables; both built from the published layouts and tested on synthetic files only, so labelled *synthetic only*) give sessions with an absolute z in the project's length unit. `fps` and `unit` (mm, cm, m, in) are required options and never guessed (Anipose proposes `mm`, DeepLabCut 3-D proposes nothing); `target_unit` (default `cm`), `keypoint`, `likelihood_cutoff` (Anipose only), `vertical_axis` (`x`, `y`, `z`; default `z`), `flip` and `keep_skeleton` are optional. Units are converted when the file is read. IL-16 and IL-17 run on them with no water column; the 2-D metrics measure on the horizontal plane. **IL-15** runs once you give the **water column** (Surface z and Floor z on the Calibration screen); positions outside it have no depth and are counted in `frac_outside_extent`, never clipped. A native 3-D project is a 2-D project (leave Analysis type on 2D) with no Views or Fusion step; adding the first file sets Session calibration, the unit label and its confirmation, and later files must use the same target unit (`NATIVE_3D_UNIT_MISMATCH`); native and pixel sessions cannot share a project (`NATIVE_3D_MIXED`); other calibration modes are blocked (`NATIVE_3D_NEEDS_SESSION_CALIBRATION`). Not offered for native files (greyed out with a reason): every group metric, GL-16 included (a file holds one animal), and IL-3, IL-9, IL-10 and IL-14 (pixel-sized defaults that would change with the project unit). The water column is entered on the file's own vertical axis; each session's flip is applied for you. Limits: one animal per file, no z column in the per-frame table, no zones or background image, no refraction correction, a multi-animal DeepLabCut 3-D table (or one without full x, y, z triples) is still named by the scan as one it cannot read yet. The run `README.md` gains a **Native 3-D source** section. See the user guide and decision D-043.
1413
- **3-D metrics (fused 3-D projects).** Three new metrics next to the 2-D ones, which are unchanged: **IL-16 3-D Distance Travelled** (`path_length_3d_cm`, `path_length_3d_bl`, `n_valid_steps`), **IL-17 3-D Speed** (`mean_speed_3d_cm_s`, `median_speed_3d_cm_s`, `max_speed_3d_cm_s`, `mean_speed_3d_bl_s`) and **GL-16 3-D Nearest-Neighbour Distance** (`mean_nnd_3d_cm`, `median_nnd_3d_cm`, `mean_nnd_3d_bl`, `n_skipped_frames_3d`). Positions are in cm: x and y from the top view divided by its scale, depth fraction times the tank height from the fusion settings. IL-16 and IL-17 need identities, GL-16 does not; all three compute per time bin like their 2-D counterparts. They need the top view's cm scale (scalar or session calibration): in a 2-D project they are greyed out ("needs a fused 3-D session"), under body-length calibration they are greyed out ("needs a cm scale for the top view (use scalar or session calibration)"), and a unit whose top view has no scale is skipped at run time ("needs a cm scale for the top view"), with the reason in the run README. Track2Data does not estimate the scale from the fusion. IL-17 uses the project's kinematics estimator for the vertical speed, and its horizontal part is IL-2's speed. A step, frame or speed needs a valid x, y and depth, otherwise the value is NaN, never 0. Not included: vertical speed as its own metric, 3-D inter-individual distance and cohesion. The Metrics screen presets (for example *All metrics*) no longer tick any row that is greyed out for the project (also, for instance, identity-needing metrics when every session is identity-free). `codebook.csv` lists the whole registry, so it gains rows for the three metrics in 2-D runs too; no other 2-D output changes. `track2data list-metrics` shows "fused 3-D + cm scale" for them.
1514
- **Run a 3-D project.** A 3-D project now runs: every top/side pair with fusion settings that fuses without error becomes one run unit (id `<top>+<side>`, output folder `<run>/<top>+<side>/`), and each unit goes through the usual pipeline as a fused session. Sessions in no fusable pair, and pairs that fail to fuse, are left out with their reason: the Processing screen lists them, the run writes them to `skipped.csv` and to a "Skipped sessions" section of `PROJECT_SUMMARY.md` (both only when something was skipped), and `track2data run` prints them. A 3-D project with no fusable pair refuses to run ("no pair is ready to fuse: ..." or "Pair and fuse a top and a side session first"); one bad pair among good ones does not stop the run. Vertical position (IL-15) reads the fused depth, with `depth_extent_source` = `fusion`, and is available for a fused session whatever the project's camera view is; `frac_outside_extent` counts the side positions that fell outside the water column. Where the fused frames jump (a frame one of the views lacks), the fused track keeps a break, as a 2-D session tracked in separate intervals does, so path length and every other metric that steps from frame to frame never bridges the gap. A unit carries the TOP session's metadata (session-level columns such as `group_id`, `treatment` or `sex`), and per-animal metadata matches the fused fish by the top session's label or index. The per-frame tables (CSV long, Feather, Excel) gain `depth_fraction` and `depth_cm` for fused sessions; `sessions.csv`, each unit's README ("3-D fusion") and `manifest.json` (`run_metadata.fusion`) record how the pair was fused, including the SHA-256 of the side trajectory file. `PROJECT_SUMMARY.md` counts "Units processed (fused pairs)", and the pooled `all_sessions/manifest.json` records each unit's fusion. `track2data validate` and Validate check only the sessions that run: the consistency report reads the top session of each pair with fusion settings, and session calibration checks both sessions of such pairs. Serial and parallel runs give identical files. Processing, Preview and Export are blocked only while no pair has fusion settings; Processing shows what will run and what is skipped, and Preview shows the top-view tracks of a fused unit. `track2data validate` lists unfusable pairs as notes. 2-D projects are unchanged. Still missing: 3-D speed, path length and neighbour distance (sub-project B2); zones are one set applied to the top view; `sensitivity` refuses 3-D projects. New `Engine.run_units`, `RunPlan`, `RunUnit` and `Engine.validation_issues`.
1615
- **Fusion of top and side views (3-D projects).** The Views page gains a **Fusion** section for the selected pair with a status line and **Set up fusion…**. The dialog shows the side view with two draggable lines for the water surface and the tank floor, fields for both rows, the tank height (default 20 cm), the shared horizontal axis (top-view x or y) with Flip, and the frame offset (hidden and fixed at 0 for "One video, two panels"), a live summary (shared frames, fused fish, positions outside the water column, agreement) and **Suggest offset**, which scans ±5 s around 0 (about 3 s on a 1-hour, 5-fish recording; needs a calibrated top view, otherwise it is disabled with a hint). The fusion keeps the fish matched in the pair on the frames both views have and adds a depth (0 = surface, 1 = floor; positions outside the column become empty and are counted). The cameras are assumed to be at right angles; the two views' horizontal positions are compared in cm and a warning shows when they disagree by more than 10% of the top-view range (needs a calibrated top view). Frame rates must match. New `FusionSettings` on each pair (`ViewPair.fusion`), `PreprocessedSession.depth`, `Engine.fuse_pair`, `fuse_all` and `suggest_offset`. The Views status warns "Fusion setup needed for …" and never blocks. A fused pair is what a 3-D run is made of (see "Run a 3-D project" above).

‎README.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ It is a desktop application and a Python engine you can drive headlessly for bat
3434
**Reads** idtracker.ai (6.x and v5), DeepLabCut (CSV, also Lightning Pose), SLEAP
3535
(analysis HDF5), Ctrax (raw `.mat`), `trx.mat`, and two native 3-D formats, Anipose
3636
`pose-3d` CSV and DeepLabCut 3-D tables (positions in physical units; the 3-D metrics
37-
IL-16, IL-17 and GL-16 run on them, and IL-15 once you give the water column). Run
37+
the 3-D metrics IL-16 and IL-17 run on them, and IL-15 once you give the water column; GL-16 does not, because a native file holds one animal). Run
3838
`track2data list-readers` for the list and the options each reader needs.
3939

4040
**Individual** — distance travelled, speed, acceleration, tortuosity,

‎docs/3d-movement/2026-10-10-native-3d-files-design.md‎

Lines changed: 15 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -139,14 +139,21 @@ code does.
139139
- **IL-17 reads `z`.** The first build left IL-17 on `depth * height`, so a native session without the
140140
water column got NaN; found by the end-to-end test and fixed. IL-15 outside values, and non-finite z,
141141
are NaN and counted.
142-
- **GL-16 is empty for native files** (one animal per file; the metric needs two in a session), with every
143-
frame counted in `n_skipped_frames_3d`.
144-
- **The unreadable-format notice for DLC-3D was removed** from `readers/recognise.py`: the reader now
145-
reads those files. A multi-animal DLC-3D table (extra `individuals` header row) is not detected and
146-
gets no notice.
147-
- **Provenance and outputs.** `project_hash` changes once for every project (the manifest gained
148-
`calibration.water_column`); `manifest.json` carries empty `water_column` and `native_3d` keys for pixel
149-
projects. Nothing else in a pixel project's output changed (a golden README test pins it).
142+
- **GL-16 is not offered for native files** (one animal per file; the metric needs two in a session).
143+
- **The unreadable-format notice for DLC-3D was narrowed** in `readers/recognise.py`: the single-animal
144+
tables the reader takes are no longer reported, while a multi-animal table (extra `individuals` header
145+
row) or one without full x, y, z triples is still named as one that cannot be read yet.
146+
- **Metrics not offered in a native project** (one reason each, through `availability.unavailable_reason`):
147+
IL-3, IL-9, IL-10 and IL-14 (pixel-sized defaults would silently change with `target_unit`; the
148+
data-extent arena fallback is kept but not reachable in a run) and every group metric, GL-16 included
149+
(one animal per file).
150+
- **The water column is on the file's axis.** For a session read with `flip`, `apply_water_column` negates
151+
both values, so the same physical fish gets the same depth with or without flip.
152+
- **Provenance and outputs.** `calibration.water_column` is left out of the serialised manifest while it
153+
is None (and so out of `project_hash`, the cache key and `manifest.json`), and the provenance
154+
`native_3d` key only appears for a native session: a pixel project hashes, caches and serialises exactly
155+
as before (golden values from fc5b2fc). IL-15's `depth_extent_source` is `water_column` for native
156+
sessions. A replaced jump also blanks z.
150157
- **UI.** Zones and background are disabled for native projects; the trajectories preview frames the data
151158
from its extent; the Calibration screen shows the **WATER COLUMN** group (Surface z, Floor z) for
152159
native projects only; the GUI add refuses a batch that would break the rules before listing it.

‎docs/dev/DECISIONS.md‎

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1110,19 +1110,23 @@ checks. *No mixing:* `NATIVE_3D_MIXED` (native with pixel sessions, or native in
11101110
and `NATIVE_3D_UNIT_MISMATCH` (two target units); `fuse()` refuses native sessions. *z.*
11111111
`Session.raw_z` and `PreprocessedSession.z`, row-aligned with `xy`, not smoothed, interpolated
11121112
or permuted by identity fixes; `has_pixel_frame False`, `coordinate_unit`, `video.width_px = height_px = 0`
1113-
(zones and background disabled, the IL-3 and IL-14 arena from the data extent with a warning).
1113+
(zones and background disabled; the data-extent arena fallback of IL-3 and IL-14 is kept but not reachable, because those metrics are not offered).
11141114
`geometry3d.positions_cm` and IL-17 take Z from `z` when present, otherwise `depth * depth_height_cm`.
11151115
*IL-15* needs the water column (`CalibrationConfig.water_column`: `surface_z`, `floor_z`, finite, different,
11161116
either order): `depth = (z - surface_z) / (floor_z - surface_z)`, `depth_height_cm = |floor_z - surface_z|`,
11171117
values outside [0, 1] and non-finite z become NaN and are counted, never clipped; without it the
11181118
reason is "needs the water column (surface and floor z) on the Calibration page". *Outputs.* z is not
11191119
exported in the per-frame table (only through the metrics); with a water column the table carries the
1120-
depth columns; the run README has a "Native 3-D source" section. Pixel-frame outputs are unchanged,
1121-
except that `manifest.json` carries the new empty `water_column` and `native_3d` keys and `project_hash`
1122-
changes once (the new field is part of the manifest). *Recognition.* The `dlc_3d` entry of
1123-
`readers/recognise.py` ("unreadable format") was removed because the reader now reads those files;
1124-
a multi-animal DeepLabCut 3-D table (an extra `individuals` header row) is not detected by this reader
1125-
and so gets no notice.
1120+
depth columns; the run README has a "Native 3-D source" section. Pixel-frame outputs are unchanged
1121+
byte for byte: `water_column` is left out of the serialised manifest while None, so `project_hash`, the
1122+
cache key and `manifest.json` do not change, and the provenance `native_3d` key exists only for a native
1123+
session (golden values computed from fc5b2fc). IL-15's `depth_extent_source` is `water_column` there.
1124+
*The water column* is entered on the file's own axis; `apply_water_column` negates both values for a
1125+
session read with `flip`. *Not offered in a native project* (`availability.unavailable_reason`): IL-3,
1126+
IL-9, IL-10, IL-14 (pixel-sized defaults would silently change with `target_unit`) and every group metric
1127+
(one animal per file). *Recognition.* The `dlc_3d` entry of `readers/recognise.py` was narrowed: it names a
1128+
multi-animal DeepLabCut 3-D table (an extra `individuals` header row) or one without full x, y, z triples,
1129+
and never a table the reader takes.
11261130

11271131
**Rationale:** A wrong unit silently scales every speed, so the unit is a required option and the
11281132
calibration is set once, by the add, and only checked afterwards; a run that rewrote the project
@@ -1131,7 +1135,7 @@ unit in the whole pipeline and lets the existing "session" calibration (`length_
11311135
carry it with no new path. Keeping z absolute (not a fraction) means the 3-D metrics need no tank
11321136
height; the water column is an explicit entry because estimating the surface from the data would hide
11331137
an error. Missing-as-all-zero keeps a real zero coordinate that a per-axis rule would erase.
1134-
*Limits.* One animal per file (so GL-16 is empty: it needs two animals in a session); no mixing with
1138+
*Limits.* One animal per file (so no group metric, GL-16 included, is offered); no mixing with
11351139
pixel sessions; the board unit must be confirmed by the user; no refraction correction; the water
11361140
column is entered, not estimated; no z column in the per-frame table; formats are read from synthetic
11371141
files only.

0 commit comments

Comments
 (0)