Skip to content

Command Line Tools

jabs-classify

JABS includes a script called jabs-classify, which can be used to classify a single video from the command line.

usage: jabs-classify COMMAND COMMAND_ARGS

commands:
 classify   classify a pose file
 train      train a classifier that can be used to classify multiple pose files

See `jabs-classify COMMAND --help` for information on a specific command.

Classify Command

usage: jabs-classify classify [-h] [--random-forest | --xgboost]
                            (--training TRAINING | --classifier CLASSIFIER) --input-pose
                            INPUT_POSE --out-dir OUT_DIR [--fps FPS]
                            [--feature-dir FEATURE_DIR] [--skip-window-cache]
                            [--use-pose-hash]

optional arguments:
  -h, --help            show this help message and exit
  --fps FPS             frames per second, default=30
  --feature-dir FEATURE_DIR
                        Feature cache dir. If present, look here for features before computing.
                        If features need to be computed, they will be saved here.
  --skip-window-cache   Only cache per-frame features when --feature-dir is provided, reducing
                        cache size at the cost of needing to re-calculate window features.
  --use-pose-hash       Include the pose file hash as a subdirectory level in the feature cache
                        path (e.g. <feature-dir>/<video>/<pose-hash>/<identity>). Prevents
                        collisions when multiple pipelines share a feature cache directory and
                        different pose files happen to share the same video name.

required arguments:
  --input-pose INPUT_POSE
                        input HDF5 pose file (v2, v3, or v4).
  --out-dir OUT_DIR     directory to store classification output

optionally override the classifier specified in the training file:
 Ignored if trained classifier passed with --classifier option.
 (the following options are mutually exclusive):
  --random-forest       Use Random Forest
  --xgboost             Use XGBoost

Classifier Input (one of the following is required):
  --training TRAINING   Training data h5 file exported from JABS
  --classifier CLASSIFIER
                        Classifier file produced from the `jabs-classify train` command

When --feature-dir is provided, jabs-classify automatically detects the existing cache format (HDF5 or Parquet) and reads from it. New cache files are always written in Parquet format. If the directory already contains an HDF5 cache, it is read as-is and any new window features are added to the existing HDF5 file — no conversion takes place.

Train Command

usage: jabs-classify train [-h] [--random-forest | --xgboost]
                         training_file out_file

positional arguments:
  training_file        Training h5 file exported by JABS
  out_file             output filename

optional arguments:
  -h, --help           show this help message and exit

optionally override the classifier specified in the training file:
 (the following options are mutually exclusive):
  --random-forest      Use Random Forest
  --xgboost            Use XGBoost

Note: XGBoost may be unavailable on macOS if libomp isn't installed. See jabs-classify classify --help output for list of classifiers supported in the current execution environment.

Note: fps parameter is used to specify the frames per second (used for scaling time unit for speed and velocity features from "per frame" to "per second").

jabs-features

Deprecated. jabs-features has been replaced by jabs-cli compute-features. The command still works, but it prints a deprecation warning, translates its arguments, and runs jabs-cli compute-features for you. Update existing pipelines to call the new command directly.

The legacy options map onto the new command as follows:

jabs-features jabs-cli compute-features
--pose-file --pose-file (unchanged)
--pose-version Ignored. The pose version is inferred from the pose filename (e.g. *_pose_est_v6.h5).
--feature-dir --feature-dir (unchanged)
--use-cm-distances Not needed. Distances default to cm when the pose file provides a pixel-to-cm scale. Omitting --use-cm-distances maps to --use-pixel-distances, preserving the legacy pixel default.
--window-size N -w N (repeatable for multiple window sizes)
--fps --fps (unchanged)
--use-pose-hash --use-pose-hash (unchanged)

jabs-cli

jabs-cli is a command line interface that provides access to multiple JABS command line tools. To get a listing of current commands, run:

jabs-cli --help
Usage: jabs-cli [OPTIONS] COMMAND [ARGS]...

  JABS CLI.

Options:
  --verbose  Enable verbose output.
  --help     Show this message and exit.

Commands:
  compute-features      Compute and cache JABS features for a pose file.
  convert-parquet       Convert a parquet pose file to JABS HDF5 pose format.
  postprocess           Apply a postprocessing pipeline to a JABS prediction HDF5 file.
  convert-to-nwb        Convert a JABS pose HDF5 file to NWB format.
  cross-validation      Run leave-one-group-out cross-validation for a JABS project.
  evaluate              Evaluate a trained JABS classifier against a densely labeled project.
  export-training       Export training data for a specified behavior and JABS project directory.
  export-video          Export a copy of a video with the JABS pose overlay drawn on every frame.
  link-nwb-video        Link JABS pose NWB files to their source videos.
  merge                 Merge one JABS project into another.
  prune                 Prune unused videos from a JABS project directory.
  rename-behavior       Rename a behavior in a JABS project.
  sample-frames         Sample PNG frames from a JABS project filtered by a behavior label.
  sample-pose-intervals Sample contiguous intervals from a batch of JABS pose and video files.
  update-pose           Update a JABS project to use updated pose files while remapping labels.
  update-labels         Replace a JABS project's labels with labels imported from another project.

For full documentation of the convert-to-nwb command, including output modes, subjects and session metadata JSON formats, and NWB file structure, see NWB Export. To add the videos to NWB files before uploading them to DANDI with link-nwb-video, see Link the videos.

To get help for a specific command, run:

jabs-cli <command> --help

jabs-cli export-video

The jabs-cli export-video command writes a copy of a video with the JABS pose overlay drawn on every frame. It is the batch equivalent of the GUI's File→Export Video with Overlays, and renders with the same code, so the output matches what the player shows.

It works on any video and pose file pair and does not need a JABS project. The GUI's label and prediction markers have no equivalent here: labels and predictions live inside a JABS project, which this command does not take.

Usage:

jabs-cli export-video [OPTIONS] <video_path>
  • <video_path>: Path to the video to render. Required.
  • --output, -o: Output video path. Defaults to <video>_overlay.mp4 beside the source video.
  • --pose-file: Pose file to use. Defaults to the highest-version _pose_est_v*.h5 file found beside the video.
  • --segmentation / --no-segmentation: Whether to include segmentation contours alongside the pose skeleton. Defaults to --segmentation. Segmentation needs a pose file that actually contains it: version 6 or newer and generated with segmentation, which is optional even in v6+. When the pose file has none, the command reports that and exports the pose overlay only. Use --no-segmentation to get a pose-only video from a file that does have it.
  • --codec: FourCC codec for the output. Defaults to mp4v. avc1 (H.264) produces smaller files but is absent from some OpenCV builds, in which case the export fails when the writer is opened.
  • --force, -f: Overwrite the output file if it already exists.

