fix(ocr): 引擎对比长度保护——Windows OCR截断丢消息

根因:_pick_ocr_text() 用 score >= score 选引擎,Windows OCR
虽然行数少(8行 vs EasyOCR 19行),但因垃圾字符少、空格惩罚低,
分数可能略高或持平,导致完整 EasyOCR 文本被丢弃。

修复:
- 新增长度保护:当一方行数 >=1.5x 且字符数 >=1.3x 另一方时,
  分数差 <10 分则强制选更长文本
- 引擎对比日志增加行数/字符数/分数差,方便排查
- 不影响分数差距大时的正常选择逻辑
This commit is contained in:
youpaishidifu
2026-08-02 17:51:27 +08:00
parent 3853804ca4
commit 6f791622c9
+29 -3
View File
@@ -1366,20 +1366,46 @@ class MessageMonitor:
return max(score, 0.0)
def _pick_ocr_text(self, win_text: str, easy_text: str, image: Image.Image) -> tuple:
"""双引擎对比,选质量更高的结果。日志记录引擎胜负统计。返回 (text, engine_name)。"""
"""双引擎对比,选质量更高的结果。日志记录引擎胜负统计。返回 (text, engine_name)。
⭐ 长度保护:当一引擎识别行数显著多于另一方(>=1.5x)时,
即使分数略低也优先选更长文本——避免 Windows OCR 截断丢消息。
"""
if not easy_text:
return (win_text, "windows")
if not win_text:
return (easy_text, "easyocr")
win_score = self._score_ocr_text(win_text)
easy_score = self._score_ocr_text(easy_text)
win_lines = len([l for l in win_text.split('\n') if l.strip()])
easy_lines = len([l for l in easy_text.split('\n') if l.strip()])
win_len = len(win_text.strip())
easy_len = len(easy_text.strip())
# 长度保护:一方行数 >= 1.5x 另一方 → 倾向更长文本(分数差 < 10 时触发)
chosen = "windows" if win_score >= easy_score else "easyocr"
score_diff = abs(win_score - easy_score)
if score_diff < 10:
if easy_lines >= win_lines * 1.5 and easy_len > win_len * 1.3:
chosen = "easyocr"
logger.debug(
f"[ocr] 长度保护: easy行数{easy_lines}>=1.5x win行数{win_lines},"
f"分数差仅{score_diff:.1f},强制选easyocr"
)
elif win_lines >= easy_lines * 1.5 and win_len > easy_len * 1.3:
chosen = "windows"
logger.debug(
f"[ocr] 长度保护: win行数{win_lines}>=1.5x easy行数{easy_lines},"
f"分数差仅{score_diff:.1f},强制选windows"
)
self._ocr_engine_wins[chosen] = self._ocr_engine_wins.get(chosen, 0) + 1
total = sum(self._ocr_engine_wins.values())
ratio = self._ocr_engine_wins.get(chosen, 0) / max(total, 1)
logger.debug(
f"[ocr] 引擎对比: win={win_score:.1f} vs easy={easy_score:.1f} → 选{chosen} "
f"(胜率{ratio:.0%}, {total}轮)"
f"[ocr] 引擎对比: win={win_score:.1f}(行{win_lines},字符{win_len}) "
f"vs easy={easy_score:.1f}(行{easy_lines},字符{easy_len}) "
f"→ 选{chosen}(分数差{score_diff:.1f}, 胜率{ratio:.0%}, {total}轮)"
)
return (win_text, chosen) if chosen == "windows" else (easy_text, chosen)