修复2个问题: OCR拼接合并 + 微信定位冻结3-5s
问题1: OCR拼接合并 (2处改动)
- easyocr_worker.py: ' '.join→'\n'.join, 各检测区域用换行而非空格拼接
避免 '兑换码导入错误时如何处置?游戏专用小筑憩-草一' 式合并
- _process_ocr_output 尾部新增 _split_merged_ocr_text:
在中文句尾标点([。?!?])后紧跟CJK处拆分,两侧CJK>=3且非续接语气词
将合并文本分为多条消息,各自独立参与KB匹配
问题2: 微信 _fresh_window 冻结3-5s (2处改动)
- _poll: 对wechat跳过 _fresh_window(ControlFromHandle构建Electron UIA树冻结)
用 win=None 代替
- _read_messages(wechat): 接收window=None, OCR失败后惰性创建UIA控件
大多数轮次 OCR 读到消息就直接返回,不碰UIA树
This commit is contained in:
+1
-1
@@ -58,7 +58,7 @@ def _process_image(img_b64: str, reader) -> str:
|
||||
img = img.convert("RGBA")
|
||||
img_np = np.array(img)
|
||||
results = reader.readtext(img_np)
|
||||
return " ".join([r[1] for r in results]).strip()
|
||||
return "\n".join([r[1] for r in results]).strip()
|
||||
|
||||
|
||||
def _main():
|
||||
|
||||
+73
-5
@@ -1500,7 +1500,12 @@ class MessageMonitor:
|
||||
if self._is_reply_fragment(msg):
|
||||
logger.debug(f"[ocr] stage[{i}] 回复残片跳过: {msg[:60]!r}")
|
||||
else:
|
||||
expanded.append(msg)
|
||||
# EasyOCR 常把横向相邻的两条消息(用户消息+群昵称/UI文字)
|
||||
# 检测为同一行,导致"兑换码导入错误时如何处置?游戏专用小筑憩-草一"
|
||||
# 这种合并文本。尝试在中文句尾标点处拆分为多条。
|
||||
split_msgs = self._split_merged_ocr_text(msg)
|
||||
for sm in split_msgs:
|
||||
expanded.append(sm)
|
||||
|
||||
# —— 逐行过滤 ——
|
||||
ocr_lines = expanded
|
||||
@@ -2189,6 +2194,57 @@ class MessageMonitor:
|
||||
# 拿不准保守处理
|
||||
return True
|
||||
|
||||
@staticmethod
|
||||
def _split_merged_ocr_text(text: str) -> list:
|
||||
"""尝试将 OCR 单行合并文本在中文句尾标点处拆分。
|
||||
|
||||
EasyOCR 常把横向相邻的两条消息(用户问句 + 群昵称/UI文字/其他人消息)
|
||||
检测为同一行文本,导致"兑换码导入错误时如何处置?游戏专用小筑憩-草一"式合并。
|
||||
|
||||
启发式: 在[。?!?]后紧跟CJK字符的位置拆分,前提是两侧都有足够中文。
|
||||
不拆分续接句(第二段以"但/而/并/且/或/还/也/又/就/才/再/变"开头)。
|
||||
"""
|
||||
if len(text) < 10:
|
||||
return [text]
|
||||
|
||||
_continuations = {'但', '而', '并', '且', '或', '还', '也', '又',
|
||||
'就', '才', '再', '变', '便', '则', '却', '虽',
|
||||
'因', '为', '与', '和', '同', '跟'}
|
||||
result = []
|
||||
remaining = text
|
||||
max_splits = 2 # 最多拆 2 次(3 段),防过度拆分
|
||||
|
||||
while max_splits > 0:
|
||||
# 找[。?!?]后紧跟CJK的位置
|
||||
m = re.search(r'([。?!?])([\u4e00-\u9fff])', remaining)
|
||||
if not m:
|
||||
break
|
||||
split_pos = m.start() + 1 # 标点后
|
||||
first = remaining[:split_pos]
|
||||
second = remaining[split_pos:]
|
||||
|
||||
# 两侧都需有足够 CJK
|
||||
first_cjk = sum(1 for c in first if '\u4e00' <= c <= '\u9fff')
|
||||
second_cjk = sum(1 for c in second if '\u4e00' <= c <= '\u9fff')
|
||||
if first_cjk < 3 or second_cjk < 3:
|
||||
break
|
||||
|
||||
# 第二段不以续接词开头
|
||||
if second[0] in _continuations:
|
||||
break
|
||||
|
||||
# 第二段太短(少于 3 CJK)→ 不拆
|
||||
first_part = first
|
||||
second_part = second
|
||||
result.append(first_part)
|
||||
remaining = second_part
|
||||
max_splits -= 1
|
||||
|
||||
result.append(remaining)
|
||||
if len(result) > 1:
|
||||
logger.debug(f"[ocr] 句尾标点拆分: {[r[:60] for r in result]}")
|
||||
return result
|
||||
|
||||
def _is_reply_fragment(self, text: str) -> bool:
|
||||
"""检查文本是否为 bot 回复内容的碎片(OCR 截取的 md 残片/跨行碎片)。"""
|
||||
if not self._replied_texts or len(text) < 6:
|
||||
@@ -2961,6 +3017,12 @@ class MessageMonitor:
|
||||
# 截图未变→跳过 fallback
|
||||
if self._ocr_unchanged.get("wechat"):
|
||||
return []
|
||||
# 未提供 UIA 控件(_poll 跳过了 _fresh_window 避免 Electron 冻结)
|
||||
# 此时惰性创建,仅在 OCR 失败后 fallback 时才需要
|
||||
if window is None:
|
||||
window = self._fresh_window(platform)
|
||||
if window is None:
|
||||
return []
|
||||
# 微信: UIA 兜底(可能读到陈旧数据或 UI 文字)
|
||||
texts = self._read_message_list(window)
|
||||
if texts:
|
||||
@@ -3238,10 +3300,16 @@ class MessageMonitor:
|
||||
return
|
||||
|
||||
# 2. 从 HWND 创建全新 Control(绕过缓存)
|
||||
win = self._fresh_window(platform)
|
||||
if not win:
|
||||
logger.debug(f"[{platform}] 创建窗口控件失败")
|
||||
return
|
||||
# QQ: 需要 UIA 控件做多种 fallback 读取(ListControl、全窗爬文本等)
|
||||
# 微信( Electron ): ControlFromHandle 构建 UIA 控件树冻结 3-5s,
|
||||
# 而微信 OCR 读到消息就直接返回,不需要 UIA。只在 OCR 空/fallback 时创建。
|
||||
if platform == "qq":
|
||||
win = self._fresh_window(platform)
|
||||
if not win:
|
||||
logger.debug(f"[{platform}] 创建窗口控件失败")
|
||||
return
|
||||
else:
|
||||
win = None # 惰性创建:OCR 失败后需要 fallback 时再建
|
||||
|
||||
# 3. 读消息
|
||||
texts = self._read_messages(win, platform)
|
||||
|
||||
Reference in New Issue
Block a user