Examples:

# pose + segmentation, written to clip_overlay.mp4 beside the source
jabs-cli export-video /data/videos/clip.avi

# pose only, to a chosen path
jabs-cli export-video /data/videos/clip.avi -o /tmp/pose_only.mp4 --no-segmentation

Every identity is drawn the same way, at full opacity. Unlike the live player, an export has no active identity, so no animal is singled out.

jabs-init

The jabs-init command initializes a JABS project directory and computes features for all videos in parallel. This is much faster than computing features through the GUI during training. It also validates the project directory and can accept a metadata file describing the project and videos.

Usage:

jabs-init <project_dir> [--metadata <metadata.json>] [--force] [--processes <N>]
          [-w WINDOW_SIZE] [--cache-format {hdf5,parquet}] [--skip-feature-generation]
          [--timing-report <report.md> [--timing-top-n <N>]]
  • <project_dir>: Path to the JABS project directory containing video and pose files.
  • --metadata <metadata.json>: Optional path to a JSON metadata file describing the project and videos.
  • --force: Recompute features even if cache files already exist.
  • --processes <N>: Number of parallel workers to use for feature computation. If omitted, this defaults to the logical CPU count.
  • -w WINDOW_SIZE: Window size(s) to pre-compute. Can be repeated (e.g. -w 2 -w 5). Defaults to 5 if omitted.
  • --cache-format {hdf5,parquet}: Feature cache storage format. If omitted, the existing project setting is preserved. New projects default to parquet; projects created before this option existed default to hdf5. Pass --cache-format explicitly only when you want to change or set the format — use --force alongside it to rewrite existing cache files in the new format.
  • --skip-feature-generation: Validate and initialize the project without computing features.
  • --timing-report <report.md>: Write a Markdown report listing how long each feature took to compute, sorted from slowest to fastest. Times are summed across all identities, worker processes, and window sizes. Features loaded from the cache are not timed, so combine with --force to time every feature.
  • --timing-top-n <N>: Only list the N slowest features in the timing report. Requires --timing-report.

Examples:

# Initialize a project with default settings (Parquet cache, window size 5)
jabs-init /path/to/project

# Initialize with metadata, 8 workers, and explicit window sizes
jabs-init /path/to/project --metadata project_metadata.json --processes 8 -w 2 -w 5

# Migrate an existing HDF5 cache to Parquet (recomputes all features)
jabs-init /path/to/project --cache-format parquet --force

# Recompute all features and report the 10 slowest
jabs-init /path/to/project --force --timing-report feature_timing.md --timing-top-n 10

See the Project Setup Guide for a brief overview and Feature Cache Format for migration guidance.

jabs-cli compute-features

The jabs-cli compute-features command computes and caches JABS features for a single pose file, without requiring a JABS project. This is intended for batch and HPC pipelines that process pose files individually. It replaces the deprecated jabs-features script.

Usage:

jabs-cli compute-features --pose-file <POSE_FILE> --feature-dir <FEATURE_DIR> [--use-pixel-distances]
                          [-w WINDOW_SIZE] [--fps FPS] [--use-pose-hash]
                          [--cache-format {hdf5,parquet}] [--force]
  • --pose-file <POSE_FILE>: Pose file to compute features for. The pose version is inferred from the filename (e.g. *_pose_est_v6.h5).
  • --feature-dir <FEATURE_DIR>: Directory to write output features.
  • --use-pixel-distances: Force pixel distance units. By default, cm units are used when the pose file provides a pixel-to-cm scale, and pixel units otherwise.
  • -w, --window-size <N>: Window size for window features. Repeat to compute multiple window sizes (e.g. -w 5 -w 10). Omit to compute per-frame features only.
  • --fps <FPS>: Frames per second used to scale time-based features from "per frame" to "per second". Default: 30.
  • --use-pose-hash: Include the pose file hash as a subdirectory level in the feature cache path (e.g. <feature-dir>/<video>/<pose-hash>/<identity>). Use this when building a shared feature cache for multiple pipelines where video filenames may collide.
  • --cache-format {hdf5,parquet}: Storage format for the feature cache. Default: parquet.
  • --force: Recompute features and overwrite the cache even when a valid cache exists.

Examples:

# Per-frame features only
jabs-cli compute-features --pose-file session_pose_est_v6.h5 --feature-dir /path/to/features

# Per-frame plus two window sizes, at 60 fps
jabs-cli compute-features --pose-file session_pose_est_v6.h5 --feature-dir /path/to/features \
    -w 5 -w 10 --fps 60

# Shared cache across pipelines, forcing recomputation
jabs-cli compute-features --pose-file session_pose_est_v6.h5 --feature-dir /shared/features \
    --use-pose-hash --force

jabs-cli update-pose

The jabs-cli update-pose command updates an existing JABS project to use updated pose files for the same videos while remapping existing labels onto the updated poses. This is intended for keeping labels when pose files have been regenerated or otherwise updated.

Usage:

jabs-cli update-pose <project_dir> <updated_pose_dir> [--min-iou-thresh <FLOAT>] [--verbose] [--annotate-failures] [--drop-timeline-annotations] [--skip-feature-gen] [--tolerate-orphan-identities]
  • <project_dir>: Path to the JABS project to update in place.
  • <updated_pose_dir>: Directory containing updated pose files for the project videos. For each video, the latest pose version in this directory is used. All videos must resolve to the same latest version, and that version must include bounding boxes.
  • --min-iou-thresh <FLOAT>: Minimum acceptable median IoU for a label remap match. Blocks below this threshold are skipped. Default: 0.5.
  • --verbose: Print successful label remap assignments in addition to warnings.
  • --annotate-failures: Add timeline annotations to the project for blocks whose label remap fails.
  • --drop-timeline-annotations: Discard existing timeline annotations from the source project instead of copying or remapping them.
  • --skip-feature-gen: Skip automatic feature regeneration after a successful pose update.
  • --tolerate-orphan-identities: Continue past orphan-identity references in the live project's annotations (warn instead of erroring at preflight). Affected (identity, behavior) pairs are skipped during remap; all other labels are remapped normally. By default the command aborts at preflight when orphans are detected.

