Zion Boggan
repos/Pitch Tracker CV/tools/snapshot.py
zionboggan.com ↗
86 lines · python
History for this file →
1
"""One-shot detection snapshot.
2
 
3
Opens the capture card directly (bypasses ZMQ), grabs one frame, runs the
4
ball and PCI detectors, and saves an annotated PNG to logs/snapshot.png.
5
Useful for eyeballing what the current HSV ranges actually pick up on
6
whatever screen the Xbox is showing right now.
7
"""
8
from __future__ import annotations
9
 
10
import sys
11
import time
12
from pathlib import Path
13
 
14
import cv2
15
import numpy as np
16
from rich.console import Console
17
 
18
sys.path.insert(0, str(Path(__file__).resolve().parents[1]))
19
from capture.ingest import open_capture
20
from cv._common import load_config
21
from cv.ball_tracker import detect_ball
22
from cv.pci_tracker import detect_by_hsv as pci_detect
23
 
24
console = Console()
25
OUT_DIR = Path(__file__).resolve().parents[1] / "logs"
26
 
27
def main() -> int:
28
    OUT_DIR.mkdir(parents=True, exist_ok=True)
29
    cfg = load_config()
30
    cap = open_capture(cfg)
31
    if cap is None or not cap.isOpened():
32
        console.print("[red]Could not open capture card.[/red]")
33
        return 2
34
 
35
    for _ in range(5):
36
        cap.read()
37
        time.sleep(0.02)
38
 
39
    ok, frame = cap.read()
40
    cap.release()
41
    if not ok or frame is None:
42
        console.print("[red]Capture returned no frame.[/red]")
43
        return 2
44
 
45
    raw_out = OUT_DIR / "snapshot_raw.png"
46
    cv2.imwrite(str(raw_out), frame)
47
    console.print(f"[green]Saved raw  -> {raw_out}[/green]")
48
 
49
    overlay = frame.copy()
50
    h, w = frame.shape[:2]
51
    plate_y = int(h * float(cfg["cv"].get("plate_y_frac", 0.72)))
52
    cv2.line(overlay, (0, plate_y), (w, plate_y), (255, 255, 255), 1, cv2.LINE_AA)
53
 
54
    ball = detect_ball(frame, cfg)
55
    if ball is not None:
56
        cv2.circle(overlay, (int(ball.x), int(ball.y)), max(int(ball.r), 4), (0, 255, 255), 2)
57
        cv2.putText(
58
            overlay,
59
            f"ball r={ball.r:.0f} s={ball.score:.2f}",
60
            (int(ball.x) + 10, int(ball.y) - 10),
61
            cv2.FONT_HERSHEY_SIMPLEX, 0.6, (0, 255, 255), 2, cv2.LINE_AA,
62
        )
63
        console.print(f"[bold]ball:[/bold] x={ball.x:.0f} y={ball.y:.0f} r={ball.r:.1f} score={ball.score:.2f}")
64
    else:
65
        console.print("[yellow]ball: none[/yellow]")
66
 
67
    pci = pci_detect(frame, cfg["cv"]["pci"])
68
    if pci is not None:
69
        cv2.circle(overlay, (int(pci["x"]), int(pci["y"])), int(pci["r"]), (0, 255, 0), 2)
70
        cv2.putText(
71
            overlay,
72
            f"pci r={pci['r']:.0f}",
73
            (int(pci["x"]) + 10, int(pci["y"]) + 20),
74
            cv2.FONT_HERSHEY_SIMPLEX, 0.6, (0, 255, 0), 2, cv2.LINE_AA,
75
        )
76
        console.print(f"[bold]pci:[/bold]  x={pci['x']:.0f} y={pci['y']:.0f} r={pci['r']:.1f} score={pci['score']:.2f}")
77
    else:
78
        console.print("[yellow]pci:  none[/yellow]")
79
 
80
    out = OUT_DIR / "snapshot.png"
81
    cv2.imwrite(str(out), overlay)
82
    console.print(f"[green]Saved -> {out}[/green]")
83
    return 0
84
 
85
if __name__ == "__main__":
86
    sys.exit(main())