CogletESP-camera-version/scripts/extract_jpeg_from_log.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

89 lines
2.8 KiB
Python
Executable File

#!/usr/bin/env python3
# extract_jpeg_from_log.py
# 从 ESP32 串口日志中提取 base64 编码的 JPEG 图像并保存为 .jpg 文件
#
# 用法:
# 1) 启动 monitor 并把日志重定向到文件:
# idf.py -p /dev/cu.usbmodem834401 monitor > /tmp/esp32.log
# 或者直接从已有日志提取:
# python3 extract_jpeg_from_log.py /tmp/esp32.log
# 2) 设备启动后会打印 "===JPEG_DUMP_BEGIN===" .... "===JPEG_DUMP_END==="
# 3) 运行此脚本会在当前目录生成 frame_001.jpg 等文件
#
# 也支持从 stdin 实时读取:
# idf.py monitor | python3 extract_jpeg_from_log.py -
import sys
import re
import base64
from pathlib import Path
BEGIN_RE = re.compile(r"===JPEG_DUMP_BEGIN\s+size=(\d+)===")
END_RE = re.compile(r"===JPEG_DUMP_END===")
# 匹配纯 base64 行(不含普通文本)
B64_RE = re.compile(r"^[A-Za-z0-9+/=]+$")
def extract(lines, out_dir=Path(".")):
frame_idx = 0
in_dump = False
b64_buf = []
expected_size = 0
for raw in lines:
line = raw.rstrip("\r\n")
# 去掉 ESP 日志时间戳前缀可能引入的干扰:只在 begin/end 标记附近处理
if not in_dump:
m = BEGIN_RE.search(line)
if m:
in_dump = True
expected_size = int(m.group(1))
b64_buf = []
print(f"[+] JPEG_BEGIN 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"[!] size mismatch: got {len(data)} expected {expected_size} "
f"(可能是 monitor 丢字节,仍尝试保存)"
)
frame_idx += 1
out_path = out_dir / f"frame_{frame_idx:03d}.jpg"
out_path.write_bytes(data)
print(f"[✓] saved {out_path} ({len(data)} bytes)")
# macOS: 自动打开
if sys.platform == "darwin":
import subprocess
subprocess.run(["open", str(out_path)])
continue
# 处于 dump 区间,只收集看起来是 base64 的行
stripped = line.strip()
if B64_RE.match(stripped):
b64_buf.append(stripped)
# 其他行(比如 "I (xxx) TAG:" 的日志)忽略
def main():
if len(sys.argv) < 2:
print("Usage: extract_jpeg_from_log.py <logfile | ->")
sys.exit(1)
src = sys.argv[1]
if src == "-":
extract(sys.stdin)
else:
with open(src, "r", errors="replace") as f:
extract(f)
if __name__ == "__main__":
main()