feat(cli): atm debug — one-shot capture + detect + annotated PNG

Quick sanity check after calibration. Prints window/dot detection state,
classified colour + confidence, saves full/cropped/annotated frames to logs/.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Claude Agent
2026-04-16 06:45:01 +00:00
parent 562df0c395
commit f43f5ce93b

View File

@@ -62,6 +62,9 @@ def main(argv=None) -> None:
metavar="PATH", help="Journal JSONL file (default: trades.jsonl)",
)
# debug
sub.add_parser("debug", help="Capture one frame, run detector, annotate + print result")
# report
p_report = sub.add_parser("report", help="Print weekly performance report")
p_report.add_argument(
@@ -80,6 +83,7 @@ def main(argv=None) -> None:
"label": _cmd_label,
"dryrun": _cmd_dryrun,
"run": _cmd_run,
"debug": _cmd_debug,
"journal": _cmd_journal,
"report": _cmd_report,
}
@@ -140,6 +144,72 @@ def _cmd_journal(args) -> None:
j.add(entry)
def _cmd_debug(args) -> None:
"""One-shot capture + detect. Saves annotated PNG. Prints classification."""
from datetime import datetime
try:
from atm.detector import Detector
from atm.vision import crop_roi
except ImportError as exc:
sys.exit(f"debug module deps missing: {exc}")
cfg = Config.load_current(Path("configs"))
capture = _build_capture(cfg)
frame = capture()
if frame is None:
sys.exit("capture returned None (window not found or region invalid)")
out_dir = Path("logs")
out_dir.mkdir(exist_ok=True)
ts = datetime.now().strftime("%Y%m%d_%H%M%S")
import cv2 # type: ignore[import-untyped]
full_path = out_dir / f"debug_full_{ts}.png"
cv2.imwrite(str(full_path), frame)
print(f"captured frame: {frame.shape}{full_path}")
# crop dot_roi + run detector
try:
dot_crop = crop_roi(frame, cfg.dot_roi)
dot_path = out_dir / f"debug_dot_roi_{ts}.png"
cv2.imwrite(str(dot_path), dot_crop)
print(f"dot_roi crop: {dot_crop.shape}{dot_path}")
except Exception as exc:
sys.exit(f"dot_roi crop failed: {exc}")
det = Detector(cfg, capture)
res = det.step(0.0)
print("---")
print(f"window_found: {res.window_found}")
print(f"dot_found: {res.dot_found}")
if res.match is not None:
print(f"rgb: {res.rgb}")
print(f"classified: {res.match.name} distance={res.match.distance:.1f} confidence={res.match.confidence:.2f}")
else:
print("no match (dot not found or UNKNOWN classification)")
print(f"accepted: {res.accepted} color={res.color}")
# Annotate: draw dot_roi rect + mark detected dot
annotated = frame.copy()
x, y, w, h = cfg.dot_roi.x, cfg.dot_roi.y, cfg.dot_roi.w, cfg.dot_roi.h
cv2.rectangle(annotated, (x, y), (x + w, y + h), (0, 255, 255), 2)
if res.dot_found and res.match is not None:
# re-find dot to know exact x,y for annotation
from atm.vision import find_rightmost_dot
bg_rgb = (18, 18, 18)
if "background" in cfg.colors:
bg_rgb = cfg.colors["background"].rgb
dot_crop = crop_roi(frame, cfg.dot_roi)
pos = find_rightmost_dot(dot_crop, bg_rgb)
if pos is not None:
dx, dy = pos
cx, cy = x + dx, y + dy
cv2.circle(annotated, (cx, cy), 12, (0, 0, 255), 3)
annotated_path = out_dir / f"debug_annotated_{ts}.png"
cv2.imwrite(str(annotated_path), annotated)
print(f"annotated: {annotated_path}")
def _cmd_report(args) -> None:
try:
from atm.journal import Journal