diff --git a/.gitignore b/.gitignore index 085e3fc..81f98e7 100644 --- a/.gitignore +++ b/.gitignore @@ -72,3 +72,4 @@ test_*.txt _radical_*.txt unicode_radicals.md.bak easyocr_worker_state.json +easyocr_worker_state.json diff --git a/easyocr_worker.py b/easyocr_worker.py index 4b8f12a..8c8438f 100644 --- a/easyocr_worker.py +++ b/easyocr_worker.py @@ -286,8 +286,10 @@ def _init_reader(): return reader -def _process_image(img_b64: str, reader) -> str: - """解码 base64 图片 → EasyOCR 识别 → 返回空格拼接文本。""" +def _process_image(img_b64: str, reader, timeout: float = 30.0) -> str: + """解码 base64 图片 → EasyOCR 识别 → 返回空格拼接文本。 + timeout: 单张图片OCR超时秒数,防止GPU/CPU推理卡死。 + """ if reader is None: return "" raw = base64.b64decode(img_b64) @@ -296,7 +298,23 @@ def _process_image(img_b64: str, reader) -> str: if img.mode != "RGBA": img = img.convert("RGBA") img_np = np.array(img) - results = reader.readtext(img_np) + + # ⭐ 超时保护:用 concurrent.futures 包装 readtext,防止GPU推理卡死 + from concurrent.futures import ThreadPoolExecutor, TimeoutError as FuturesTimeoutError + with ThreadPoolExecutor(max_workers=1) as executor: + future = executor.submit(reader.readtext, img_np) + try: + results = future.result(timeout=timeout) + except FuturesTimeoutError: + sys.stderr.write(f"[easyocr_worker] readtext 超时({timeout}s),放弃本次OCR\n") + sys.stderr.flush() + _write_crash_log(f"{datetime.now()} [easyocr_worker] readtext 超时({timeout}s)") + return "" + except Exception as e: + sys.stderr.write(f"[easyocr_worker] readtext 异常: {e}\n") + sys.stderr.flush() + raise + return "\n".join([r[1] for r in results]).strip() diff --git a/monitor.py b/monitor.py index b5150ac..003e659 100644 --- a/monitor.py +++ b/monitor.py @@ -2030,6 +2030,9 @@ class MessageMonitor: # 重置计数器(冷却后重试重新计数) self._easyocr_consecutive_startup_failures = 0 else: + # ⭐ 短冷却:未达阈值时也设30s冷却,避免反复启动失败刷屏 + if self._easyocr_retry_until <= time.time(): + self._easyocr_retry_until = time.time() + 30 logger.debug(f"[ocr] EasyOCR 第{self._easyocr_consecutive_startup_failures}次启动失败," f"冷却({int(self._easyocr_retry_until - time.time())}s)") return None @@ -2421,6 +2424,15 @@ class MessageMonitor: wt = win_text[0] et = easy_text[0] + # ⭐ EasyOCR超时+Windows OCR混合消息检测:当EasyOCR超时且Windows OCR结果包含 + # 多个时间戳(表明是多条消息混合),返回空结果,避免消息搅和 + if not et and wt: + timestamps = re.findall(r'\d{1,2}:\d{1,2}', wt) + if len(timestamps) >= 2: + logger.warning(f"[{platform}] EasyOCR超时+Windows OCR检测到{len(timestamps)}个时间戳" + f"({timestamps[:3]}...),疑似消息混合,跳过本轮避免搅和") + return ("", "") + # 日志(原始输出逐行记录,用於長期對比兩引擎能力) if wt: for i, l in enumerate(wt.split('\n')):