Created
May 21, 2026 01:03
-
-
Save Breakthrough/68c90853d54cabf98b0a467ea32e630c to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| """ | |
| 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