CogletESP-camera-version/scripts/auto_capture_jpeg.py
Rdzleo f1c2bfce93 Phase 01 JPEG Dump 诊断 + YVYU 修正 + 矛盾分析汇总
核心变更:
- face_tracker.cc: YUYV→YVYU 序列修正(byte[1]=V, byte[3]=U),
  基于 JPEG Dump 诊断工具验证 OV3660 FORMAT_CTRL00=0x61 实际是 YVYU
- face_tracker.cc: 启动时 base64 打印一帧 JPEG 到串口,用于肉眼验证
- config.h: XCLK 20MHz→10MHz,给飞线信号完整性 2x 裕度
- scripts/auto_capture_jpeg.py: 自动串口抓帧工具(DTR/RTS 复位 + base64 解码)
- scripts/extract_jpeg_from_log.py: 从日志离线提取 JPEG
- Coglet项目分析与开发指南.md: 新增"六点六"章节,汇总 Phase 01
  主要矛盾(画面可辨识≠模型可识别)、YUV→RGB 色偏三层原因、
  esp-dl 模型输入分布敏感性、延迟分析、三方案对比、方案 B 突破口
- docs/: 新增 2 篇 OV3660 相关 CSDN 参考资料

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-22 11:01:02 +08:00

108 lines
3.1 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/usr/bin/env python3
# auto_capture_jpeg.py
# 自动连接 ESP32 串口、触发复位、等待多个 JPEG dump、保存并打开所有图片
# 支持新格式:===JPEG_DUMP_BEGIN fmt=<NAME> size=<N>===
import serial
import base64
import re
import sys
import time
import subprocess
from pathlib import Path
PORT = "/dev/cu.usbmodem834401"
BAUD = 115200
OUT_DIR = Path("/Users/rdzleo/Desktop/CogletESP-camera-version/scripts")
TIMEOUT_SEC = 90
MAX_FRAMES = 4
BEGIN_RE = re.compile(r"===JPEG_DUMP_BEGIN\s+(?:fmt=(\S+)\s+)?size=(\d+)===")
END_RE = re.compile(r"===JPEG_DUMP_END===")
B64_RE = re.compile(r"^[A-Za-z0-9+/=]+$")
def main():
print(f"[·] 打开串口 {PORT} @ {BAUD}")
ser = serial.Serial(PORT, BAUD, timeout=1)
print("[·] 复位 ESP32 …")
ser.dtr = False
ser.rts = True
time.sleep(0.1)
ser.rts = False
time.sleep(0.1)
ser.reset_input_buffer()
print(f"[·] 等待 JPEG_DUMP 标记(最多 {TIMEOUT_SEC}s期望 {MAX_FRAMES} 张)…")
start = time.time()
in_dump = False
expected_size = 0
current_fmt = None
b64_buf = []
saved_files = []
while time.time() - start < TIMEOUT_SEC and len(saved_files) < MAX_FRAMES:
raw = ser.readline()
if not raw:
continue
try:
line = raw.decode("utf-8", errors="replace").rstrip("\r\n")
except Exception:
continue
if any(k in line for k in ["FaceTracker", "Camera", "panic", "Guru", "ov3660", "Compile time"]):
print(f" {line}")
if not in_dump:
m = BEGIN_RE.search(line)
if m:
in_dump = True
current_fmt = m.group(1) or "unknown"
expected_size = int(m.group(2))
b64_buf = []
print(f"[+] JPEG_BEGIN fmt={current_fmt} size={expected_size}")
continue
if END_RE.search(line):
in_dump = False
b64_str = "".join(b64_buf)
try:
data = base64.b64decode(b64_str)
except Exception as e:
print(f"[!] base64 decode failed: {e}")
continue
if len(data) != expected_size:
print(f"[!] 字节数差异 got={len(data)} expected={expected_size}")
out_path = OUT_DIR / f"frame_{current_fmt}.jpg"
out_path.write_bytes(data)
print(f"[✓] 保存 {out_path.name} ({len(data)/1024:.1f} KB)")
saved_files.append(out_path)
continue
stripped = line.strip()
if B64_RE.match(stripped):
b64_buf.append(stripped)
ser.close()
if not saved_files:
print("[!] 没有抓到任何 JPEG 帧")
return 1
print(f"\n[✓] 共保存 {len(saved_files)}")
for p in saved_files:
print(f" - {p}")
# 用 Finder 打开目录,用户可以并排对比
subprocess.run(["open", str(OUT_DIR)])
# 或者直接打开所有 JPEG
for p in saved_files:
subprocess.run(["open", str(p)])
return 0
if __name__ == "__main__":
sys.exit(main())