修复微信自身消息未跳过(自回复循环) + QQ调试截图被清理顶掉
1. 微信颜色门禁误判根因:聊天浅灰背景(≈237)原阈值>225被算作"对方白气泡", 且绿色自身气泡上的白色文字也通过白像素检测,导致门禁永远不跳过→一直OCR→自回复循环。 改为:白像素须≥245(纯白,排除浅灰背景),且四邻域≥3个同为纯白才计为"对方气泡", 从而正确排除绿/蓝自身气泡上的白色文字。合成图验证:仅自身绿泡→跳过、对方白泡→OCR、 空白灰屏→跳过、绿底白字→跳过。 2. QQ调试截图"没保存"根因:_clean_old_ocr_debug 用 QQ+微信共享6张上限池, 微信轮询频率远高于QQ,其清理调用把刚保存的QQ截图顶掉。改为按前缀分别保留 (ocr_debug_qq_ / ocr_debug_wx_ 各6张),互不挤占。 3. 截图保存硬化:保存后校验文件存在并INFO记录;异常时WARNING输出真实路径(原输出的是glob); 显式指定 format=PNG。 Co-Authored-By: Senior Developer <senior-developer@experts>
This commit is contained in:
co-authored by
Senior Developer
parent
01d0604f11
commit
62928d81eb
+59
-18
@@ -1269,11 +1269,14 @@ class MessageMonitor:
|
||||
script_dir = os.path.dirname(os.path.abspath(__file__))
|
||||
ts = datetime.now().strftime('%Y%m%d_%H%M%S.%f')[:19]
|
||||
_dbg_path = os.path.join(script_dir, f"ocr_debug_qq_{ts}.png")
|
||||
im.save(_dbg_path)
|
||||
logger.debug(f"[qq] 调试截图: {_dbg_path}")
|
||||
_clean_old_ocr_debug(script_dir, keep=6)
|
||||
im.save(_dbg_path, format="PNG")
|
||||
_clean_old_ocr_debug(script_dir, keep=6, prefix="ocr_debug_qq_")
|
||||
if os.path.exists(_dbg_path):
|
||||
logger.info(f"[qq] 调试截图已保存: {_dbg_path}")
|
||||
else:
|
||||
logger.warning(f"[qq] 调试截图保存后未找到文件: {_dbg_path}")
|
||||
except Exception as e:
|
||||
logger.warning(f"[qq] 调试截图保存失败: {e} (路径: {os.path.join(script_dir, 'ocr_debug_qq_*.png')})")
|
||||
logger.warning(f"[qq] 调试截图保存失败: {e} (路径: {_dbg_path})")
|
||||
|
||||
# ⭐ 颜色像素检测:如果截图中只有 bot 蓝色气泡,无白色用户消息,跳过
|
||||
if not self._has_user_colors(im, "qq"):
|
||||
@@ -1373,12 +1376,14 @@ class MessageMonitor:
|
||||
script_dir = os.path.dirname(os.path.abspath(__file__))
|
||||
ts = datetime.now().strftime('%Y%m%d_%H%M%S.%f')[:19]
|
||||
_dbg_path = os.path.join(script_dir, f"ocr_debug_wx_{ts}.png")
|
||||
im.save(_dbg_path)
|
||||
logger.debug(f"[wechat] 调试截图: {_dbg_path}")
|
||||
# 清理 ocr_debug_* 调试图(unified pool)
|
||||
_clean_old_ocr_debug(script_dir, keep=6)
|
||||
im.save(_dbg_path, format="PNG")
|
||||
_clean_old_ocr_debug(script_dir, keep=6, prefix="ocr_debug_wx_")
|
||||
if os.path.exists(_dbg_path):
|
||||
logger.info(f"[wechat] 调试截图已保存: {_dbg_path}")
|
||||
else:
|
||||
logger.warning(f"[wechat] 调试截图保存后未找到文件: {_dbg_path}")
|
||||
except Exception as e:
|
||||
logger.warning(f"[wechat] 调试截图保存失败: {e} (路径: {os.path.join(script_dir, 'ocr_debug_wx_*.png')})")
|
||||
logger.warning(f"[wechat] 调试截图保存失败: {e} (路径: {_dbg_path})")
|
||||
|
||||
# ⭐ 颜色像素检测:如果截图中只有自身绿色气泡,无对方白色消息,跳过
|
||||
if not self._has_user_colors(im, "wechat"):
|
||||
@@ -2196,6 +2201,27 @@ class MessageMonitor:
|
||||
bits.append('1' if left > right else '0')
|
||||
return ''.join(bits)
|
||||
|
||||
def _is_white_bubble_pixel(px, x, y, w, h) -> bool:
|
||||
"""判定 (x,y) 的纯白像素是否属于"白色气泡"(对方消息),而非:
|
||||
- 聊天浅灰背景(本身不是 >=245 候选)
|
||||
- 彩色(绿/蓝)自身气泡上的白色文字(邻域为彩色底色)
|
||||
规则:四方向(±4px)邻域中 >=3 个也为 near-white(>=245) 才算气泡。
|
||||
白字笔画的邻域多为彩色气泡底色 → 不通过 → 不被误判为对方消息。
|
||||
"""
|
||||
d = 4
|
||||
hits = 0
|
||||
for dx, dy in ((d, 0), (-d, 0), (0, d), (0, -d)):
|
||||
nx, ny = x + dx, y + dy
|
||||
if 0 <= nx < w and 0 <= ny < h:
|
||||
try:
|
||||
rr, gg, bb = px[nx, ny][:3]
|
||||
if rr >= 245 and gg >= 245 and bb >= 245:
|
||||
hits += 1
|
||||
except Exception:
|
||||
pass
|
||||
return hits >= 3
|
||||
|
||||
|
||||
@staticmethod
|
||||
def _has_user_colors(im, platform: str) -> bool:
|
||||
"""区域颜色检测:判断截图中是否有**对方发来的消息**,决定是否需要OCR。
|
||||
@@ -2236,15 +2262,21 @@ class MessageMonitor:
|
||||
def analyze(img):
|
||||
p = img.load()
|
||||
bot = other = white = total = 0
|
||||
for y in range(0, img.height, step_y):
|
||||
for x in range(0, img.width, step_x):
|
||||
iw, ih = img.width, img.height
|
||||
for y in range(0, ih, step_y):
|
||||
for x in range(0, iw, step_x):
|
||||
total += 1
|
||||
try:
|
||||
r, g, b = p[x, y][:3]
|
||||
except:
|
||||
continue
|
||||
if r > 225 and g > 225 and b > 225:
|
||||
white += 1
|
||||
if r >= 245 and g >= 245 and b >= 245:
|
||||
# ⚠️ 纯白像素可能是:①对方白色气泡(背景/白字) ②聊天浅灰背景
|
||||
# ③绿色/蓝色自身气泡上的白色文字。
|
||||
# ②③ 不能算"对方消息"——否则微信自身绿气泡白字被误判为对方
|
||||
# → OCR → 自回复循环。必须确认邻域也为大面积白色(气泡)才计入。
|
||||
if _is_white_bubble_pixel(p, x, y, iw, ih):
|
||||
white += 1
|
||||
elif is_bot(r, g, b):
|
||||
bot += 1
|
||||
else:
|
||||
@@ -2311,7 +2343,7 @@ class MessageMonitor:
|
||||
for sx in range(0, left_35.width, l35_step_x):
|
||||
try:
|
||||
r, g, b = l35_p[sx, sy][:3]
|
||||
if r > 225 and g > 225 and b > 225:
|
||||
if r >= 245 and g >= 245 and b >= 245:
|
||||
rw += 1
|
||||
except:
|
||||
continue
|
||||
@@ -3676,15 +3708,24 @@ class MessageMonitor:
|
||||
self._cached_hwnd.pop(platform, None)
|
||||
self._cached_pid.pop(platform, None)
|
||||
|
||||
def _clean_old_ocr_debug(dir_path: str, keep: int = 20):
|
||||
"""清理 ocr_debug_*.png 文件,只保留最近 keep 张。"""
|
||||
def _clean_old_ocr_debug(dir_path: str, keep: int = 20, prefix: str = "ocr_debug_"):
|
||||
"""清理 ocr_debug_*.png 调试截图,按 prefix 分别保留最近 keep 张。
|
||||
|
||||
分前缀清理:微信轮询频率远高于 QQ,若共享一个 6 张上限池,微信的密集保存
|
||||
会把 QQ 的调试截图顶掉,导致"QQ 截图没保存"。各前缀独立保留 keep 张,
|
||||
既控制总量又保证两种截图都能留存可查。
|
||||
"""
|
||||
try:
|
||||
files = [f for f in os.listdir(dir_path) if f.startswith("ocr_debug_") and f.endswith(".png")]
|
||||
files = [f for f in os.listdir(dir_path)
|
||||
if f.startswith(prefix) and f.endswith(".png")]
|
||||
if len(files) <= keep:
|
||||
return
|
||||
files.sort() # 按字母序 = 时间序
|
||||
for f in files[:-keep]:
|
||||
os.remove(os.path.join(dir_path, f))
|
||||
try:
|
||||
os.remove(os.path.join(dir_path, f))
|
||||
except OSError:
|
||||
pass
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
|
||||
Reference in New Issue
Block a user