Before modifying the project, the command validates the updated pose files, runs the pose update and label remap in disposable staging projects, and creates a timestamped backup zip under <project_dir>/.backup. By default, existing timeline annotations are also carried forward: video-level annotations are copied as-is, and identity-scoped annotations are remapped by the same interval-matching logic used for label blocks. Use --drop-timeline-annotations if you want to discard existing timeline annotations instead. Only after the staged pose update succeeds are annotations, project metadata, and pose files copied back into the project.

Orphan identity check. Preflight rejects projects whose annotation files reference identity indices that the corresponding pose file no longer supplies — typically a sign the pose was regenerated with fewer identities, leaving stale label entries that can no longer be matched against pose data. The command lists every offending video and identity, then exits without modifying anything. Clean up the affected annotation files (drop the orphan identity entries from each .json) and re-run, or pass --tolerate-orphan-identities to warn-and-skip the affected entries instead of aborting.

After a successful live pose update, features are regenerated automatically only when --skip-feature-gen is not passed and the existing jabs/project.json already contains explicit window_sizes. If the project file has no window_sizes entry, there is nothing to regenerate and feature generation is skipped. If you want more control over feature regeneration, use --skip-feature-gen and run jabs-init manually, or generate features from the GUI.

Example:

jabs-cli update-pose /path/to/project /path/to/updated_pose_dir --min-iou-thresh 0.5

If instead you want to import labels from another JABS project while keeping the target's pose untouched, see jabs-cli update-labels.

jabs-cli update-labels

The jabs-cli update-labels command is the inverse of update-pose: instead of keeping the target's labels and replacing its pose, it keeps the target's pose and replaces its labels with labels imported from another JABS project. The source project provides both the labels and the pose used for IoU-based identity matching.

Usage:

jabs-cli update-labels <project_dir> <source_project_dir> [--min-iou-thresh <FLOAT>] [--verbose] [--annotate-failures] [--drop-timeline-annotations] [--tolerate-orphan-identities]
  • <project_dir>: Path to the target JABS project whose labels will be replaced in place. The target's pose is unchanged. If <project_dir> is a directory of videos + pose files with no jabs/ subdirectory, a minimal JABS project is scaffolded automatically — features are not generated, so you may want to run jabs-init separately afterwards.
  • <source_project_dir>: Path to a JABS project providing the replacement labels and the pose used for IoU matching. Must already be a valid JABS project; every source-labeled video must also exist in the target.
  • --min-iou-thresh <FLOAT>: Minimum acceptable median IoU for a label remap match. Blocks below this threshold are skipped. Default: 0.5.
  • --verbose: Print successful label remap assignments in addition to warnings.
  • --annotate-failures: Add timeline annotations to the target for blocks whose label remap fails. Annotations use the same behavior-remap-failed / not-behavior-remap-failed tags as update-pose; the description text distinguishes the originating operation.
  • --drop-timeline-annotations: Discard source timeline annotations instead of copying or remapping them.
  • --tolerate-orphan-identities: Continue past orphan-identity references in the source's annotations (warn instead of erroring at preflight). Affected (identity, behavior) pairs are skipped during remap; all other labels are imported normally. By default the command aborts at preflight when orphans are detected.

Before modifying the project, the command validates both inputs, runs the label remap in disposable staging projects, and creates a timestamped backup zip under <project_dir>/.backup covering jabs/project.json, annotations, and predictions (pose files are not touched). Labels are processed block by block, matched by median bbox IoU between the source pose and the target's existing pose, and written to the staged destination label track. By default, source timeline annotations are also carried forward and remapped the same way as label blocks; use --drop-timeline-annotations to discard them.

Existing target labels for videos that the source does not cover are left untouched (per-video replace). Behaviors named in the source's project.json but not present in the target are merged into the target's project.json so the imported labels are usable in the GUI; behaviors already configured in the target keep their existing settings.

Orphan identity check. Preflight rejects the run if any source annotation file references identity indices that the source's own pose file no longer supplies. This typically means the source's pose was regenerated with fewer identities and the affected label entries are stranded — there is no source-side bounding-box data to anchor the IoU match against. The command lists every offending video and identity, then exits without modifying the target. Clean up the source annotation files (drop the orphan identity entries from each .json) and re-run, or pass --tolerate-orphan-identities to warn-and-skip the affected entries instead of aborting.

The target's pose is unchanged, so the feature cache stays valid and is not regenerated. Predictions are cleared because they are stale relative to the new labels; classifiers, the performance cache, and feature files are all left in place. If you need to retrain after a label import, run training from the GUI or via jabs-classify.

If a failure occurs after the live apply begins, the command prints the backup path plus cleanup and manual restore instructions instead of restoring automatically.

Example:

jabs-cli update-labels /path/to/target_project /path/to/source_project --min-iou-thresh 0.5

If instead you want to replace the target's pose while keeping its labels, see jabs-cli update-pose.

jabs-cli merge

The jabs-cli merge command merges a source JABS project into a destination JABS project. Videos, pose files, behaviors, and labels from the source project are imported into the destination project, which is modified in place. The source project is left unchanged.

Note: this command was previously the standalone jabs-project-merge script, which has been removed.

Usage:

jabs-cli merge <destination_project> <source_project> --merge-strategy <STRATEGY>
  • <destination_project>: Path to the destination JABS project. This project is modified by importing videos and labels from the source project.
  • <source_project>: Path to the source JABS project. Not modified.
  • --merge-strategy <STRATEGY>: Required. How to resolve conflicting labels, i.e. frames labeled differently in both projects for the same video, identity, and behavior:
    • behavior-wins: Keep the label with the behavior annotation.
    • not-behavior-wins: Keep the label without the behavior annotation.
    • destination-wins: Keep the label from the destination project.

Videos present only in the source project are copied into the destination project along with their pose files, and behaviors defined only in the source project are added to the destination project's project.json. Labels for videos already in the destination project are merged annotation by annotation using the selected strategy.

Both paths must already be valid JABS project directories, and they must be different directories.

Example:

jabs-cli merge /path/to/destination_project /path/to/source_project --merge-strategy destination-wins

jabs-cli sample-frames

The jabs-cli sample-frames command extracts individual video frames as PNG images from a JABS project, filtered to frames annotated with a specific behavior label. The primary use case is building targeted training datasets for the keypoint/pose model — particularly for behaviors where pose estimation quality is known to be degraded.

Usage:

