Skip to content

Instantly share code, notes, and snippets.

@Breakthrough
Created May 21, 2026 01:03
Show Gist options
  • Select an option

  • Save Breakthrough/68c90853d54cabf98b0a467ea32e630c to your computer and use it in GitHub Desktop.

Select an option

Save Breakthrough/68c90853d54cabf98b0a467ea32e630c to your computer and use it in GitHub Desktop.
"""
Parallel chunked scene detection experiment.
Each thread opens the video independently, seeks to its chunk, and runs detection.
Chunks are spaced every 30s with a small overlap so cuts near boundaries aren't missed.
> time python parallel.py
Video: 82.6s @ 24000/1001fps -> 3 chunks, 4 workers
chunk 60s- end -> 5 cuts (541 frames @ 411.5 fps)
chunk 0s- 32.0s -> 5 cuts (767 frames @ 440.0 fps)
chunk 30s- 62.0s -> 11 cuts (767 frames @ 444.9 fps)
Total: 2075 frames in 1.80s = 1154.3 fps (wall clock)
Total unique cuts: 21
00:00:03.754
00:00:08.759
00:00:10.802
00:00:15.599
00:00:27.110
00:00:34.117
00:00:36.536
00:00:42.501
00:00:44.002
00:00:45.837
00:00:48.966
00:00:51.134
00:00:52.553
00:00:53.428
00:00:55.639
00:00:56.932
00:01:06.316
00:01:10.779
00:01:18.036
00:01:19.913
00:01:21.999
real 0m2.272s
user 0m0.045s
sys 0m0.015s
(PySceneDetect)
"""
import time
from concurrent.futures import ThreadPoolExecutor, as_completed
from scenedetect import open_video, SceneManager, AdaptiveDetector, FrameTimecode
VIDEO_PATH = "tests/resources/goldeneye.mp4"
CHUNK_SECONDS = 30.0
OVERLAP_SECONDS = 2.0 # each chunk extends this far past its nominal end
MAX_WORKERS = 4
def detect_chunk(
path: str,
start_sec: float,
end_sec: float | None,
) -> tuple[list[FrameTimecode], int, float]:
video = open_video(path)
fps = video.frame_rate
video.seek(FrameTimecode(start_sec, fps))
manager = SceneManager()
manager.add_detector(AdaptiveDetector())
duration = None if end_sec is None else FrameTimecode(end_sec - start_sec, fps)
t0 = time.perf_counter()
frames = manager.detect_scenes(video, duration=duration, show_progress=False)
elapsed = time.perf_counter() - t0
scenes = manager.get_scene_list()
cuts = [start for start, _ in scenes[1:]]
return cuts, frames, elapsed
def main():
# Open once just to read duration + fps, then close.
probe = open_video(VIDEO_PATH)
fps = probe.frame_rate
total_sec = probe.duration.seconds if probe.duration is not None else 0.0
del probe
starts = [s * CHUNK_SECONDS for s in range(int(total_sec / CHUNK_SECONDS) + 1)]
chunks = [
(
s,
min(s + CHUNK_SECONDS + OVERLAP_SECONDS, total_sec)
if s + CHUNK_SECONDS < total_sec
else None,
)
for s in starts
]
print(f"Video: {total_sec:.1f}s @ {fps}fps -> {len(chunks)} chunks, {MAX_WORKERS} workers")
all_cuts: list[FrameTimecode] = []
total_frames = 0
wall_start = time.perf_counter()
with ThreadPoolExecutor(max_workers=MAX_WORKERS) as pool:
futures = {pool.submit(detect_chunk, VIDEO_PATH, s, e): (s, e) for s, e in chunks}
for fut in as_completed(futures):
s, e = futures[fut]
cuts, frames, elapsed = fut.result()
chunk_fps = frames / elapsed if elapsed > 0 else 0.0
print(
f" chunk {s:.0f}s-{(str(e) + 's') if e else 'end':>8s} -> {len(cuts)} cuts ({frames} frames @ {chunk_fps:.1f} fps)"
)
all_cuts.extend(cuts)
total_frames += frames
wall_elapsed = time.perf_counter() - wall_start
avg_fps = total_frames / wall_elapsed if wall_elapsed > 0 else 0.0
print(f"\nTotal: {total_frames} frames in {wall_elapsed:.2f}s = {avg_fps:.1f} fps (wall clock)")
# Deduplicate and sort (overlap means the same cut can appear in two chunks).
seen: set[int] = set()
unique_cuts = []
for cut in sorted(all_cuts, key=lambda c: c.frame_num):
if cut.frame_num not in seen:
seen.add(cut.frame_num)
unique_cuts.append(cut)
print(f"Total unique cuts: {len(unique_cuts)}")
for cut in unique_cuts:
print(f" {cut}")
if __name__ == "__main__":
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment