From 5c4a17043aa05a57948dfdf36a24b61d2e16fe37 Mon Sep 17 00:00:00 2001 From: youpaishidifu Date: Fri, 7 Aug 2026 14:50:29 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E4=BF=AE=E5=A4=8D=E4=B8=A4=E4=B8=AAOCR?= =?UTF-8?q?=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. 修复_clean_ocr_prefix误删登录关键词导致答非所问 - 噪声前缀后必须跟标点/空格或字符串结束,不能紧跟中文字符 - 登录插件,哪里可以下载到?不再被清理为插件,哪里可以下载到? 2. 修复EasyOCR静默原生崩溃检测 - 父进程:空stderr+空crash log也计入CUDA崩溃计数,2次后强制CPU - Worker:添加CUDA驱动DLL预检,加载失败时强制CPU模式 --- easyocr_worker.py | 15 +++++++++++++++ monitor.py | 13 +++++++++---- 2 files changed, 24 insertions(+), 4 deletions(-) diff --git a/easyocr_worker.py b/easyocr_worker.py index efb2cfc..4b8f12a 100644 --- a/easyocr_worker.py +++ b/easyocr_worker.py @@ -137,6 +137,21 @@ try: os.environ.setdefault("OMP_NUM_THREADS", "1") os.environ.setdefault("MKL_NUM_THREADS", "1") + # ── CUDA 驱动预检:在 import torch 之前探测 CUDA 驱动是否可用 ── + # 防止 CUDA 驱动损坏导致 import torch 时原生崩溃(crash log 为空的静默崩溃) + if os.environ.get("EASYOCR_FORCE_CPU") != "1": + try: + import ctypes + ctypes.windll.nvcuda # 尝试加载 CUDA 驱动 DLL + sys.stderr.write("[easyocr_worker] CUDA 驱动 DLL 加载成功\n") + sys.stderr.flush() + except Exception as _cuda_drv_err: + os.environ["EASYOCR_FORCE_CPU"] = "1" + os.environ["CUDA_VISIBLE_DEVICES"] = "" + sys.stderr.write(f"[easyocr_worker] ⚠️ CUDA 驱动加载失败({_cuda_drv_err}),强制 CPU 模式\n") + sys.stderr.flush() + _write_crash_log(f"{datetime.now()} [easyocr_worker] CUDA 驱动预检失败: {_cuda_drv_err}") + # ── 预检:物理内存+虚拟内存是否足够加载 CUDA 库 ── _vm = _get_virtual_memory_info() # ⭐ 统一使用 GlobalMemoryStatusEx(与 monitor.py / Task Manager 一致) diff --git a/monitor.py b/monitor.py index 0d1c215..19edbb5 100644 --- a/monitor.py +++ b/monitor.py @@ -1856,10 +1856,13 @@ class MessageMonitor: if crash: detail = (detail + "\n[ocr] worker 崩溃日志(easyocr_crash.log):\n" + crash).strip() logger.error(f"[ocr] EasyOCR worker 启动即崩溃: {detail}") - # ⭐ 检测 CUDA 相关崩溃(access violation / torch 错误)→ 增加 OOM 计数,下次强制 CPU - if crash and ("access violation" in crash.lower() or "torch" in crash.lower()): + # ⭐ 检测 CUDA 相关崩溃(access violation / torch 错误 / 静默原生崩溃)→ 增加 OOM 计数,下次强制 CPU + _is_cuda_crash = (crash and ("access violation" in crash.lower() or "torch" in crash.lower())) + _is_silent_native_crash = (not err.strip() and not crash.strip()) # 无stderr无crash log = 原生层崩溃 + if _is_cuda_crash or _is_silent_native_crash: self._easyocr_cuda_oom_count += 1 - logger.warning(f"[ocr] 检测到 CUDA 崩溃,累计{self._easyocr_cuda_oom_count}次,下次可能强制 CPU") + logger.warning(f"[ocr] 检测到{'CUDA崩溃' if _is_cuda_crash else '静默原生崩溃(无日志)'}, " + f"累计{self._easyocr_cuda_oom_count}次,下次可能强制 CPU") # ⭐ 检测页面文件不足(WinError 1455)→ 长期冷却,不反复重试 _is_pagefile_error = "页面文件" in str(detail) or "1455" in str(detail) if _is_pagefile_error: @@ -4810,6 +4813,8 @@ class MessageMonitor: # ⭐ OCR噪声前缀清理:去除UI按钮标签等OCR误识别的前缀 # 如"必选,运营方有哪些权利和义务?" → "运营方有哪些权利和义务?" + # 注意:前缀后必须跟标点/空格或字符串结束,不能紧跟中文字符 + # 否则会误删"登录插件"中的"登录" _OCR_NOISE_PREFIXES = re.compile( r'^(?:必选|必填|可选|请输入|搜索|查找|查询|发送|取消|确认|确定|提交|保存|删除|编辑| ' r'|添加|新建|创建|设置|修改|返回|关闭|展开|收起|更多|选项|操作|' @@ -4817,7 +4822,7 @@ class MessageMonitor: r'同意|拒绝|同意并|下一步|上一步|完成|跳过|继续|登录|注册|退出|' r'开启|关闭|启用|禁用|暂停|恢复|开始|停止|运行|' r'是|否|有|无|对|错|真|假|好|坏|新|旧|已|未)' - r'[,,::\s]*' + r'[,,::\s]+|$' ) @staticmethod