WeChat screenshot computer-vision algorithms: card bbox, speaker bands, avatar detection, OCR helpers
Project description
wechat_screenshot_vision_algorithm
微信截图计算机视觉算法库 —— 简历卡片检测、发言人气泡分割、头像检测、OCR 辅助等。
安装
pip install wechat_screenshot_vision_algorithm
# 可选依赖
pip install wechat_screenshot_vision_algorithm[ocr] # PaddleOCR >=3.0
pip install wechat_screenshot_vision_algorithm[png] # Pillow PNG 压缩上传
pip install wechat_screenshot_vision_algorithm[merge] # python-Levenshtein 多页文本合并
包结构
src/wechat_screenshot_vision_algorithm/
├── _config.py # Platform/Profile 解析与参数查表
├── png_utils.py # extras=[png]: PNG 长边压缩
├── algorithms/ # 默认依赖 (numpy + opencv)
│ ├── card_bbox.py # 简历卡片 bbox 检测
│ ├── template_matching.py # 模板匹配原语 (收藏标签/未读分割线/标题栏等)
│ ├── speaker_band.py # 发言人气泡纵向分桶
│ ├── avatar_column.py # 左侧头像列布局检测
│ ├── phash_utils.py # dHash 感知哈希 (滚动到底检测 / 去重)
│ ├── badge_detection.py # 未读红点 HSV 检测
│ └── title_ocr.py # 群聊标题 OCR
├── ocr/ # extras=[ocr]: PaddleOCR ≥3.0
│ ├── text_ocr_adapter.py # PaddleOCR 引擎封装 (UVDoc 已禁用)
│ ├── nickname_binding.py # 昵称归属 (卡片/聊天气泡绑定)
│ ├── avatar_guard.py # 昵称 vs 头像距离守门
│ └── badge_ocr.py # 未读徽章数字 OCR
├── merge/ # extras=[merge]: python-Levenshtein
│ └── multipage.py # N-gram + Levenshtein 多页拼接
├── profiles/ # 平台参数配置
│ ├── android_wechat.py # 安卓微信 (头像左侧, 全部阈值)
│ ├── ios_wechat.py # iOS 微信 (头像右侧, 占位)
│ └── harmony_wechat.py # 鸿蒙微信 (占位)
└── templates/ # package_data (pip install 分发)
└── wechat/{platform}/{version}/
核心概念
Platform / WeChatVersion / Profile
不同平台 (Android / iOS / 鸿蒙) 和微信版本的截图布局不同(头像列位置、颜色阈值、间距等),通过 Profile 统一参数化:
from wechat_screenshot_vision_algorithm import Platform, WeChatVersion, Profile
# 安卓微信 8.0.69
profile = Profile(platform=Platform.ANDROID, wechat_version=WeChatVersion.V8_0_69)
profile.AVATAR_SIDE # "left" — Android 发言人头像在左侧
profile.CORE_THRESHOLD # 0.80 — 模板匹配阈值
profile.VLINE_MIN_SEG_RATIO # 0.08 — 竖线候选列最低方差段占比
profile.templates_dir # Path — 内嵌模板 PNG 目录
所有设计常量基于 1080px 屏幕宽度锚定;运行时按 device_screen_width / 1080 缩放。
模块详解
_config — 配置与 Profile 解析
| 名称 | 类型 | 说明 |
|---|---|---|
Platform |
Enum | ANDROID, IOS, HARMONY 三平台枚举 |
WeChatVersion |
Enum | V8_0_69 微信版本枚举 |
Profile |
Class | 参数束:按 (platform, version) 查表返回阈值常量与模板路径 |
BASELINE_WIDTH |
int | 设计基准宽度 1080px |
Profile 实例的属性委托到对应平台模块(如 profiles/android_wechat.py),包含以下参数类:
卡片 bbox 检测参数: TOP_BAR_BOT_RATIO, BOT_BAR_TOP_RATIO, AVATAR_COLUMN_WIDTH_BASELINE, MIN_CARD_X_GAP_BASELINE, VLINE_MIN_SEG_RATIO, CARD_HSPAN_MIN_RATIO, HLINE_NEAR_VLINE_LIMIT_BASELINE, ZONE_MIN_HEIGHT, ZONE_MIN_SEG, LABEL_INTERSECT_MARGIN, GAP_MERGE_MAX_DIST
模板匹配参数: CORE_THRESHOLD, AUX_THRESHOLD
发言人气泡参数: CHAT_FIRST_BAND_TOP_EXTEND_BASELINE, CHAT_COMPOSER_RESERVE_BOTTOM_BASELINE, 头像 Hough 圆参数等
头像检测参数: LIST_HOUGH_MIN_DIST_BASELINE, LIST_HOUGH_MIN_R_BASELINE, NICKNAME_AVATAR_BIND_MAX_DY_BASELINE 等
卡片点击参数: FAVORITE_LABEL_TEMPLATE_W_BASELINE, FAVORITE_TO_CARD_TOP_OFFSET_BASELINE, REFERENCE_RESUME_CARD_TOP_GAP_BASELINE 等
algorithms.card_bbox — 简历卡片边界框检测
微信聊天列表截图中检测「收藏标签」并推导简历卡片精确 bbox。核心入口 _compute_exact_card_bboxes 基于三区方差 + 竖线分段判断卡片左右边界。
数据结构:
| 名称 | 字段 | 说明 |
|---|---|---|
FavoriteLabelHit |
x, y, w, h, score |
收藏标签模板匹配命中 (y1, y2 属性) |
ThumbnailCard |
index, top, bottom, left, right, click_x, click_y, click_side, favorite_hit |
一张简历卡片的导出矩形(像素坐标) |
BubbleBbox |
top, bottom, left, right |
文本聊天气泡矩形 (卡片 zone 排除后的剩余 zone) |
TrackedCard |
height, clicked |
高度序列去重追踪状态 |
函数:
| 函数 | 签名 | 说明 |
|---|---|---|
_compute_exact_card_bboxes |
(ordered_hits, bgr_img, screen_w, screen_h) → list[ThumbnailCard] |
核心算法:从收藏标签命中 + 截图推导卡片 bbox |
derive_cards |
(...) → list[ThumbnailCard] |
高层入口:整合模板匹配 + 竖线检测,输出卡片列表 |
drop_top_clamped_false_positive_cards |
(cards) → list[ThumbnailCard] |
过滤顶部被截断的误检卡片 |
bbox_to_metadata_list |
(card) → list[int] |
卡片 bbox 转为 [left, top, right, bottom] |
card_bounding_tuple |
(card) → tuple |
转为 (left, top, right, bottom) 元组 |
card_overlaps_processed |
(card, processed_bboxes) → bool |
判断卡片是否与已处理卡片重叠 |
click_context_for_tap_thumbnail |
(card) → dict |
生成 click_context (click_coords + click_side) |
pick_first_unprocessed_card |
(cards, processed_bboxes) → ThumbnailCard|None |
返回第一个不重叠的未处理卡片 (旧版,建议用 pick_next_unclicked_card) |
pick_next_unclicked_card |
(cards, tracked_cards) → ThumbnailCard|None |
基于高度序列匹配的卡片去重,返回下一个未点击卡片 |
y_interval_overlap_ratio |
(top_a, bottom_a, top_b, bottom_b) → float |
两区间的 Y 轴重叠率 |
algorithms.template_matching — 模板匹配原语
所有模板匹配函数 + 布局常量,跨 collector 运行时和回归测试工具共享。
常量:
| 常量 | 说明 |
|---|---|
BASELINE_WIDTH |
基准屏幕宽度 1080 |
CORE_THRESHOLD |
收藏标签/新消息提示的硬阈值 0.80 |
TEMPLATES_DIR |
模板 PNG 目录 (可通过 set_templates_dir 覆盖) |
TEMPLATE_FILE |
模板名到文件名的映射 dict |
函数:
| 函数 | 说明 |
|---|---|
load_template(name) |
加载模板 PNG 返回 BGR numpy 数组 |
set_templates_dir(path) |
覆盖模板目录(iOS / 鸿蒙使用) |
is_wechat_main_conversation_list_chrome(...) |
检测是否为微信主会话列表页面 |
find_favorite_labels(...) |
在截图中匹配收藏标签 |
find_new_messages_hints(...) |
匹配「新消息」分割线 |
find_unread_dividers(...) |
匹配未读消息分割线 |
find_wechat_note_header(...) |
匹配微信笔记标题栏 |
find_chat_back_chevron(...) |
匹配聊天返回箭头 |
find_chat_title_more_dots(...) |
匹配聊天标题栏更多按钮 |
find_chat_input_voice(...) |
匹配语音输入按钮 |
find_chat_input_emoji_smile(...) |
匹配表情按钮 |
find_chat_input_plus(...) |
匹配加号按钮 |
split_conversation_rows(...) |
按头像分割会话列表行 |
algorithms.speaker_band — 发言人气泡纵向分桶
根据聊天截图中的发言人头像位置,划分发言人纵向区间(speaker bands)。
数据结构:
| 名称 | 说明 |
|---|---|
ChatVerticalBounds |
聊天区域纵向边界 (top / bottom / title_bar_bottom / composer_top) |
AvatarTimelineEntry |
单条发言人头像时间线条目 |
PrdChatVerticalLayout |
PRD 规定的聊天纵向布局 (bounds + speaker_bands) |
函数:
| 函数 | 说明 |
|---|---|
compute_chat_content_vertical_bounds(...) |
计算聊天内容的纵向边界 |
detect_chat_side_avatar_ytops(...) |
检测聊天侧发言人头像 Y 坐标 |
merge_avatar_ytops_time_order(...) |
按时间顺序合并头像 Y 坐标 |
build_prd_speaker_vertical_bands(...) |
构建 PRD 规定的发言人纵向分桶 |
algorithms.avatar_column — 左侧头像列布局检测
检测微信会话列表中左侧头像列的布局(数量、Y 坐标等)。
数据结构:
| 名称 | 说明 |
|---|---|
AvatarCentroid |
单个头像质心 (x, y, radius) |
LeftAvatarColumnLayout |
左侧头像列布局 (centroids, median_x, gaps 等) |
函数:
| 函数 | 说明 |
|---|---|
detect_left_avatar_column_layout(...) |
用 Hough 圆检测左侧头像列 |
find_avatar_anchor_for_nickname_bbox(...) |
为昵称 bbox 找到对应的发言人头像锚点 |
nickname_bbox_in_avatar_column(...) |
判断昵称 bbox 是否在头像列区域内 |
left_avatar_ytops(layout) |
提取所有头像的 Y 坐标列表 |
algorithms.phash_utils — 感知哈希与去重
dHash 感知哈希,用于滚动到底检测与会话去重。
数据结构:
| 名称 | 说明 |
|---|---|
PHashResult |
哈希结果 (value, roi_kind, bbox) |
ScrollDuplicationTracker |
滚动去重追踪器 (observe → is_stuck) |
函数:
| 函数 | 说明 |
|---|---|
compute_dhash(bgr, roi_kind) |
计算截图的 dHash |
hamming_distance(a, b) |
两个哈希的汉明距离 |
is_duplicate(ph1, ph2, threshold) |
判断两帧是否重复 |
crop_roi(bgr, roi_kind) |
根据 roi_kind 裁剪 ROI |
manual_labeling_phash_matches(a, b) |
判定手动标注阶段是否匹配 |
algorithms.badge_detection — 未读红点检测
HSV 颜色空间检测微信会话列表中白底红字的未读徽章。
函数:
| 函数 | 说明 |
|---|---|
count_badges_white_on_red(bgr, min_area_px) |
检测全图白底红字徽章数量 |
count_badges_per_row_roi(bgr, avatar_ytops, ...) |
按行 ROI 统计徽章数 |
algorithms.title_ocr — 群聊标题 OCR
函数:
| 函数 | 说明 |
|---|---|
try_ocr_group_chat_title_from_png(png_path, scale_w) |
从 PNG 截图 OCR 群聊标题文本 |
ocr.text_ocr_adapter — PaddleOCR 引擎封装
依赖:
pip install wechat_screenshot_vision_algorithm[ocr]
PaddleOCR 3.x 引擎封装,已显式禁用 UVDoc 矫正(防止坐标变形)。跨 collector 和 housekeeping 后端共享。
数据结构:
| 名称 | 说明 |
|---|---|
OcrPageResult |
OCR 页面结果 (blocks, page_size, engine_info) |
TextBlock |
单个 OCR 文本块 (text, bbox_xyxy, confidence) |
ocr.nickname_binding — 昵称归属
依赖:
pip install wechat_screenshot_vision_algorithm[ocr]
将 OCR 识别出的文本块归因为「昵称」「正文」「丢弃」三类,并绑定到发言人或简历卡片。
数据结构:
| 名称 | 说明 |
|---|---|
NicknameOcrConfig |
昵称 OCR 配置 (阈值、avatar_column_width 等) |
NicknameExtraction |
单条昵称提取结果 |
SpeakerBodySegment |
发言人正文段落 |
ResumeThumbBinding |
简历卡片绑定结果 |
ChatSpeakerAttribution |
完整发言人归属结果 |
函数:
| 函数 | 说明 |
|---|---|
extract_nicknames(...) |
主入口:从 OCR 页面结果提取昵称并归属 |
first_attrib_verbatim_display_line(...) |
获取首次属性的原始显示行 |
ocr.avatar_guard — 头像距离守门
依赖:
pip install wechat_screenshot_vision_algorithm[ocr]
验证 OCR 识别出的昵称文本块与对应发言人头像的距离是否合理。通过拉普拉斯方差、颜色均值、边缘形状等多重信号判断。
函数:
| 函数 | 说明 |
|---|---|
nickname_row_passes_prd_avatar_guard(...) |
PRD 规定的完整头像守门检查 |
nickname_row_passes_avatar_roi(...) |
头像 ROI 区域通过性检查 |
avatar_roi_pass(...) |
头像 ROI 多重信号判断 |
nickname_left_roi_passes_avatar_signal(...) |
昵称左侧 ROI 的头像信号检测 |
ocr.badge_ocr — 未读数字 OCR
依赖:
pip install wechat_screenshot_vision_algorithm[ocr]
对检测到的未读红点区域进行数字 OCR,返回未读计数。
函数:
| 函数 | 说明 |
|---|---|
ocr_unread_badge_digits(bgr, badge_hits) |
对未读徽章区域进行数字 OCR,返回 {index: digit} |
merge.multipage — 多页文本合并
依赖:
pip install wechat_screenshot_vision_algorithm[merge]
将同一简历的多页 OCR 文本合并为完整简历文本。使用 N-gram 重叠检测 + Levenshtein 编辑距离相似度。
函数:
| 函数 | 说明 |
|---|---|
merge_multipage_ocr_texts(pages) |
合并多页 OCR 文本,返回完整简历字符串 |
paragraph_content_bounds(merged, anchor) |
在合并文本中查找锚点所在的段落边界 |
png_utils — PNG 压缩上传
依赖:
pip install wechat_screenshot_vision_algorithm[png]
将截图 PNG 压缩到指定长边尺寸,用于上传前预处理。
函数:
| 函数 | 说明 |
|---|---|
resolved_collector_upload_long_edge_max(argparse_value) |
解析上传 PNG 长边最大像素数(环境变量 COLLECTOR_UPLOAD_LONG_EDGE_MAX / 默认 1920) |
maybe_resize_png_long_edge(png_bytes, long_edge_max) |
如 PNG 长边超过限制则等比缩小 |
使用示例
from wechat_screenshot_vision_algorithm import Platform, WeChatVersion, Profile
# 1. 按平台选择 Profile
profile = Profile(platform=Platform.ANDROID, wechat_version=WeChatVersion.V8_0_69)
# 2. 模板匹配
from wechat_screenshot_vision_algorithm.algorithms.template_matching import (
load_template, find_favorite_labels,
)
import cv2
screenshot = cv2.imread("chat_screenshot.png")
favorite_labels = find_favorite_labels(screenshot, scale_w=screenshot.shape[1] / profile.BASELINE_WIDTH)
# 3. 简历卡片检测
from wechat_screenshot_vision_algorithm.algorithms.card_bbox import (
_compute_exact_card_bboxes, pick_next_unclicked_card,
)
cards = _compute_exact_card_bboxes(favorite_labels, screenshot,
screen_w=screenshot.shape[1],
screen_h=screenshot.shape[0])
print(f"检测到 {len(cards)} 张简历卡片")
# 4. 发言人分桶
from wechat_screenshot_vision_algorithm.algorithms.speaker_band import (
compute_chat_content_vertical_bounds, build_prd_speaker_vertical_bands,
)
bounds = compute_chat_content_vertical_bounds(screenshot, scale_w=1.0)
layout = build_prd_speaker_vertical_bands(screenshot, bounds, scale_w=1.0)
# 5. OCR 昵称提取 (需 pip install wechat_screenshot_vision_algorithm[ocr])
from wechat_screenshot_vision_algorithm.ocr.text_ocr_adapter import OcrPageResult
from wechat_screenshot_vision_algorithm.ocr.nickname_binding import extract_nicknames, NicknameOcrConfig
# ... 初始化 PaddleOCR 并调用 extract_nicknames(ocr_result, config)
开发指南
添加新平台支持
- 在
profiles/下创建新文件(如ios_wechat.py),复制android_wechat.py并修改阈值 - 将模板 PNG 放入
templates/wechat/{platform}/{version}/ - 在
_config.py的_PROFILE_MODULES中注册
本地开发安装
git clone https://github.com/yuyidream/wechat_screenshot_vision_algorithm.git
cd wechat_screenshot_vision_algorithm
pip install -e . # 默认依赖
pip install -e ".[ocr,png,merge]" # 全部可选依赖
运行测试
pytest tests/ -v
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distributions
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file wechat_screenshot_vision_algorithm-0.2.0-py3-none-any.whl.
File metadata
- Download URL: wechat_screenshot_vision_algorithm-0.2.0-py3-none-any.whl
- Upload date:
- Size: 135.8 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.10
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
aa2389dbd0d486ecf9b366c8cbd7eb26ff5b9d073a9bdff90323a30b70a17180
|
|
| MD5 |
4d940dd9de2bbab81396c4acdfb52025
|
|
| BLAKE2b-256 |
f4db8684f1ece2874229e874222e79a3a974f9696122aeac392b3ca0a3f31de7
|