Skip to content

Commit c2b9bb8

Browse files
authored
Merge pull request #144 from mbel-tech/fatina-del-codice/beautiful-hypatia-wlxfol
Read native 3-D tracker files (Anipose pose-3d, DLC-3D) — sub-project A
2 parents 7052e9b + 4e5e2e2 commit c2b9bb8

64 files changed

Lines changed: 5265 additions & 113 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

‎CHANGELOG.md‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +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 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.
1213
- **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.
1314
- **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`.
1415
- **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: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,12 @@ It is a desktop application and a Python engine you can drive headlessly for bat
3131
→ export sessions.csv
3232
```
3333

34+
**Reads** idtracker.ai (6.x and v5), DeepLabCut (CSV, also Lightning Pose), SLEAP
35+
(analysis HDF5), Ctrax (raw `.mat`), `trx.mat`, and two native 3-D formats, Anipose
36+
`pose-3d` CSV and DeepLabCut 3-D tables (positions in physical units; the 3-D metrics
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
38+
`track2data list-readers` for the list and the options each reader needs.
39+
3440
**Individual** — distance travelled, speed, acceleration, tortuosity,
3541
freezing bouts, thigmotaxis, turn rate, home-base use.
3642
**Group** — nearest-neighbour and inter-individual distance, polarisation,

‎docs/3d-movement/2026-10-08-3d-roadmap.md‎

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# 3-D movement: roadmap
22

3-
**Status:** approved 2026-10-08. Sub-project C is in progress; E (mode switch), G, F, D (fusion) B1 (running a 3-D project) and B2 (3-D metrics) are implemented; A is not started.
3+
**Status:** approved 2026-10-08. Sub-project C is in progress; E (mode switch), G, F, D (fusion) B1 (running a 3-D project) and B2 (3-D metrics) and A (native 3-D files) are implemented.
44
**Relates to:** `docs/dev/DECISIONS.md` D-031, `docs/dev/ROADMAP.md` (Z-10),
55
`docs/tracker-formats/2026-10-07-tracker-import-design.md` (Tier 3)
66

@@ -13,20 +13,20 @@ build cycle.
1313
| **E** | **2D/3D project mode**: `ProjectManifest.mode`, Project-screen choice, lock, 3-D compute blocked. Done; see [the spec](2026-10-09-mode-switch-design.md) | nothing |
1414
| F | **Panel split**: one video with the top and side views in two panels becomes two sessions (a panel rectangle per session, panel-relative coordinates). Done; see [the spec](2026-10-09-panel-split-design.md) | E |
1515
| G | **ID correspondence**: view roles, pairing top and side sessions (regex or by hand), fish mapping between views, a Views step. Done; see [the spec](2026-10-09-id-correspondence-design.md) | E |
16-
| A | Read 3-D tracker files (Anipose pose-3d CSV, DLC-3D). Metrics run on a chosen plane and z is stored (D-031 as written) | `feat/native-units` merged |
16+
| A | Read 3-D tracker files (Anipose pose-3d CSV, DLC-3D). Metrics run on the horizontal plane and z is stored in the project unit. Done; see [the spec](2026-10-10-native-3d-files-design.md). **Amends D-031 a second time** | `feat/native-units` merged |
1717
| B1 | **Run a 3-D project**: run units per fused pair, IL-15 from the fused depth, depth in the exports, UI and CLI. Done; see [the spec](2026-10-10-3d-run-design.md) | D |
1818
| B2 | Metrics that use z: 3-D speed and path length, 3-D neighbour distance. Done; see [the spec](2026-10-10-3d-metrics-design.md). **Amends D-031** | B1 |
1919
| D | Fuse top and side recordings into one 3-D session. Done; see [the spec](2026-10-09-fusion-design.md) | F and G (done) |
2020

21-
**Order: C, F/G/E, D, B1 and B2 are done; A remains.** A moves first if real 3-D files turn up. C went
21+
**Order: all sub-projects are done.** C went
2222
first because no data of any kind existed, it is testable on synthetic tracks, and it created the
2323
project-level camera-view setting that B reuses.
2424

2525
## Hand-offs between the sub-projects
2626

2727
- `KeypointSelection.plane` and `camera_view` have no defined precedence. A and B must define
2828
a vertical axis for 3-D data.
29-
- The `has_pixel_frame` guard from native-units joins the IL-15 extent derivation when A lands.
29+
- The `has_pixel_frame` guard from native-units joined the IL-3 and IL-14 arena fallback in A (the data extent is used, with a warning).
3030
- Per-session camera views arrive with D (`Engine.camera_view_for(session)` is the seam).
3131
- Whether IL-3 and IL-14, which assume a top-down view, should be gated for side views is
3232
left open.

0 commit comments

Comments
 (0)