jabs-cli sample-frames --behavior <label> \
    (--num-frames N | --frames-per-bout N) \
    [--out-dir <path>] \
    <project_dir>
  • <project_dir>: Path to the JABS project directory.
  • --behavior: Behavior label name to sample frames for (required). Must match a behavior defined in the project. In multi-class projects, None samples frames labeled as the None (background) class.
  • --num-frames N: Sample a total of N frames distributed uniformly at random across all labeled bouts in the project. Mutually exclusive with --frames-per-bout.
  • --frames-per-bout N: Sample up to N frames from each individual labeled bout. If a bout contains fewer than N frames, all frames in that bout are used. Mutually exclusive with --num-frames.
  • --out-dir: Directory to write PNG files. Defaults to the current working directory. Created (including intermediate directories) if it does not exist.

Exactly one of --num-frames or --frames-per-bout must be provided.

Output filenames follow the JABS GUI "Export Frame" convention:

{video_stem}_frame{frame_number:06d}.png

For example, frame 1234 from session_2024.mp4 is written as session_2024_frame001234.png.

Frames are deduplicated across identities: if multiple identities overlap in the same bout, each unique video frame is written only once.

Examples:

# Sample 200 frames total, distributed uniformly across all walking bouts
jabs-cli sample-frames --behavior walking --num-frames 200 /path/to/project

# Sample up to 10 frames from each individual walking bout
jabs-cli sample-frames --behavior walking --frames-per-bout 10 /path/to/project

# Write frames to a specific output directory
jabs-cli sample-frames --behavior walking --num-frames 100 \
    --out-dir /data/frames /path/to/project

# Sample None (background) frames from a multi-class project
jabs-cli sample-frames --behavior None --num-frames 200 /path/to/project

jabs-cli sample-pose-intervals

The jabs-cli sample-pose-intervals command clips a contiguous interval from each video and pose file in a batch, writing the results to an output directory. It is useful for creating smaller representative subsets of large datasets for annotation or testing. The pose file version is inferred automatically from the filename — the highest version available for each video is used.

Usage:

jabs-cli sample-pose-intervals \
    --batch-file <batch.txt> \
    --root-dir <root_dir> \
    --out-dir <out_dir> \
    --out-frame-count <N> \
    [--start-frame <F>] \
    [--only-pose]
  • --batch-file: Path to a plain-text file listing video filenames to process, one per line (see Batch file format below).
  • --root-dir: Root directory. All filenames in the batch file are interpreted as relative to this directory.
  • --out-dir: Output directory for clipped pose and video files. Created automatically if it does not exist.
  • --out-frame-count: Number of frames in each clipped output. At 30 fps, 1800 ≈ one minute.
  • --start-frame: 1-based start frame index. If omitted, a random start frame is chosen independently for each video.
  • --only-pose: Write only the clipped pose HDF5 file; skip video output.

Example:

jabs-cli sample-pose-intervals \
    --batch-file batch.txt \
    --root-dir /data/videos \
    --out-dir /data/sampled \
    --out-frame-count 9000 \
    --start-frame 54000

Batch file format

The batch file is a plain-text file with one video filename per line, relative to --root-dir. Blank lines are ignored. Subdirectory separators (/ or \) are replaced with + in output filenames to keep the output directory flat.

experiment1/mouse_a.avi
experiment1/mouse_b.avi
experiment2/mouse_c.avi

For each video, the corresponding pose file is located automatically by searching beside the video for <stem>_pose_est_v*.h5 files and selecting the highest version. If no pose file is found the video is skipped with a warning.

Output files

For each successfully processed video, two files are written to --out-dir (or one file with --only-pose):

  • <flat_video_name>_<start_frame>_pose_est_v*.h5 — clipped pose HDF5 file
  • <flat_video_name>_<start_frame>.avi — clipped video (MJPEG, 30 fps)

where <flat_video_name> is the video filename with path separators replaced by + and the extension removed, and <start_frame> is the 1-based index of the first frame included in the clip.

Dynamic object handling

Pose v7+ files may contain dynamic objects (e.g. fecal boli) stored as sparse predictions indexed by sample_indices rather than by frame. When clipping:

  • The last prediction strictly before the clip start is included and clamped to clip-relative frame 0, so the most recent pre-clip state is carried into the clip.
  • All predictions whose sample_indices fall within [start, stop) are included and rebased by subtracting start.
  • Predictions at or after stop are excluded.

If no predictions exist within or before the clip window, empty arrays are written for that dynamic object.

jabs-cli postprocess

The jabs-cli postprocess command applies a postprocessing pipeline to an existing JABS prediction HDF5 file. It reads the raw predicted classes, runs the configured pipeline stages, and writes the results back as predicted_class_postprocessed datasets alongside the original predictions. The raw predictions are never modified.

Usage:

jabs-cli postprocess PREDICTION_FILE \
    --config CONFIG_FILE \
    [--behavior BEHAVIOR] \
    [--output OUTPUT_FILE]
  • PREDICTION_FILE: Path to the JABS prediction HDF5 file to process.
  • --config CONFIG_FILE: Path to a JSON or YAML pipeline config file (see Config file format below). Required unless --list-behaviors is used.
  • --behavior BEHAVIOR: Restrict processing to a single named behavior. Required when the config file is a list of stages. When the config is a dict, filters to only the specified behavior.
  • --output OUTPUT_FILE: Path for the output file. If omitted, the input file is updated in place.
  • --list-behaviors: Print the behavior names present in the prediction file and exit. Useful for finding the correct behavior names to use in a config dict.

To inspect which behaviors are stored in a prediction file before writing a config:

jabs-cli postprocess /path/to/predictions.h5 --list-behaviors

Examples:

Apply a single-behavior pipeline (config is a list of stages):

jabs-cli postprocess /path/to/predictions.h5 \
    --config grooming_pipeline.json \
    --behavior grooming

Apply a multi-behavior pipeline and write to a new file:

jabs-cli postprocess /path/to/predictions.h5 \
    --config all_behaviors_pipeline.yaml \
    --output /path/to/predictions_postprocessed.h5

Config file format

The config file is a JSON or YAML file in one of two formats.

Note: YAML support requires the yaml extra: pip install 'jabs-behavior-classifier[yaml]'

List format (single behavior)

A top-level list of stage configurations applied in order to a single behavior. --behavior is required when this format is used.

[
  {
    "stage_name": "GapInterpolationStage",
    "parameters": {"max_interpolation_gap": 5}
  },
  {
    "stage_name": "BoutDurationFilterStage",
    "parameters": {"min_duration": 10},
    "enabled": true
  }
]

