fix: EasyOCR启动异常+超时保护+消息搅和隔离
1. 修复负冷却时间bug:启动失败时若未设置冷却,现在设置30s冷却 2. EasyOCR worker添加超时保护:readtext()使用concurrent.futures包装,30s超时 3. Windows OCR消息搅和隔离:EasyOCR超时+Windows OCR多时间戳→返回空结果 4. 添加easyocr_worker_state.json到.gitignore
This commit is contained in:
@@ -72,3 +72,4 @@ test_*.txt
|
|||||||
_radical_*.txt
|
_radical_*.txt
|
||||||
unicode_radicals.md.bak
|
unicode_radicals.md.bak
|
||||||
easyocr_worker_state.json
|
easyocr_worker_state.json
|
||||||
|
easyocr_worker_state.json
|
||||||
|
|||||||
+21
-3
@@ -286,8 +286,10 @@ def _init_reader():
|
|||||||
return reader
|
return reader
|
||||||
|
|
||||||
|
|
||||||
def _process_image(img_b64: str, reader) -> str:
|
def _process_image(img_b64: str, reader, timeout: float = 30.0) -> str:
|
||||||
"""解码 base64 图片 → EasyOCR 识别 → 返回空格拼接文本。"""
|
"""解码 base64 图片 → EasyOCR 识别 → 返回空格拼接文本。
|
||||||
|
timeout: 单张图片OCR超时秒数,防止GPU/CPU推理卡死。
|
||||||
|
"""
|
||||||
if reader is None:
|
if reader is None:
|
||||||
return ""
|
return ""
|
||||||
raw = base64.b64decode(img_b64)
|
raw = base64.b64decode(img_b64)
|
||||||
@@ -296,7 +298,23 @@ def _process_image(img_b64: str, reader) -> str:
|
|||||||
if img.mode != "RGBA":
|
if img.mode != "RGBA":
|
||||||
img = img.convert("RGBA")
|
img = img.convert("RGBA")
|
||||||
img_np = np.array(img)
|
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()
|
return "\n".join([r[1] for r in results]).strip()
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
+12
@@ -2030,6 +2030,9 @@ class MessageMonitor:
|
|||||||
# 重置计数器(冷却后重试重新计数)
|
# 重置计数器(冷却后重试重新计数)
|
||||||
self._easyocr_consecutive_startup_failures = 0
|
self._easyocr_consecutive_startup_failures = 0
|
||||||
else:
|
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}次启动失败,"
|
logger.debug(f"[ocr] EasyOCR 第{self._easyocr_consecutive_startup_failures}次启动失败,"
|
||||||
f"冷却({int(self._easyocr_retry_until - time.time())}s)")
|
f"冷却({int(self._easyocr_retry_until - time.time())}s)")
|
||||||
return None
|
return None
|
||||||
@@ -2421,6 +2424,15 @@ class MessageMonitor:
|
|||||||
wt = win_text[0]
|
wt = win_text[0]
|
||||||
et = easy_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:
|
if wt:
|
||||||
for i, l in enumerate(wt.split('\n')):
|
for i, l in enumerate(wt.split('\n')):
|
||||||
|
|||||||
Reference in New Issue
Block a user