Zion Boggan
repos/Pitch Tracker CV/tools/burst.py
zionboggan.com ↗
72 lines · python
History for this file →
1
"""Burst snapshot: capture N frames over T seconds, save each as PNG.
2
 
3
Useful for catching the ball in flight and getting a clean PCI frame for
4
tuning / template extraction. Saves to logs/burst_NN_tXXXXXXms.png.
5
"""
6
from __future__ import annotations
7
 
8
import argparse
9
import shutil
10
import sys
11
import time
12
from pathlib import Path
13
 
14
import cv2
15
from rich.console import Console
16
 
17
sys.path.insert(0, str(Path(__file__).resolve().parents[1]))
18
from capture.ingest import open_capture
19
from cv._common import load_config
20
 
21
console = Console()
22
OUT_DIR = Path(__file__).resolve().parents[1] / "logs" / "burst"
23
 
24
def main() -> int:
25
    ap = argparse.ArgumentParser(description="Burst frame capture.")
26
    ap.add_argument("--count", type=int, default=30, help="Total frames to save.")
27
    ap.add_argument("--duration", type=float, default=10.0, help="Spread frames over this many seconds.")
28
    ap.add_argument("--clear", action="store_true", help="Wipe logs/burst before starting.")
29
    args = ap.parse_args()
30
 
31
    if args.clear and OUT_DIR.exists():
32
        shutil.rmtree(OUT_DIR)
33
    OUT_DIR.mkdir(parents=True, exist_ok=True)
34
 
35
    cfg = load_config()
36
    cap = open_capture(cfg)
37
    if cap is None or not cap.isOpened():
38
        console.print("[red]Could not open capture card.[/red]")
39
        return 2
40
 
41
    for _ in range(5):
42
        cap.read()
43
 
44
    interval = args.duration / max(args.count - 1, 1)
45
    t0 = time.perf_counter()
46
    console.print(
47
        f"[bold cyan]Burst: {args.count} frames over {args.duration:.1f}s "
48
        f"(every {interval*1000:.0f}ms) -> {OUT_DIR}[/bold cyan]"
49
    )
50
 
51
    saved = 0
52
    for i in range(args.count):
53
        target = t0 + i * interval
54
 
55
        while time.perf_counter() < target - 0.002:
56
            cap.read()
57
        ok, frame = cap.read()
58
        if not ok or frame is None:
59
            continue
60
        elapsed_ms = int((time.perf_counter() - t0) * 1000)
61
        out = OUT_DIR / f"burst_{i:02d}_t{elapsed_ms:05d}ms.png"
62
        cv2.imwrite(str(out), frame)
63
        saved += 1
64
        if i % 5 == 0:
65
            console.print(f"  [{i+1}/{args.count}] t={elapsed_ms}ms  -> {out.name}")
66
 
67
    cap.release()
68
    console.print(f"[green]Saved {saved}/{args.count} frames to {OUT_DIR}[/green]")
69
    return 0
70
 
71
if __name__ == "__main__":
72
    sys.exit(main())