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:
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user