Native ndx-pose interoperability
ndx-pose is the NWB extension used by tools including DeepLabCut, SLEAP,
Keypoint-MoSeq, NeuroConv, and movement to exchange pose-estimation data. The
FiberPhotometry adapter reads and writes the extension directly while keeping pose
discovery in those upstream tools.
Install the NWB extra:
The current contract is tested against ndx-pose 0.3.0 and PyNWB. It supports
explicit timestamps and regular starting_time/rate, 2D and 3D coordinates,
confidence, coordinate units, conversion/offset, reference frames, skeletons,
source software, scorer metadata, and original/labeled video path arrays.
Inspect before loading arrays
An NWB file can contain several cameras or estimators. Inventory the file first:
from fiberphotometry import inspect_ndx_pose_nwb
inspection = inspect_ndx_pose_nwb("mouse-07-day-04.nwb")
for item in inspection.pose_estimations:
print(
item.metadata.processing_module_name,
item.metadata.pose_estimation_name,
[series.name for series in item.series],
)
The bounded inspection reports each keypoint's sample count, coordinate dimension, unit, reference frame, confidence presence, timing representation, and physical time range. It also records the file SHA-256, source software/version, skeleton, videos, and linked-device names without reading the full coordinate arrays.
Read one declared estimator
from fiberphotometry import poses_from_ndx_pose_nwb
result = poses_from_ndx_pose_nwb(
"mouse-07-day-04.nwb",
subject="mouse-07",
session="day-04",
clock_id="video",
processing_module_name="behavior",
pose_estimation_name="TopCameraPose",
)
nose = next(pose for pose in result.trajectories if pose.keypoint == "nose")
Selection may be omitted only when the file contains exactly one
PoseEstimation. Multiple matches are refused with the available module/name pairs.
Subject, session, and clock remain explicit because a valid NWB structure does not
prove that a video clock is already synchronized to photometry.
Coordinates are returned in the declared unit after applying NWB's
physical = stored × conversion + offset rule. A two-column series produces x
and y; a three-column series also retains z. Missing confidence becomes an
all-NaN vector rather than an invented score. Finite extension confidence must
lie in [0, 1].
Every child series in one estimator must have identical timestamps. This follows
the extension contract and prevents keypoints from appearing to share frames when
they do not. PoseTrajectory.speed() uses all present spatial axes and invalidates
steps touching a non-finite coordinate or insufficient confidence.
Write native extension objects
from fiberphotometry import NdxPoseMetadata, add_poses_to_nwb
metadata = NdxPoseMetadata(
pose_estimation_name="TopCameraPose",
scorer="DLC_resnet50_arena",
source_software="DeepLabCut",
source_software_version="3.0.0",
skeleton_name="mouse-skeleton",
skeleton_nodes=("nose", "tail-base"),
skeleton_edges=((0, 1),),
original_videos=("videos/mouse-07-day-04.mp4",),
dimensions=((1280, 720),),
device_names=("top-camera",),
)
camera = nwbfile.create_device(name="top-camera", description="arena camera")
write = add_poses_to_nwb(
result.trajectories,
nwbfile,
metadata=metadata,
devices=(camera,),
)
The writer creates Skeletons and PoseEstimation interfaces in the declared
processing module. It refuses duplicate keypoints, inconsistent subject/session or
clock identity, unequal timestamps, mismatched skeleton order, invalid edges,
missing reference frames, and confidence outside [0, 1].
The extension's Python constructor currently describes confidence as optional,
while its 0.3.0 NWB schema validator requires the dataset. FiberPhotometry therefore
writes all-NaN confidence when it is unknown. This preserves missingness and keeps
the generated NWB file schema-valid.
Understand link fidelity
Formal Device, source-video ImageSeries, and labeled-video ImageSeries objects
are links to objects owned by the destination NWBFile. An imported link cannot be
recreated safely from its name alone. NdxPoseImportResult.issues reports such
links, and NdxPoseWriteResult.omitted_links records which were not supplied during
export. Video paths and dimensions are omitted when their linked camera devices are
omitted, avoiding a structurally misleading partial container.
Provide destination devices, source_video, and labeled_video objects when
those relationships are known. FiberPhotometry will never invent camera hardware or
claim that a path string is a formal NWB link.
Continue into photometry analysis
The restored trajectories use the same type as the existing DeepLabCut and SLEAP adapters:
nose_speed = nose.speed(minimum_confidence=0.9)
nose_speed = synchronization.synchronize_covariate(nose_speed)
aligned_speed = nose_speed.aligned_to(
photometry_time,
target_clock_id="photometry",
max_gap_s=2 / 30,
)
See the complete executable
examples/ndx_pose_roundtrip.py,
the behavior-tool composition tutorial,
and the upstream ndx-pose format documentation.
Current limits
- One NWB file is expected to describe one experimental subject, matching the upstream extension design.
- Training frames and
PoseTrainingare outside this neural-analysis boundary. - Import copies complete selected series into memory; bounded remote DANDI reading is not yet exposed here.
- Multi-animal identity switches remain an upstream tracking/QC responsibility.
- Coordinate calibration is retained, not inferred from pixels or camera metadata.