Skip to content

Shot Based Segmenter

Content-based shot segmenter without OpenCV / FFmpeg-Python dependencies.

Frame decoding is delegated to the backend-swappable FrameDecodeProcessor family (backend="gstreamer" decodes with system GStreamer plugins, backend="ffmpeg" shells out to the system ffmpeg binary). Shot scoring uses mean absolute HSV-plane distance between adjacent frames on numpy + scikit-image (skimage.color.rgb2hsv), with an adaptive neighbour-ratio detector implemented in pure numpy.

FIPS note: with the default GStreamer FrameDecodeProcessor backend, this segmenter never imports cv2 and never loads the FFmpeg blobs bundled in opencv-python / PyAV wheels. The scoring dependencies (numpy + scikit-image) ship in the existing video-ffmpeg / video-gst extras; GStreamer decoding needs system GStreamer (video-gst extra). Switch decode backends via the composite backend preference or set_processor_backend("FrameDecodeProcessor", ...).

Detector semantics:

  • content — cut when the HSV distance score >= threshold (default 27.0; planes are scaled to 0–255 before the mean-absolute-difference).
  • adaptive — cut when score / neighbour_mean >= threshold and score >= min_content_val. Neighbours are window_width frames on each side of the current frame (current frame excluded). threshold is the adaptive ratio.
  • threshold — cut when the absolute change in mean frame brightness (raw RGB mean, matching PySceneDetect ThresholdDetector) >= threshold. HSV conversion is skipped in this mode.

Assumes a constant framerate (sample-caps framerate, 30.0 fallback).

Detector

Bases: StrEnum

Supported shot-detector algorithm names.

ShotBasedSegmenter(detector=Detector.CONTENT, threshold=27.0, min_shot_duration=1.0, min_content_val=15.0, window_width=2, sample_fps=5, target_width=320)

Bases: BaseSegmenter

Segment video into shots with swappable decode + numpy/skimage scoring.

Frame decoding is delegated to the nested FrameDecodeProcessor family (GStreamer by default; override via composite backend or set_processor_backend); shot scoring runs on numpy + scikit-image.

Attributes:

Name Type Description
detector Detector

content | adaptive | threshold.

_config _ShotBasedSegmenterConfig

Validated scoring and decode knobs.

Initialize the FIPS-friendly content segmenter.

Parameters:

Name Type Description Default
detector Detector | str

Detector algorithm. Defaults to content.

CONTENT
threshold float

Detector threshold. For adaptive this is the neighbour ratio. Defaults to 27.0.

27.0
min_shot_duration float

Minimum shot length in seconds. Defaults to 1.0.

1.0
min_content_val float

adaptive-only absolute score floor. Defaults to 15.0.

15.0
window_width int

adaptive-only neighbour half-window in frames (per side). Defaults to 2.

2
sample_fps int | None

Decode-time sampling rate. Defaults to 5. Pass None for native framerate.

5
target_width int | None

Downscale decode width. Defaults to 320. Pass None for native resolution.

320

Raises:

Type Description
ValueError

If detector is not a supported algorithm name.

ValidationError

If scoring / decode knobs fail _ShotBasedSegmenterConfig.

ImportError

If numpy / scikit-image / Pillow are not installed.

adaptive_cut_indices(scores, *, adaptive_threshold, min_content_val, min_scene_len, window_width)

Cut on adaptive ratio of the score against its neighbour mean.

For each frame i, the neighbour mean is the average of window_width scores on each side (current frame excluded). A cut requires score / neighbour_mean >= adaptive_threshold and score >= min_content_val.

Parameters:

Name Type Description Default
scores list[float]

Per-frame content scores.

required
adaptive_threshold float

Required ratio above the neighbour mean.

required
min_content_val float

Minimum absolute content score.

required
min_scene_len int

Minimum cut spacing in frames.

required
window_width int

Neighbour half-window in frames (per side).

required

Returns:

Type Description
list[int]

list[int]: Cut frame indices.

brightness_cut_indices(brightness, *, threshold, min_scene_len)

Cut on large frame-to-frame brightness changes (brightness-delta).

Parameters:

Name Type Description Default
brightness list[float]

Per-frame mean brightness values.

required
threshold float

Cut when abs(mean[i] - mean[i-1]) >= threshold.

required
min_scene_len int

Minimum cut spacing in frames.

required

Returns:

Type Description
list[int]

list[int]: Cut frame indices.

content_cut_indices(scores, *, threshold, min_scene_len)

Cut where the content score meets threshold, spaced by min_scene_len.

Parameters:

Name Type Description Default
scores list[float]

Per-frame content scores.

required
threshold float

Cut threshold (compared with >=).

required
min_scene_len int

Minimum cut spacing in frames.

required

Returns:

Type Description
list[int]

list[int]: Cut frame indices.

enforce_min_scene_len(indices, min_scene_len)

Drop cut indices closer than min_scene_len frames to the kept cut.

Parameters:

Name Type Description Default
indices list[int]

Candidate cut frame indices in ascending order.

required
min_scene_len int

Minimum spacing in frames.

required

Returns:

Type Description
list[int]

list[int]: Filtered cut indices (first candidate always kept).

mean_pixel_distance(left, right)

Mean absolute per-pixel distance between two equally shaped planes.

Parameters:

Name Type Description Default
left Any

First 2D plane (numpy array).

required
right Any

Second 2D plane (numpy array, same shape as left).

required

Returns:

Name Type Description
float float

mean(abs(left - right)) computed in int32 space.

rgb_frame_to_hsv_planes(frame)

Convert an RGB uint8 frame to H/S/V planes scaled to 0–255.

Parameters:

Name Type Description Default
frame Any

HxWx3 uint8 RGB numpy array.

required

Returns:

Type Description
tuple[Any, Any, Any]

tuple[Any, Any, Any]: (hue, sat, lum) float planes in 0–255. The 0–255 scale keeps threshold values on a familiar 8-bit range.

score_frame_stream(frames, *, include_content=True)

Score a stream of RGB frames in one pass with O(1) frame memory.

This bounds Python RAM for frames, not decoder disk usage. The decode path still materialises the full sampled PNG sequence before the first yield.

When include_content is True, each frame is converted to HSV and scored with mean-absolute plane distance. When False (threshold detector), brightness is the raw RGB mean and HSV work is skipped.

Parameters:

Name Type Description Default
frames Iterable[Any]

RGB uint8 frames in decode order.

required
include_content bool

Compute HSV content scores. Defaults to True.

True

Returns:

Type Description
tuple[list[float], list[float]]

tuple[list[float], list[float]]: (content_scores, brightness). content_scores is empty when include_content is False.