Skip to content

Add COCO keypoint and BVH motion capture I/O support#930

Open
iftiquar wants to merge 8 commits intoneuroinformatics-unit:mainfrom
iftiquar:feature/human-motion-io
Open

Add COCO keypoint and BVH motion capture I/O support#930
iftiquar wants to merge 8 commits intoneuroinformatics-unit:mainfrom
iftiquar:feature/human-motion-io

Conversation

@iftiquar
Copy link
Copy Markdown

@iftiquar iftiquar commented Mar 26, 2026

Description

What is this PR

  • Bug fix
  • Addition of a new feature
  • Other

Why is this PR needed?

Closes #182. Relates to #175, #299, #581.

The movement package currently supports pose estimation formats used primarily in animal behaviour research (DeepLabCut, SLEAP, LightningPose, Anipose). However, the project's roadmap calls for expanding I/O support to human motion tracking formats. Two of the most widely used are:

This PR adds loaders, validators, tests, and gallery examples for both formats — with zero new dependencies.

What does this PR do?

  • Adds from_coco_file() loader that parses COCO keypoint annotation JSON files into movement poses datasets (movement/io/load_poses.py).
  • Adds ValidCOCOJSON file validator with JSON schema validation and a custom check for keypoint array lengths (movement/validators/files.py).
  • Adds COCO_KEYPOINTS_SCHEMA JSON schema (movement/validators/_json_schemas.py).
  • Adds from_bvh_file() loader that parses BVH skeleton hierarchy and computes 3D joint positions via forward kinematics (movement/io/load_poses.py).
  • Adds ValidBVHFile file validator that checks for HIERARCHY and MOTION sections (movement/validators/files.py).
  • Updates the SourceSoftware type alias to include "COCO" and "BVH" (movement/io/load.py).
  • Adds 30+ unit tests covering valid loading, edge cases, and validator error paths (tests/test_unit/test_io/test_load_coco_and_bvh.py).
  • Adds two sphinx-gallery examples: load_coco_data.py and load_bvh_data.py (examples/).

COCO loader details

The COCO loader (from_coco_file()) handles the following:

  • Each image in the JSON is treated as one time frame (sorted by id to establish temporal order).

  • Each annotation per image is treated as a separate individual.

  • If annotations include a track_id field, it is used for consistent individual identity across frames; otherwise, individuals are numbered per frame (id_0, id_1, …).

  • Visibility flag mapping to confidence scores:

    COCO v Position Confidence
    0 (not labelled) NaN 0.0
    1 (labelled, occluded) actual x, y 0.5 × score
    2 (labelled, visible) actual x, y 1.0 × score
  • If the annotation has no score field, a default of 1.0 is used.

BVH loader details

The BVH loader (from_bvh_file()) handles the following:

  • Parses the HIERARCHY section to build a skeleton tree (joint names, offsets, channel types, parent–child relationships).
  • Parses the MOTION section for per-frame channel values.
  • Computes absolute 3D joint positions via forward kinematics: for each frame, Euler angles are converted to rotation matrices and composed along the parent chain.
  • Handles arbitrary rotation orders (ZXY, XYZ, YZX, etc.) as specified in each joint's CHANNELS line.
  • Derives fps automatically from the BVH Frame Time field (overridable via the fps parameter).
  • End Site nodes are excluded from the output; only ROOT and JOINT nodes become keypoints.
  • Produces a single-individual 3D dataset (BVH files typically describe one actor).

Design decisions

Following existing patterns. Both loaders use the @register_loader decorator, from_numpy() for dataset construction, and cast() for type narrowing — identical to the existing DeepLabCut, SLEAP, and VIA-tracks loaders.

Validator conventions. ValidCOCOJSON follows the ValidROICollectionGeoJSON pattern: schema validation via _json_validator() with a standalone custom_checks callable (_check_coco_keypoint_lengths). ValidBVHFile follows the ValidAniposeCSV pattern: basic file validation via _file_validator() plus a @file.validator method for format-specific structure checks.

No new dependencies. COCO parsing uses only stdlib json (already imported by _json_validator). BVH parsing and forward kinematics use only numpy (already a core dependency). This keeps the installation lightweight.

BVH forward kinematics from scratch. Rather than adding a dependency on a motion capture library, the FK implementation uses standard Euler-angle-to-rotation-matrix conversion. The code is factored into small, tested helpers (_axis_rotation_matrix, _euler_to_rotation_matrix, _extract_bvh_channels, _bvh_forward_kinematics).