Dict format (multiple behaviors)

A top-level object mapping behavior names to lists of stage configurations. Behavior names must match the safe names stored in the HDF5 file (special characters are replaced with underscores). Use --list-behaviors to check the names in a file.

{
  "grooming": [
    {"stage_name": "GapInterpolationStage", "parameters": {"max_interpolation_gap": 5}},
    {"stage_name": "BoutDurationFilterStage", "parameters": {"min_duration": 10}}
  ],
  "rearing": [
    {"stage_name": "BoutStitchingStage", "parameters": {"max_stitch_gap": 3}}
  ]
}

Stage schema

Each stage entry has the following fields:

Field Required Description
stage_name Yes Name of the postprocessing stage class (see Available stages below).
parameters Yes Object of stage-specific parameters, or null if the stage takes no parameters.
enabled No Boolean; defaults to true. Set to false to skip the stage without removing it from the config.

Available stages

Stage name Parameter Description
GapInterpolationStage max_interpolation_gap (int > 0) Fill short gaps of no-prediction frames between behavior bouts. Gaps up to this length (inclusive) are filled in.
BoutStitchingStage max_stitch_gap (int > 0) Merge behavior bouts separated by short not-behavior gaps. Gaps up to this length (inclusive) are stitched over.
BoutDurationFilterStage min_duration (int > 0) Remove behavior bouts shorter than the specified duration (in frames).

Stages are applied in the order they appear in the list. A typical pipeline runs GapInterpolationStage or BoutStitchingStage before BoutDurationFilterStage so that short gaps are merged prior to duration filtering.

jabs-cli convert-parquet

The jabs-cli convert-parquet command converts a parquet pose file to the JABS HDF5 pose format (v5). The output file is named after the input with .parquet replaced by _pose_est_v8.h5.

Usage

jabs-cli convert-parquet PARQUET_PATH [OPTIONS]

Options

Option Default Description
--lixit-parquet PATH - Single-row summary parquet with lixit keypoints. The keypoints value contains (x, y) pairs; each consecutive group of 3 keypoints (tip, left_side, right_side) defines one lixit. 6 keypoints = 2 lixits.
--num-frames INT 1800 Total number of frames in the video.
--out-dir PATH same as input Output directory for the converted HDF5 file. Created if it does not exist.

Input format

The input parquet file must have the following columns:

  • frame - frame number
  • eartag_code - external identity string (used for external_identity_mapping); rows with empty, "00", or "01" values are treated as no-reads and excluded
  • kpt_1_x / kpt_1_y - nose
  • kpt_2_x / kpt_2_y - left ear
  • kpt_3_x / kpt_3_y - right ear
  • kpt_4_x / kpt_4_y - base of tail
  • kpt_5_x / kpt_5_y - tip of tail

Examples

# Basic conversion
jabs-cli convert-parquet session_poses.parquet

# With lixit landmarks, custom frame count, and custom output directory
jabs-cli convert-parquet session_poses.parquet \
    --lixit-parquet lixit_summary.parquet \
    --num-frames 3600 \
    --out-dir /path/to/output

jabs-cli cross-validation

The jabs-cli cross-validation command runs leave-one-group-out cross-validation in a JABS project, then trains a final model on all labeled data to report feature importance. It prints per-iteration metrics to the console and writes a training report file (the same report produced by the GUI). Use it to estimate how well a classifier generalizes before committing to a trained model.

For a binary project, cross-validation is run for a single behavior chosen with --behavior. For a multi-class project, all behaviors (plus the None class) are cross-validated together as one classifier, exactly as in the GUI, and the report gives macro- and micro-averaged metrics and per-class counts. --behavior is not needed for multi-class projects and is ignored if given.

Features must already be computed for the project (for example via jabs-init); if they are missing this command will compute them, which can be slow.

Usage:

jabs-cli cross-validation DIRECTORY [--behavior BEHAVIOR] \
    [-k SPLITS] \
    [--grouping-strategy {video|individual|filename}] \
    [--grouping-pattern REGEX] \
    [--classifier {catboost|random_forest|xgboost}] \
    [--postprocessing | --no-postprocessing] \
    [--report-file FILE] \
    [--mlflow [ENV_FILE]] [--mlflow-experiment NAME] [--mlflow-tag KEY=VALUE] \
    [--mlflow-no-report] [--mlflow-no-annotations]
  • DIRECTORY: Path to the JABS project directory.
  • --behavior BEHAVIOR: Behavior to evaluate. Required for binary projects; quote it if it contains spaces; must match an existing behavior in the project. Ignored for multi-class projects.
  • -k SPLITS: Number of cross-validation iterations. 0 (the default) uses the maximum number of splits supported by the data and grouping strategy.
  • --grouping-strategy {video|individual|filename}: How labeled frames are grouped into cross-validation folds (see Grouping strategies). If omitted, the project's saved setting is used.
  • --grouping-pattern REGEX: Regular expression applied to each video filename to derive a grouping key. Only used with --grouping-strategy filename. If omitted, the pattern saved in the project is used.
  • --classifier {catboost|random_forest|xgboost}: Classifier to evaluate. Defaults to xgboost. The available choices depend on which classifier libraries are installed; see Classifier Types.
  • --postprocessing / --no-postprocessing: Whether to also report metrics with the behavior's prediction postprocessing pipeline applied. If omitted, the behavior's saved Evaluate in Cross-Validation setting is used. Evaluating postprocessing re-predicts each held-out animal's full track, so it costs roughly one extra classification pass over the labeled animals. Binary classifiers only; on a multi-class project the evaluation is skipped with a warning.
  • --report-file FILE: Where to write the training report. The format is chosen by extension: .md (Markdown) or .json (JSON). If omitted, a timestamped Markdown file is written to the current directory (<behavior>_<timestamp>_training_report.md, or multiclass_<timestamp>_training_report.md for a multi-class project).
  • --mlflow, --mlflow-experiment, --mlflow-tag, --mlflow-no-report, --mlflow-no-annotations: Optional MLflow logging (see MLflow logging).

Grouping strategies

Cross-validation holds out one group of labeled data per iteration and trains on the rest, so groups define what "generalization" means for the score. JABS supports three strategies:

Strategy Each group is... Use when
individual one (video, identity) pair you want to measure generalization across individual animals
video one whole video (all identities in it) you want to measure generalization across videos/sessions
filename all videos whose filename yields the same key under --grouping-pattern videos from the same cage/cohort/day share a filename component and should not be split across train and test

