Zion Boggan
repos/Pitch Tracker CV/capture/diagnose.py
zionboggan.com ↗
105 lines · python
History for this file →
1
"""Per-device diagnostic: probes each video index, reports real FOURCC/size/brightness."""
2
from __future__ import annotations
3
 
4
import sys
5
import time
6
from pathlib import Path
7
 
8
import cv2
9
import numpy as np
10
from rich.console import Console
11
 
12
console = Console()
13
LOG_DIR = Path(__file__).resolve().parents[1] / "logs"
14
 
15
def fourcc_to_str(val: float) -> str:
16
    v = int(val)
17
    if v <= 0:
18
        return "NONE"
19
    return "".join(chr((v >> (8 * i)) & 0xFF) for i in range(4))
20
 
21
def probe(idx: int, target_w: int = 1920, target_h: int = 1080, target_fps: int = 60) -> dict:
22
    result: dict = {"index": idx, "opened": False}
23
    cap = cv2.VideoCapture(idx, cv2.CAP_DSHOW)
24
    if not cap.isOpened():
25
        cap.release()
26
        return result
27
    result["opened"] = True
28
 
29
    result["default_size"] = (int(cap.get(cv2.CAP_PROP_FRAME_WIDTH)), int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT)))
30
    result["default_fourcc"] = fourcc_to_str(cap.get(cv2.CAP_PROP_FOURCC))
31
    result["default_fps"] = cap.get(cv2.CAP_PROP_FPS)
32
 
33
    mjpg = cv2.VideoWriter_fourcc(*"MJPG")
34
    cap.set(cv2.CAP_PROP_FOURCC, mjpg)
35
    cap.set(cv2.CAP_PROP_FRAME_WIDTH, target_w)
36
    cap.set(cv2.CAP_PROP_FRAME_HEIGHT, target_h)
37
    cap.set(cv2.CAP_PROP_FPS, target_fps)
38
 
39
    result["set_size"] = (int(cap.get(cv2.CAP_PROP_FRAME_WIDTH)), int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT)))
40
    result["set_fourcc"] = fourcc_to_str(cap.get(cv2.CAP_PROP_FOURCC))
41
    result["set_fps"] = cap.get(cv2.CAP_PROP_FPS)
42
 
43
    frame_count = 0
44
    first: np.ndarray | None = None
45
    mean_brightness: list[float] = []
46
    t_start = time.perf_counter()
47
    deadline = t_start + 3.0
48
    while time.perf_counter() < deadline:
49
        ok, frame = cap.read()
50
        if not ok or frame is None:
51
            continue
52
        if first is None:
53
            first = frame.copy()
54
        mean_brightness.append(float(frame.mean()))
55
        frame_count += 1
56
    elapsed = time.perf_counter() - t_start
57
    cap.release()
58
 
59
    result["frames"] = frame_count
60
    result["elapsed"] = elapsed
61
    result["fps"] = frame_count / elapsed if elapsed > 0 else 0.0
62
    if first is not None:
63
        result["frame_shape"] = first.shape
64
        result["brightness_mean"] = float(np.mean(mean_brightness)) if mean_brightness else 0.0
65
        result["brightness_std"] = float(np.std(mean_brightness)) if mean_brightness else 0.0
66
        out = LOG_DIR / f"diag_device_{idx}.png"
67
        cv2.imwrite(str(out), first)
68
        result["saved"] = str(out)
69
    return result
70
 
71
def main() -> int:
72
    LOG_DIR.mkdir(parents=True, exist_ok=True)
73
    console.print("[bold cyan]Per-device diagnostic (each opens/probes for 3s)[/bold cyan]\n")
74
    for idx in range(0, 4):
75
        r = probe(idx)
76
        console.print(f"[bold]Device {idx}[/bold]")
77
        if not r["opened"]:
78
            console.print("  [dim]not openable[/dim]\n")
79
            continue
80
        console.print(
81
            f"  default: {r['default_size'][0]}x{r['default_size'][1]} "
82
            f"fourcc={r['default_fourcc']} fps={r['default_fps']:.1f}"
83
        )
84
        console.print(
85
            f"  after set MJPG+1080p60: {r['set_size'][0]}x{r['set_size'][1]} "
86
            f"fourcc={r['set_fourcc']} fps={r['set_fps']:.1f}"
87
        )
88
        if "frame_shape" in r:
89
            console.print(
90
                f"  captured {r['frames']} frames in {r['elapsed']:.2f}s = [bold]{r['fps']:.2f} FPS[/bold]  "
91
                f"shape={r['frame_shape']}"
92
            )
93
            b = r["brightness_mean"]
94
            std = r["brightness_std"]
95
            tag = "[red]ALL BLACK (no signal or HDCP block)[/red]" if b < 3.0 else (
96
                  "[yellow]very dim[/yellow]" if b < 20.0 else "[green]normal[/green]")
97
            console.print(f"  brightness: mean={b:.2f} std={std:.2f}  {tag}")
98
            console.print(f"  saved first frame: {r['saved']}")
99
        else:
100
            console.print("  [red]opened but produced no frames[/red]")
101
        console.print("")
102
    return 0
103
 
104
if __name__ == "__main__":
105
    sys.exit(main())