修复3个问题:
1. EasyOCR OOM降级CPU+重启恢复重检内存:_init_easyocr和_ensure_easyocr_subprocess添加内存检测,<2GB强制CPU;start()添加full_restart参数,用户手动启用时重置OOM计数重新评估GPU/CPU
2. PyInstaller exe闪退:spec入口改main.py,main.py添加EASYOCR_WORKER环境变量检测跳转worker逻辑;build.bat只清理build/build和dist不删spec/hooks
3. QQ OCR _processed_path未定义:补充ts和_processed_path变量定义
@
This commit is contained in:
youpaishidifu
2026-08-03 10:12:07 +08:00
parent 30106571d7
commit fa67eb407e
3 changed files with 59 additions and 8 deletions
+2 -1
View File
@@ -216,7 +216,8 @@ class MsgBotApp(ctk.CTk):
self._log("⚠ 知识库未加载,请先设置知识库路径")
self._toggle_btn.deselect()
return
self.monitor.start()
# 用户手动点击启用 → 彻底重启(重新检测内存,尝试恢复GPU)
self.monitor.start(full_restart=True)
self._status_label.configure(text="▶ 运行中", text_color="#2ecc71")
self._log("✅ 消息监控已启动")
else:
+8
View File
@@ -5,6 +5,14 @@ import os
import sys
import signal
# ── Worker 模式检测:PyInstaller 打包后,exe 被调用来运行 worker 脚本时,
# 通过环境变量区分,避免 bootloader 执行 main() 导致闪退 ──
if os.environ.get("EASYOCR_WORKER") == "1":
# 直接执行 worker 逻辑,跳过 GUI 初始化
import easyocr_worker
easyocr_worker._main()
sys.exit(0)
# 确保在项目目录
os.chdir(os.path.dirname(os.path.abspath(__file__)))
+45 -3
View File
@@ -402,7 +402,13 @@ class MessageMonitor:
def set_emergency_callback(self, cb: Callable[[], None]):
self._emergency_stop_callback = cb
def start(self):
def start(self, full_restart: bool = False):
"""启动监控。
Args:
full_restart: True=彻底重启(进程级,重新检测内存恢复GPU)
False=急停/手动停止恢复(软重启,不重新检测内存)
"""
if self._running:
return
self._running = True
@@ -413,7 +419,16 @@ class MessageMonitor:
self._easyocr_retry_until = 0.0 # 清除崩溃冷却
self._easyocr_call_count = 0
self._easyocr_consecutive_failures = 0 # ⭐ 清除连续超时计数(stop/急停后不残留)
self._easyocr_cuda_oom_count = 0 # 重启后重新评估 GPU/CPU
# ⭐ 彻底重启时重新检测内存,尝试恢复GPU模式
# 急停/手动停止恢复时保持上次状态,不重新检测
if full_restart:
self._easyocr_cuda_oom_count = 0 # 重新评估 GPU/CPU
logger.info("[ocr] 彻底重启:重新检测内存,尝试恢复GPU模式")
else:
# 急停/手动停止恢复:保持上次的降级状态(如果上次因OOM降级CPU,这次仍用CPU)
logger.info(f"[ocr] 软重启:保持上次EasyOCR状态(cuda_oom_count={self._easyocr_cuda_oom_count})")
self._easyocr_proc = None # 确保 clean state
# ⭐ OCR 状态清零:截图hash/垃圾计数/引擎选择/冷却等,确保重启不残留上一轮状态
# (上次 CUDA 报错降级 CPU 的计数、超时计数、截图指纹都从头开始)
@@ -1240,7 +1255,19 @@ class MessageMonitor:
# 限制 torch 线程数,避免 MKL/OpenMP 在多线程环境段错误
import torch
torch.set_num_threads(1)
# 检测可用内存,决定是否使用GPU
use_gpu = torch.cuda.is_available()
if use_gpu:
mem = psutil.virtual_memory()
available_gb = mem.available / (1024**3)
# 可用内存 < 2GB 时强制降级CPU,防止OOM崩溃
if available_gb < 2.0:
logger.warning(f"[ocr] 可用内存不足({available_gb:.1f}GB < 2GB),EasyOCR 降级CPU模式")
use_gpu = False
else:
logger.info(f"[ocr] 可用内存充足({available_gb:.1f}GB),尝试GPU模式")
self._easyocr_reader = _easyocr.Reader(['ch_sim', 'en'], gpu=use_gpu)
if use_gpu:
logger.info("[ocr] EasyOCR 就绪(GPU 模式)")
@@ -1310,10 +1337,23 @@ class MessageMonitor:
_worker_env = dict(os.environ)
# ⭐ CUDA_LAUNCH_BLOCKING=1: CUDA 操作同步化,access violation 时只杀子进程不冻整机
_worker_env["CUDA_LAUNCH_BLOCKING"] = "1"
if self._easyocr_cuda_oom_count >= 2:
# ⭐ EASYOCR_WORKER=1: 让 main.py 检测到 worker 模式,跳过 GUI 初始化
_worker_env["EASYOCR_WORKER"] = "1"
# 内存检测:可用内存 < 2GB 时强制CPU模式,防止OOM崩溃
try:
mem = psutil.virtual_memory()
available_gb = mem.available / (1024**3)
if available_gb < 2.0:
_worker_env["EASYOCR_FORCE_CPU"] = "1"
logger.warning(f"[ocr] 可用内存不足({available_gb:.1f}GB),EasyOCR 子进程强制CPU模式")
elif self._easyocr_cuda_oom_count >= 2:
# ⭐ 连续 CUDA OOM/kernel error → 强制 CPU 模式启动(显存不足时防冻结)
_worker_env["EASYOCR_FORCE_CPU"] = "1"
logger.warning("[ocr] EasyOCR 连续CUDA错误≥2次,本次强制 CPU 模式启动 worker")
except Exception:
pass # 获取内存失败不阻断
self._easyocr_proc = subprocess.Popen(
[sys.executable, '-u', worker_path],
stdin=subprocess.PIPE,
@@ -1867,6 +1907,8 @@ class MessageMonitor:
# 保存OCR处理后的图片(JPG格式)用于肉眼对比
try:
debug_dir = _get_ocr_processed_dir("qq")
ts = time.strftime("%Y%m%d_%H%M%S")
_processed_path = os.path.join(debug_dir, f"ocr_processed_qq_{ts}.jpg")
# 转换为RGB模式保存JPG(不支持RGBA)
if im.mode == 'RGBA':
im_rgb = im.convert('RGB')