修复3个OCR问题: easyocr子进程ready握手+60s初始化预算, 偏旁过滤改Unicode范围判断, 新旧消息按新鲜特征放行
- easyocr_worker.py: 初始化完成后输出{"ready":true}握手, 父进程不再把3-10s初始化计入10s请求超时
- monitor.py: _ensure_easyocr_subprocess重写 - 60s握手预算(daemon线程+Queue), stderr PIPE捕获崩溃原因, 崩溃/超时改30s冷却自动重试(不再永久broken)
- monitor.py: 删除markdown表格字符key偏旁库, 改U+2E80-U+2FDF码位范围判断, 修复"地图"/"太"等正常汉字被误判为偏旁丢弃
- monitor.py: _looks_fresh_content新鲜特征判断 - 旧时间戳但含疑问词/问句/句尾标点的消息放行, 不再整块丢弃
- 新增test_easyocr_handshake.py: 子进程握手+OCR往返回归测试(实测通过: 28s含初始化, 识别'你好世界测试123')
This commit is contained in:
@@ -0,0 +1,110 @@
|
||||
#!/usr/bin/env python3
|
||||
"""测试 easyocr_worker 子进程 ready 握手 + OCR 请求往返。
|
||||
|
||||
模拟 monitor.py 父进程行为:
|
||||
1. 启动 easyocr_worker.py 子进程
|
||||
2. 等待 {"ready": true} 握手(90s 预算,覆盖 3-10s torch+模型加载)
|
||||
3. 发一张含中文的测试图,验证 OCR 结果
|
||||
"""
|
||||
import json
|
||||
import subprocess
|
||||
import sys
|
||||
import os
|
||||
import base64
|
||||
import io
|
||||
import threading
|
||||
from queue import Queue, Empty
|
||||
|
||||
from PIL import Image, ImageDraw, ImageFont
|
||||
|
||||
|
||||
def main():
|
||||
# ── 1. 生成测试图(白底黑字中文) ──
|
||||
img = Image.new("RGB", (500, 120), "white")
|
||||
d = ImageDraw.Draw(img)
|
||||
try:
|
||||
font = ImageFont.truetype("C:/Windows/Fonts/msyh.ttc", 32)
|
||||
except Exception:
|
||||
font = ImageFont.load_default()
|
||||
d.text((20, 40), "你好世界 测试123", fill="black", font=font)
|
||||
buf = io.BytesIO()
|
||||
img.save(buf, format="PNG")
|
||||
img_b64 = base64.b64encode(buf.getvalue()).decode()
|
||||
print(f"[test] 测试图生成 OK, b64长度={len(img_b64)}", flush=True)
|
||||
|
||||
worker_path = os.path.join(
|
||||
os.path.dirname(os.path.abspath(__file__)), "easyocr_worker.py"
|
||||
)
|
||||
print(f"[test] worker路径: {worker_path}", flush=True)
|
||||
|
||||
# ── 2. 启动子进程 ──
|
||||
proc = subprocess.Popen(
|
||||
[sys.executable, "-u", worker_path],
|
||||
stdin=subprocess.PIPE,
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.PIPE,
|
||||
text=True,
|
||||
bufsize=1,
|
||||
)
|
||||
print(f"[test] 子进程已启动 pid={proc.pid}", flush=True)
|
||||
|
||||
def _read_line(pipe, q):
|
||||
try:
|
||||
q.put(pipe.readline())
|
||||
except Exception as e:
|
||||
q.put(f"__READ_ERR__:{e}")
|
||||
|
||||
# ── 3. 等待 ready 握手(90s 预算) ──
|
||||
q = Queue()
|
||||
t = threading.Thread(target=_read_line, args=(proc.stdout, q), daemon=True)
|
||||
t.start()
|
||||
try:
|
||||
line = q.get(timeout=90)
|
||||
except Empty:
|
||||
print("[test] FAIL: ready 握手 90s 超时", flush=True)
|
||||
proc.kill()
|
||||
return 1
|
||||
print(f"[test] 握手响应: {line.strip()!r}", flush=True)
|
||||
try:
|
||||
payload = json.loads(line)
|
||||
assert payload.get("ready") is True, f"非 ready 响应: {line!r}"
|
||||
except Exception as e:
|
||||
err = proc.stderr.read()[:1500]
|
||||
print(f"[test] FAIL: 握手解析异常 {e}, stderr={err!r}", flush=True)
|
||||
proc.kill()
|
||||
return 1
|
||||
print("[test] PASS: ready 握手成功", flush=True)
|
||||
|
||||
# ── 4. 发送 OCR 请求 ──
|
||||
proc.stdin.write(json.dumps({"img": img_b64}) + "\n")
|
||||
proc.stdin.flush()
|
||||
|
||||
q2 = Queue()
|
||||
t2 = threading.Thread(target=_read_line, args=(proc.stdout, q2), daemon=True)
|
||||
t2.start()
|
||||
try:
|
||||
rline = q2.get(timeout=30)
|
||||
except Empty:
|
||||
print("[test] FAIL: OCR 请求 30s 超时", flush=True)
|
||||
proc.kill()
|
||||
return 1
|
||||
print(f"[test] OCR 响应: {rline.strip()!r}", flush=True)
|
||||
try:
|
||||
res = json.loads(rline)
|
||||
assert res.get("ok"), f"OCR 失败: {res}"
|
||||
print(f"[test] OCR 文本: {res['text']!r}", flush=True)
|
||||
assert "你好" in res["text"] or "测试" in res["text"], "识别结果缺中文"
|
||||
except Exception as e:
|
||||
print(f"[test] FAIL: OCR 结果异常 {e}", flush=True)
|
||||
proc.kill()
|
||||
return 1
|
||||
|
||||
# ── 5. 诊断信息 ──
|
||||
proc.kill()
|
||||
proc.wait(timeout=3)
|
||||
print("[test] PASS: easyocr 子进程 ready 握手 + OCR 请求往返全部成功", flush=True)
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
||||
Reference in New Issue
Block a user