For the filename strategy, the pattern is applied with re.search, so it matches anywhere in the filename. If the pattern has a capturing group, the first captured group is the key; otherwise the whole match is the key. Videos that do not match the pattern are placed in their own single-video group. For example, --grouping-pattern '^(\w+?)_' groups cage12_2024-01-01.mp4 and cage12_2024-01-02.mp4 together under the key cage12.

Training report

The report includes:

  • Per-iteration accuracy, precision and recall for both classes, and F1 for the behavior class, plus the held-out test group label for each iteration.
  • The top features (by importance) from a final model trained on all labeled data.
  • Labeled frame and bout counts, the window size, distance unit, classifier type, and the grouping strategy/pattern used.
  • When postprocessing evaluation is enabled, a second set of per-iteration metrics with the postprocessing pipeline applied, next to the raw ones, and the stage configuration that was evaluated. The console shows only the postprocessed accuracy and F1 per iteration; the full postprocessed metrics and the stage configuration are in the report. If the postprocessed pass does not reproduce an iteration's raw labels and predictions frame for frame, the two are not measuring the same thing, and the report and console say so rather than presenting the comparison as sound.

Examples:

# Cross-validate "grooming" with default settings (project grouping, all splits, xgboost)
jabs-cli cross-validation /path/to/project --behavior grooming

# 5-fold, grouped by individual animal, with a CatBoost classifier
jabs-cli cross-validation /path/to/project --behavior grooming \
    -k 5 --grouping-strategy individual --classifier catboost

# Group videos by a shared filename prefix and write a JSON report
jabs-cli cross-validation /path/to/project --behavior grooming \
    --grouping-strategy filename --grouping-pattern '^(\w+?)_' \
    --report-file grooming_cv.json

MLflow logging

The cross-validation command can optionally log each run to an MLflow tracking server, recording aggregate metrics, run parameters, descriptive tags, and — as artifacts — the training report and a zip of the project's annotations. This is opt-in and off by default.

Installing the MLflow extra

MLflow is an optional dependency. Install it with the mlflow extra:

pip install 'jabs-behavior-classifier[mlflow]'

If you request MLflow logging (--mlflow) without the extra installed, the command fails immediately with an error and exits 1 (before running the cross-validation), so you can install the extra and re-run rather than discovering after a long run that nothing was logged.

Enabling logging

Add the --mlflow flag:

# Use connection settings from the ambient environment
jabs-cli cross-validation /path/to/project --behavior grooming --mlflow

# Use connection settings from a .env file
jabs-cli cross-validation /path/to/project --behavior grooming --mlflow settings.env

--mlflow optionally takes the path to a .env file. With no path, connection settings are read from the current environment.

Connection configuration

Connection details — tracking server URI, experiment, authentication, TLS — are not passed as command-line options. They come from standard MLFLOW_* environment variables, either exported in your shell or written to the .env file you pass to --mlflow. Only keys beginning with MLFLOW_ are read from the .env file; everything else is ignored.

Common variables:

Variable Purpose
MLFLOW_TRACKING_URI URL (or local path) of the tracking server, e.g. https://mlflow.example.org
MLFLOW_EXPERIMENT_NAME Overrides the default experiment name (see Selecting the experiment)
MLFLOW_TRACKING_USERNAME / MLFLOW_TRACKING_PASSWORD HTTP basic-auth credentials
MLFLOW_TRACKING_TOKEN Bearer-token auth (alternative to username/password)

Example .env file:

MLFLOW_TRACKING_URI=https://mlflow.example.org
MLFLOW_TRACKING_USERNAME=jabs
MLFLOW_TRACKING_PASSWORD=hunter2

Selecting the experiment

Each behavior is logged to its own experiment by default, named jabs-<behavior> (for example jabs-grooming). This keeps comparisons meaningful: an experiment's runs table is effectively a leaderboard, and you want to rank runs of the same behavior over time rather than mix behaviors, whose metrics are not comparable. The experiment is created automatically if it does not exist. A multi-class project cross-validates all behaviors together, so its experiment is named for the whole set of behaviors joined with + (for example jabs-Walk+Run); projects with different behavior sets get different experiments.

To override the experiment name, in order of precedence:

  1. --mlflow-experiment NAME (highest) — use a specific experiment for this run.
  2. MLFLOW_EXPERIMENT_NAME (in your shell or the .env file).
  3. The default jabs-<behavior>.
# Default: logs to experiment "jabs-grooming"
jabs-cli cross-validation /path/to/project --behavior grooming --mlflow

# Override the experiment for this run
jabs-cli cross-validation /path/to/project --behavior grooming --mlflow \
    --mlflow-experiment grooming-hyperparam-sweep

Comparing runs (leaderboard)

The aggregate scores below (cv_f1_behavior_mean, cv_accuracy_mean, etc.) are logged as MLflow metrics, so an experiment's runs table doubles as a leaderboard — sort by the cv_f1_behavior_mean column to rank a behavior's runs by mean F1. The full per-fold breakdown rides along as the training-report artifact.

What gets logged

Each invocation creates one MLflow run named <behavior>-cv-<timestamp>. For a multi-class project the run is named for all of its behaviors joined with + (for example Walk+Run-cv-<timestamp>).

Metrics (aggregated across cross-validation iterations):

  • cv_accuracy_mean, cv_accuracy_std
  • cv_precision_behavior_mean / _std, cv_recall_behavior_mean / _std, cv_f1_behavior_mean / _std
  • cv_iterations — number of folds run
  • frames_behavior, frames_not_behavior, bouts_behavior, bouts_not_behavior — dataset composition
  • training_time_ms

Parameters: behavior, classifier, window_size, balance_labels, symmetric_behavior, distance_unit, cv_grouping_strategy, and cv_grouping_regex (only for the filename strategy).

Tags: auto-derived behavior, classifier, cv_grouping_strategy, and jabs_git (the short git SHA of the JABS checkout, when available). Any --mlflow-tag entries are merged on top, so a user tag wins over an auto tag with the same key.

