Skip to content

Clipping Pipeline

This page documents how to use scripts/clipping/clipping.py to reproduce short annotated clips from long-form source videos using JSON event metadata produced by the detection pipeline.


Summary

The clipping pipeline reads JSON event metadata produced by any stage of the detection pipeline (baseline, fine-tuned YOLO, or Seq-NMS) and reproduces the corresponding video segments as standalone annotated clips with per-frame bounding box overlays. All clips are written to results/result_clips/, preserving the input directory structure relative to the metadata/ folder.


Environment Setup

Before running the clipping script, ensure your local workspace satisfies the following:

  • .env is populated with ROOT_DIR and LOCAL_DIR (used by config.py).
  • Source videos exist under ROOT_DIR/raw_cross_sucking_datalog/.
  • JSON metadata files exist under LOCAL_DIR/results/metadata/ produced by the detection pipeline.

Output Structure

results/result_clips/
├── pipeline_demo/
│   ├── yolo/
│   │   └── ch02_20250913094601/
│   │       ├── ch02_20250913094601_event001_12.3-16.8_boxed.mp4
│   │       └── ch02_20250913094601_event002_22.1-25.4_boxed.mp4
│   └── baseline/
│       └── ch02_20250913094601/
│           └── ch02_20250913094601_event001_12.3-16.8_boxed.mp4

Usage

Clip from a single JSON file

uv run scripts/clipping/clipping.py \
  --input results/metadata/pipeline_demo/yolo/ch02_20250913094601_results.json

Clip from a directory of JSON files

uv run scripts/clipping/clipping.py \
  --input results/metadata/pipeline_demo/yolo/

Clip to a custom output directory

uv run scripts/clipping/clipping.py \
  --input results/metadata/pipeline_demo/yolo/ \
  --output /tmp/clips/

Specifying a different model output

The output path mirrors the input path relative to metadata/. To clip results from a different model, pass the corresponding metadata directory:

uv run scripts/clipping/clipping.py \
  --input results/metadata/pipeline_demo/seq-nms/

Function Reference

scripts.clipping.clipping

Reproduce short annotated video clips from longer source videos.

reproduce_clip(raw_video_path, start_sec, end_sec, output_path)

Reproduce a clip from a source video between two timestamps.

Parameters:

Name Type Description Default
raw_video_path Path

Path to the source .mp4 file.

required
start_sec float

Start time in seconds in the source video.

required
end_sec float

End time in seconds in the source video.

required
output_path Path

Destination path for the reproduced clip.

required

Returns:

Type Description
bool

True if the clip was written successfully; False if the source video could not be opened or frames could not be read.

Notes
  • Uses codec mp4v via OpenCV.
  • Output frame size and FPS are taken from the source video.
  • This function does not create output_path.parent; callers should ensure the directory exists before calling.

Examples:

.. code-block:: python

from pathlib import Path
from scripts.clipping.clipping import reproduce_clip

ok = reproduce_clip(Path("source.mp4"), 10.0, 12.5, Path("clip.mp4"))
print(ok)  # True

split_by_json_events(json_path, output_dir)

Reproduce annotated clips for all events defined in JSON metadata files.

Recursively searches json_path for *.json files produced by the detection pipeline. For each event in each JSON, reproduces a clip from the source video and writes an annotated *_boxed.mp4 with bounding boxes overlaid. The output directory structure mirrors the input path relative to the metadata/ folder.

Parameters:

Name Type Description Default
json_path Path

A single JSON file, or a root directory to search recursively for *.json files produced by the detection pipeline.

required
output_dir Path

Output root directory (e.g. RESULT_CLIPS_DIR).

required

Returns:

Type Description
int

Total number of successfully reproduced clips across all JSON files.

Raises:

Type Description
FileNotFoundError

If a JSON references a video_path that does not exist on disk.

Notes

Expected JSON schema (minimum):

  • video_path : str
  • events : list of dict with keys start_sec, end_sec

Optional keys used if present:

  • identifier : str — used as the per-video output folder name
  • fps : float — used for annotation frame alignment
  • events[*].intersection_box : list of dict with keys frame, x1, y1, x2, y2 in source-video frame coordinates

Examples:

.. code-block:: python

from pathlib import Path
from scripts.clipping.clipping import split_by_json_events
from config import RESULT_CLIPS_DIR

n = split_by_json_events(
    Path("results/metadata/pipeline_demo/yolo/"),
    RESULT_CLIPS_DIR,
)
print(f"{n} clips written")

annotate_clip_with_boxes(input_clip, output_clip, boxes, color=(0, 255, 0), thickness=2)

Annotate a clip with per-frame bounding boxes and write a new video.

Parameters:

Name Type Description Default
input_clip Path

Path to the input clip (.mp4).

required
output_clip Path

Path where the annotated clip will be written.

required
boxes list of dict

Bounding boxes in clip-relative frame coordinates. Each dict must include: frame, x1, y1, x2, y2.

required
color tuple of int

Rectangle colour in BGR.

(0, 255, 0)
thickness int

Rectangle line thickness in pixels.

2

Returns:

Type Description
bool

True if annotation succeeded; False if the clip could not be opened.

Notes
  • Creates output_clip.parent if it does not exist.
  • Supports multiple boxes per frame; boxes are grouped by frame index before rendering.

Examples:

.. code-block:: python

from pathlib import Path
from scripts.clipping.clipping import annotate_clip_with_boxes

boxes = [{"frame": 0, "x1": 100, "y1": 50, "x2": 200, "y2": 150}]
ok = annotate_clip_with_boxes(
    Path("clip.mp4"), Path("clip_boxed.mp4"), boxes
)
print(ok)  # True