Skip to content

Commit 5ec062b

Browse files
gitttt-1234claude
andauthored
Append video name to output path when video_index is specified (#378)
## Summary Fixes an issue where running inference with `video_index` on multiple videos from the same .slp file would overwrite the output file. The output path now includes the video name to create unique filenames for each video. ## Problem When running predictions on a multi-video .slp file using the `video_index` parameter: - All predictions would save to the same output path (e.g., `labels.predictions.slp`) - Running inference on different videos would overwrite previous results - Users had to manually specify unique `output_path` for each video ## Solution When `video_index` is provided and `output_path` is None: - Extract the video filename from `output.videos[video_index]` - Append the video name (stem) to the output path - Format: `<labels_file>.<video_name>.predictions.slp` - Falls back to `video_{index}` if filename is not a simple string ## Example **Before:** ```python run_inference(data_path="labels.slp", video_index=0) # → labels.predictions.slp run_inference(data_path="labels.slp", video_index=1) # → labels.predictions.slp (overwrites!) ``` **After:** ```python run_inference(data_path="labels.slp", video_index=0) # video1.mp4 → labels.video1.predictions.slp run_inference(data_path="labels.slp", video_index=1) # video2.mp4 → labels.video2.predictions.slp ``` ## Changes - Modified `run_inference()` in `sleap_nn/predict.py` to append video name when `video_index` is specified - Added `test_video_index_output_path()` test to verify the behavior ## Testing - New test verifies video name is included in output path when using video_index - Existing tests continue to pass (no breaking changes) - Behavior unchanged when video_index is None or output_path is explicitly provided 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <[email protected]> --------- Co-authored-by: Claude <[email protected]>
1 parent 558e0a9 commit 5ec062b

File tree

2 files changed

+55
-3
lines changed

2 files changed

+55
-3
lines changed

sleap_nn/predict.py

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -504,9 +504,23 @@ def run_inference(
504504

505505
if make_labels:
506506
if output_path is None:
507-
output_path = Path(
508-
data_path if data_path is not None else "results"
509-
).with_suffix(".predictions.slp")
507+
base_path = Path(data_path if data_path is not None else "results")
508+
509+
# If video_index is specified, append video name to output path
510+
if video_index is not None and len(output.videos) > video_index:
511+
video = output.videos[video_index]
512+
# Get video filename and sanitize it for use in path
513+
video_name = (
514+
Path(video.filename).stem
515+
if isinstance(video.filename, str)
516+
else f"video_{video_index}"
517+
)
518+
# Insert video name before .predictions.slp extension
519+
output_path = (
520+
base_path.parent / f"{base_path.stem}.{video_name}.predictions.slp"
521+
)
522+
else:
523+
output_path = base_path.with_suffix(".predictions.slp")
510524
output.save(Path(output_path).as_posix(), restore_original_videos=False)
511525
finish_timestamp = str(datetime.now())
512526
logger.info(f"Predictions output path: {output_path}")

tests/test_predict.py

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1423,3 +1423,41 @@ def test_topdown_predictor_with_tracking_cleaning(
14231423
device="cpu" if torch.backends.mps.is_available() else "auto",
14241424
)
14251425
assert len(labels) < 10
1426+
1427+
1428+
def test_video_index_output_path(
1429+
minimal_instance,
1430+
minimal_instance_centered_instance_ckpt,
1431+
minimal_instance_centroid_ckpt,
1432+
tmp_path,
1433+
):
1434+
"""Test that video_index appends video name to output path."""
1435+
# Run inference with video_index and no explicit output_path
1436+
labels = run_inference(
1437+
model_paths=[
1438+
minimal_instance_centroid_ckpt,
1439+
minimal_instance_centered_instance_ckpt,
1440+
],
1441+
data_path=minimal_instance.as_posix(),
1442+
video_index=0,
1443+
make_labels=True,
1444+
device="cpu",
1445+
peak_threshold=0.0,
1446+
integral_refinement=None,
1447+
)
1448+
1449+
# Check that output file was created with video name in path
1450+
expected_pattern = f"*minimal_instance*.predictions.slp"
1451+
output_files = list(Path(minimal_instance).parent.glob(expected_pattern))
1452+
assert len(output_files) > 0, "No output file found with video name in path"
1453+
1454+
# The output file should contain the video name
1455+
output_file = output_files[0]
1456+
video_name = Path(labels.videos[0].filename).stem
1457+
assert (
1458+
video_name in output_file.stem
1459+
), f"Video name '{video_name}' not found in output path '{output_file}'"
1460+
1461+
# Clean up
1462+
for f in output_files:
1463+
f.unlink()

0 commit comments

Comments
 (0)