Artifacts:

  • The generated training report file, unless --mlflow-no-report is passed.
  • annotations.zip — a zip of the project's jabs/annotations directory, unless --mlflow-no-annotations is passed. This captures the label set the run was computed from, so a run's metrics can be traced back to (and reproduced from) the exact annotations. Unpacking it recreates an annotations/ directory. If the project has no annotations directory, or it holds no archivable files, no artifact is uploaded and a warning is logged. Symlinks are not archived (zipping one would store the target's content, publishing files from outside the project); any that are skipped are logged.

Free-form tags

Add searchable tags to the run with --mlflow-tag, which is repeatable:

jabs-cli cross-validation /path/to/project --behavior grooming --mlflow settings.env \
    --mlflow-tag purpose=baseline --mlflow-tag cohort=2024Q1

Each entry is KEY=VALUE; only the first = splits the entry, so values may contain =.

Skipping artifacts

Each artifact has its own opt-out flag. To log metrics and parameters only:

jabs-cli cross-validation /path/to/project --behavior grooming --mlflow \
    --mlflow-no-report --mlflow-no-annotations

Pass only --mlflow-no-annotations to keep the report but skip the annotations upload — worth doing for a project with a very large annotations directory, since the archive is uploaded on every run.

Archiving the annotations is best-effort: if the zip cannot be written, a warning is logged and the run still gets its metrics, parameters, and report artifact.

Exit codes and failure handling

MLflow logging happens after the cross-validation results are printed and the report is saved, so a logging failure never costs you the results:

  • Extra not installed: the command fails fast with an error and exits 1 before running the cross-validation, since logging was explicitly requested but cannot be honored. Install the extra (or drop --mlflow) and re-run.
  • Env file not found: if the path passed to --mlflow does not exist, the command fails fast and exits 1 before running the cross-validation. The path is validated up front (with a leading ~ expanded), so a typo or missing file is caught immediately rather than after the run.
  • Logging fails (for example the tracking server is unreachable or authentication fails): the results and report are preserved, a warning is printed, and the command exits with code 3 — distinct from the generic error code 1, so automation can tell a push failure apart from a cross-validation failure.

Full example

# settings.env contains:
#   MLFLOW_TRACKING_URI=https://mlflow.example.org
#   MLFLOW_EXPERIMENT_NAME=mouse-grooming
jabs-cli cross-validation /path/to/project \
    --behavior grooming \
    --grouping-strategy individual \
    --classifier xgboost \
    --mlflow settings.env \
    --mlflow-tag purpose=baseline

jabs-cli evaluate

The jabs-cli evaluate command runs a trained binary classifier over every pose file in a JABS project and compares the predictions against that project's own labels. It is intended for a densely labeled project - one where every frame, or nearly every frame, is labeled for the behavior - so that the labels can stand in as ground truth.

This is a different question from cross-validation. Cross-validation estimates how well a classifier would generalize, using held-out folds of the training project. evaluate takes a classifier you already trained and scores it against a separate, fully labeled project.

Features must already be computed for the project, or they will be computed on the fly, which can be slow. The project's own feature cache is used by default.

Usage:

jabs-cli evaluate DIRECTORY --classifier CLASSIFIER \
    [--behavior BEHAVIOR] \
    [--postprocess-config FILE] \
    [--min-overlap FRAMES] [--iou-threshold FLOAT] \
    [--max-sweep-combinations N] \
    [--feature-dir DIR] [--fps FPS] \
    [--save-predictions DIR] \
    [--out-dir DIR] [--json-out FILE] [--csv-out FILE] [--report-out FILE] \
    [--per-video]
  • DIRECTORY: Path to the JABS project holding the ground-truth labels and pose files.
  • --classifier CLASSIFIER (required): Trained classifier, as produced by jabs-classify train or saved by the GUI. Multi-class classifiers are rejected; see Limitations.
  • --behavior BEHAVIOR: Behavior whose project labels are the ground truth. Defaults to the behavior name recorded in the classifier. Use this when the project names the behavior differently.
  • --postprocess-config FILE: JSON or YAML postprocessing pipeline config. When given, the postprocessed predictions are compared against the ground truth alongside the raw ones, and both appear side by side in every table. Same format as jabs-cli postprocess --config.
  • --min-overlap FRAMES: Frames two bouts must share to match under the frame-overlap criterion. Defaults to 1.
  • --iou-threshold FLOAT: Intersection-over-union two bouts must reach to match under the IoU criterion. Defaults to 0.5. Must be greater than 0 and at most 1.
  • --max-sweep-combinations N: Ceiling on the number of parameter combinations a swept config may expand to (default 256). See Sweeping postprocessing parameters.
  • --feature-dir DIR: Feature cache directory. Defaults to the project's own feature cache.
  • --fps FPS: Frames per second to assume for every video, skipping the per-video lookup. Defaults to reading it from each video file.
  • --save-predictions DIR: Write one prediction HDF5 file per video into this directory, in the same format jabs-classify classify produces (see Saving predictions).
  • --out-dir DIR: Write all three output files into this directory using generated, timestamped filenames.
  • --json-out FILE, --csv-out FILE, --report-out FILE: Write individual outputs to explicit paths.
  • --per-video: Also print a per-video breakdown to the console. The written outputs always include per-video detail regardless of this flag.

What is compared

The comparison is reported two ways.

Frame-level agreement is a straight frame-by-frame comparison, reported as accuracy, precision, recall and F1 for the behavior class. Two kinds of frame are excluded from it rather than counted as errors, and reported as their own totals:

  • Frames the ground truth leaves unlabeled. A classifier is not wrong for predicting behavior where nothing was labeled.
  • Frames that are labeled but that the classifier could not score, normally because the identity has no pose there. A missing pose is not a missed detection.

Bout-level agreement asks whether each labeled bout was found at all, allowing the predicted start and end frames to disagree with the labeled ones. A bout is a contiguous run of frames labeled (or predicted) as the behavior.

Bout match criteria

Two bouts count as the same occurrence when they satisfy a match criterion. Both criteria are always reported, in their own table, so you can see how much of the detection rate survives a boundary-quality requirement:

Criterion Matches when Reads as
overlap >= N frames the bouts share at least N frames (N = --min-overlap, default 1) did the classifier notice the bout at all
IoU >= T intersection-over-union of the two frame ranges is at least T (T = --iou-threshold, default 0.5) did it also get the extent roughly right

A classifier that fires a two-frame blip inside every labeled bout scores a perfect detection rate under the overlap criterion and a poor one under IoU. A large gap between the two tables is the signal that boundaries, not detection, are the problem.

Fragmentation and merging

Matching is many-to-many: a labeled bout is detected when any predicted bout matches it, and a predicted bout is a hit when it matches any labeled bout. A labeled bout the classifier split into several predictions is therefore still counted as detected, and each fragment still counts as a hit, rather than the split being charged against precision.

The two ways the segmentation can go wrong are reported as their own columns:

  • Fragmented: labeled bouts matched by two or more predicted bouts - the classifier broke one bout into several.
  • Merged: predicted bouts matching two or more labeled bouts - the classifier ran several bouts together.

Bouts that cannot be judged are excluded from the rates and reported separately: a labeled bout lying entirely in unscored frames is excluded from the detection rate, and a predicted bout lying entirely in unlabeled frames is excluded from precision.

Output files

Option Contents
--json-out Metrics nested overall → per-video → per-identity, for each stage. Rates whose denominator is empty are null, not 0.
--csv-out One row per labeled bout and per predicted bout: video, identity, stage, source, start/end frames, duration, whether it was evaluable, how many opposing bouts it overlaps, its best overlap and IoU, and whether it matched under each criterion. Sort by matched_overlap to find the bouts that were missed.
--report-out A Markdown report with the same tables as the console plus a per-video breakdown, in the style of the cross-validation training report.

Examples:

# Console report only
jabs-cli evaluate /path/to/dense_project --classifier grooming.pickle

# Compare raw and postprocessed predictions, writing all three output files
jabs-cli evaluate /path/to/dense_project --classifier grooming.pickle \
    --postprocess-config pipeline.yaml --out-dir results/

# Require a stricter boundary match and a 5-frame minimum overlap
jabs-cli evaluate /path/to/dense_project --classifier grooming.pickle \
    --min-overlap 5 --iou-threshold 0.75

# Find the missed bouts
jabs-cli evaluate /path/to/dense_project --classifier grooming.pickle \
    --csv-out bouts.csv

Sweeping postprocessing parameters

Tuning a postprocessing pipeline means trying several values for a parameter and comparing the results. In a config passed to this command, a parameter holding a list is a sweep axis:

Seizure:
  - stage_name: BoutStitchingStage
    enabled: true
    parameters:
      max_stitch_gap: [15, 30, 45]
  - stage_name: BoutDurationFilterStage
    enabled: true
    parameters:
      min_duration: [5, 10, 15, 30]

That expands to 12 combinations. Because a pipeline is a pure function of the predictions it is given, the project is classified once and every combination is applied to those cached predictions - the grid costs stage arithmetic, not feature extraction. Sweeping is therefore far cheaper than invoking this command once per combination.

The list syntax is specific to jabs-cli evaluate. Everywhere else a postprocessing config is consumed - jabs-cli postprocess, the GUI - a parameter holds exactly one value, and a list is rejected. Keep a swept config separate from the single-valued one you run in production.

Each swept parameter becomes a column in a sweep table, one table per match criterion, with every combination as a row sorted by that criterion's bout F1:

Postprocessing sweep - overlap >= 1 (sorted by bout F1)
 max_stitch_gap  min_duration | bout F1  detect  precision  frag  merged  frame F1
             45            10 |  0.734    0.841      0.651     2       4     0.579
             30            10 |  0.721*   0.825      0.640     3       1     0.588
             15            10 |  0.689    0.794      0.608     7       0     0.571
             ...

Postprocessing sweep - IoU >= 0.5 (sorted by bout F1)
 max_stitch_gap  min_duration | bout F1  detect  precision  frag  merged  frame F1
             30            10 |  0.612*   0.714      0.536     3       1     0.588
             45            10 |  0.601    0.730      0.511     2       4     0.579
             15            10 |  0.564    0.667      0.489     7       0     0.571
             ...

Each table is sorted by bout F1 under its own criterion. * marks the one combination
the detailed tables below describe, chosen by bout F1 under IoU >= 0.5 (the strictest
criterion) with frame F1 breaking ties - so it is not necessarily the top row of every
table.

The * is a single global choice, not one per table. It lands on the top row of the strictest criterion's table by construction, but not necessarily anywhere else: above, max_stitch_gap: 45 wins under overlap >= 1, while 30 is the combination the detailed sections go on to describe.

Axes are taken only from enabled stages, so a list on a disabled stage adds no combinations. A grid larger than --max-sweep-combinations is refused before any classification happens, naming the axis sizes.

Two things narrow when sweeping, because they have no single answer across a grid:

  • The detailed frame-level and bout-level tables cover the raw predictions plus the one combination marked *, chosen by bout F1 under the strictest criterion with frame F1 breaking ties. The sweep tables carry every combination, and so does the JSON summary.
  • --save-predictions writes the raw predictions only, with no postprocessed dataset. Re-run with your chosen values as scalars to save a postprocessed file.

Setting the value ranges is easier from evidence than from intuition. Run once with no postprocessing and --csv-out, then look at the ground-truth bout durations (the floor for min_duration) and the gaps between consecutive predicted bouts that overlap the same true bout (the range for max_stitch_gap). Note that the cost of min_duration is only interpretable after stitching: fragments that a duration filter would delete on their own survive once stitching has merged them into one bout, which is why the stage order in the config matters.

Saving predictions

--save-predictions DIR writes the predictions themselves, one HDF5 file per video named <pose stem>_behavior.h5, in the same format jabs-classify classify produces. Classifying a project is the expensive part of this command, so saving lets you reuse the result:

# classify once, keeping the predictions
jabs-cli evaluate /path/to/dense_project --classifier grooming.pickle \
    --save-predictions predictions/

# then iterate on postprocessing without recomputing features
jabs-cli postprocess predictions/video_behavior.h5 --config pipeline.yaml \
    --behavior grooming

Notes on what gets written:

  • The saved vectors cover the whole pose file, not just the labeled region. If the labels and the pose file disagree on frame count, the comparison is truncated but the saved file is not.
  • Identities that could not be classified keep a no-prediction value (-1) for every frame, rather than being written as not-behavior.
  • A video where no identity could be classified is not written at all, so an all-unscored file never appears.
  • When --postprocess-config is also given, the file carries both the raw and the postprocessed predictions, the same as a file written by jabs-cli postprocess.
  • If a file cannot be written, the failure is reported at the end of the run and in the output files, but the evaluation itself still completes and its metrics are still printed and saved.

Limitations

  • Binary classifiers only. Bout comparison treats one class as the behavior and everything else as background, which has no meaning for multi-class predictions. A multi-class classifier is rejected with an error. This matches jabs-cli postprocess, which is also binary-only.
  • Videos that cannot be opened, have no annotations, or whose frame rate cannot be read are skipped and listed at the end of the report rather than aborting the run.
  • If a video's label count and pose frame count disagree, the comparison is truncated to the shorter of the two and a warning is logged.