Files
msgbot/easyocr_worker.py
T
youpaishidifu ba0e6e2450 修复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')
2026-08-01 13:00:57 +08:00

97 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
"""EasyOCR 子进程 worker。
通过 stdin 读 base64 图片、stdout 写 JSON 结果。GPU crash 只杀此进程,不冻主进程。
协议:
stdin: {"img":"<base64_png_bytes>"}
stdout: {"ok":true,"text":"识别结果"}
stdout: {"ok":false,"error":"错误描述"}
初始化慢(首次 3-10s),初始化完成后常驻处理。
"""
import json
import sys
import base64
import io
import os
# ── 线程/进程限制:避免 MKL/OpenMP 在子进程多线程冲突 ──
os.environ.setdefault("OMP_NUM_THREADS", "1")
os.environ.setdefault("MKL_NUM_THREADS", "1")
from PIL import Image
import numpy as np
import easyocr
def _init_reader():
"""初始化 EasyOCR reader(GPU 优先)。"""
try:
import torch
use_gpu = torch.cuda.is_available()
except Exception:
use_gpu = False
try:
reader = easyocr.Reader(["ch_sim", "en"], gpu=use_gpu)
mode = "GPU" if use_gpu else "CPU"
sys.stderr.write(f"[easyocr_worker] Reader 就绪({mode}模式)\n")
sys.stderr.flush()
return reader
except Exception as e:
# GPU 不可用 → CPU 兜底
sys.stderr.write(f"[easyocr_worker] GPU 初始化失败({e}),尝试 CPU\n")
sys.stderr.flush()
reader = easyocr.Reader(["ch_sim", "en"], gpu=False)
sys.stderr.write("[easyocr_worker] Reader 就绪(CPU 模式)\n")
sys.stderr.flush()
return reader
def _process_image(img_b64: str, reader) -> str:
"""解码 base64 图片 → EasyOCR 识别 → 返回空格拼接文本。"""
raw = base64.b64decode(img_b64)
img = Image.open(io.BytesIO(raw))
# 保持与 monitor.py 一致:RGBA → numpy
if img.mode != "RGBA":
img = img.convert("RGBA")
img_np = np.array(img)
results = reader.readtext(img_np)
return "\n".join([r[1] for r in results]).strip()
def _main():
reader = _init_reader()
# ⭐ 就绪握手:初始化完成后先输出 {"ready": true},让父进程知道可以开始请求
# 否则父进程的 10s 请求超时会包含 3-10s 的初始化时间 → 永远超时
sys.stdout.write(json.dumps({"ready": True}, ensure_ascii=False) + "\n")
sys.stdout.flush()
sys.stderr.write("[easyocr_worker] 就绪,等待 stdin 输入\n")
sys.stderr.flush()
for line in sys.stdin:
line = line.strip()
if not line:
continue
try:
payload = json.loads(line)
img_b64 = payload.get("img", "")
if not img_b64:
raise ValueError("缺少 img 字段")
text = _process_image(img_b64, reader)
out = json.dumps({"ok": True, "text": text}, ensure_ascii=False)
sys.stdout.write(out + "\n")
sys.stdout.flush()
except Exception as e:
out = json.dumps({"ok": False, "error": str(e)}, ensure_ascii=False)
sys.stdout.write(out + "\n")
sys.stdout.flush()
sys.stderr.write(f"[easyocr_worker] 处理失败: {e}\n")
sys.stderr.flush()
if __name__ == "__main__":
_main()