Minimal COCO schema. The JSON schema validates structural requirements (images, annotations, categories with their core fields) but does not enforce optional COCO fields like bbox, area, or iscrowd. This allows the loader to work with both full COCO annotation files and lightweight keypoint-only exports from tools like MMPose.

How has this PR been tested?

  • Unit tests (tests/test_unit/test_io/test_load_coco_and_bvh.py): 30+ tests organised into four classes:
    • TestCOCOLoader: valid loading, shape, keypoint names, track_id, invisible keypoints, missing score, fps handling, source attributes
    • TestCOCOValidation: valid file, missing keys, wrong keypoint lengths, wrong extension
    • TestBVHLoader: valid loading, shape, joint names, 3D space, fps from Frame Time, fps override, root positions at frame 0 and frame 1, source attributes, NaN confidence, single individual
    • TestBVHValidation: valid file, missing HIERARCHY, missing MOTION, wrong extension
    • TestLoadDatasetIntegration: both formats via load_dataset() unified interface
  • Ruff linting passes with zero errors.
  • Tests use shared fixtures from conftest.py (helpers, wrong_extension_file).

Is this a breaking change?

No.

Does this PR require an update to the documentation?

  • Numpydoc docstrings for all public functions, validators, and helpers.
  • See Also cross-references between loaders and validators.
  • Two gallery examples demonstrating the new functionality.
  • API reference pages will be auto-generated by docs/make_api.py (no manual changes needed).

Checklist

  • The code has been tested locally
  • Tests have been added to cover all new functionality
  • The documentation has been updated to reflect any changes
  • The code has been formatted with pre-commit

iftiquar and others added 2 commits March 26, 2026 22:17
Add loaders for two popular human motion tracking formats:

COCO Keypoint Format:
- Add ValidCOCOJSON validator with JSON schema validation
- Add from_coco_file() loader registered as 'COCO' source software
- Parse COCO keypoint annotations with visibility-to-confidence mapping
- Support track_id for consistent individual tracking across frames
- Handle invisible keypoints (v=0) as NaN positions

BVH (Biovision Hierarchy) Format:
- Add ValidBVHFile validator for BVH structure validation
- Add from_bvh_file() loader registered as 'BVH' source software
- Parse skeleton hierarchy and motion data
- Compute 3D joint positions via forward kinematics
- Derive fps from Frame Time field when not provided

Also includes:
- COCO keypoints JSON schema in _json_schemas.py
- Comprehensive test suite for both loaders and validators
- Gallery examples demonstrating COCO and BVH data loading

Closes neuroinformatics-unit#182, relates to neuroinformatics-unit#175, neuroinformatics-unit#299, neuroinformatics-unit#581
iftiquar and others added 2 commits March 26, 2026 22:49
… in examples

- Replace tempfile.mktemp() with tempfile.NamedTemporaryFile(delete=False) in both gallery examples to resolve SonarCloud high-severity security alert
- Add required COCO schema fields (width, height, num_keypoints, bbox, area, iscrowd, skeleton) to the example data so it passes ValidCOCOJSON validation
@sonarqubecloud
Copy link
Copy Markdown

Quality Gate Failed Quality Gate failed

Failed conditions
C Reliability Rating on New Code (required ≥ A)

See analysis details on SonarQube Cloud

Catch issues before they fail your Quality Gate with our IDE extension SonarQube for IDE

iftiquar and others added 2 commits March 26, 2026 22:55
… comparison

- Refactor _ds_from_coco_data into smaller helpers (_coco_individual_mapping,
  _coco_fill_arrays, _coco_fill_keypoints) to reduce cognitive complexity
  from 22 to well under 15
- Replace direct float equality (== 0.0) with pytest.approx in test
@sonarqubecloud
Copy link
Copy Markdown

iftiquar and others added 2 commits March 26, 2026 23:16
- ValidCOCOJSON: extract _check_coco_keypoint_lengths as a standalone
  custom check passed to _json_validator(custom_checks=...) instead of
  using __attrs_post_init__, matching the ValidROICollectionGeoJSON pattern
- ValidBVHFile: replace __attrs_post_init__ with @file.validator decorator
  pattern, matching ValidAniposeCSV/ValidVIATracksCSV conventions
- Add See Also cross-references to both validator docstrings
- Add attribute-level docstrings following existing style
@sonarqubecloud
Copy link
Copy Markdown

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.

Add support for COCO formats

2 participants