screenshot-vision-algorithm 0.3.5__tar.gz → 0.3.10__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {screenshot_vision_algorithm-0.3.5 → screenshot_vision_algorithm-0.3.10}/PKG-INFO +1 -1
- {screenshot_vision_algorithm-0.3.5 → screenshot_vision_algorithm-0.3.10}/pyproject.toml +1 -1
- {screenshot_vision_algorithm-0.3.5 → screenshot_vision_algorithm-0.3.10}/src/screenshot_vision_algorithm/android/wechat/algorithms/speaker_band.py +12 -4
- {screenshot_vision_algorithm-0.3.5 → screenshot_vision_algorithm-0.3.10}/src/screenshot_vision_algorithm/android/wechat/ocr/avatar_guard.py +1 -1
- {screenshot_vision_algorithm-0.3.5 → screenshot_vision_algorithm-0.3.10}/src/screenshot_vision_algorithm/android/wechat/ocr/nickname_binding.py +78 -15
- {screenshot_vision_algorithm-0.3.5 → screenshot_vision_algorithm-0.3.10}/src/screenshot_vision_algorithm.egg-info/PKG-INFO +1 -1
- {screenshot_vision_algorithm-0.3.5 → screenshot_vision_algorithm-0.3.10}/README.md +0 -0
- {screenshot_vision_algorithm-0.3.5 → screenshot_vision_algorithm-0.3.10}/setup.cfg +0 -0
- {screenshot_vision_algorithm-0.3.5 → screenshot_vision_algorithm-0.3.10}/src/screenshot_vision_algorithm/__init__.py +0 -0
- {screenshot_vision_algorithm-0.3.5 → screenshot_vision_algorithm-0.3.10}/src/screenshot_vision_algorithm/_config.py +0 -0
- {screenshot_vision_algorithm-0.3.5 → screenshot_vision_algorithm-0.3.10}/src/screenshot_vision_algorithm/android/__init__.py +0 -0
- {screenshot_vision_algorithm-0.3.5 → screenshot_vision_algorithm-0.3.10}/src/screenshot_vision_algorithm/android/wechat/__init__.py +0 -0
- {screenshot_vision_algorithm-0.3.5 → screenshot_vision_algorithm-0.3.10}/src/screenshot_vision_algorithm/android/wechat/algorithms/__init__.py +0 -0
- {screenshot_vision_algorithm-0.3.5 → screenshot_vision_algorithm-0.3.10}/src/screenshot_vision_algorithm/android/wechat/algorithms/avatar_column.py +0 -0
- {screenshot_vision_algorithm-0.3.5 → screenshot_vision_algorithm-0.3.10}/src/screenshot_vision_algorithm/android/wechat/algorithms/badge_detection.py +0 -0
- {screenshot_vision_algorithm-0.3.5 → screenshot_vision_algorithm-0.3.10}/src/screenshot_vision_algorithm/android/wechat/algorithms/card_bbox.py +0 -0
- {screenshot_vision_algorithm-0.3.5 → screenshot_vision_algorithm-0.3.10}/src/screenshot_vision_algorithm/android/wechat/algorithms/phash_utils.py +0 -0
- {screenshot_vision_algorithm-0.3.5 → screenshot_vision_algorithm-0.3.10}/src/screenshot_vision_algorithm/android/wechat/algorithms/template_matching.py +0 -0
- {screenshot_vision_algorithm-0.3.5 → screenshot_vision_algorithm-0.3.10}/src/screenshot_vision_algorithm/android/wechat/algorithms/title_ocr.py +0 -0
- {screenshot_vision_algorithm-0.3.5 → screenshot_vision_algorithm-0.3.10}/src/screenshot_vision_algorithm/android/wechat/merge/__init__.py +0 -0
- {screenshot_vision_algorithm-0.3.5 → screenshot_vision_algorithm-0.3.10}/src/screenshot_vision_algorithm/android/wechat/merge/multipage.py +0 -0
- {screenshot_vision_algorithm-0.3.5 → screenshot_vision_algorithm-0.3.10}/src/screenshot_vision_algorithm/android/wechat/ocr/__init__.py +0 -0
- {screenshot_vision_algorithm-0.3.5 → screenshot_vision_algorithm-0.3.10}/src/screenshot_vision_algorithm/android/wechat/ocr/badge_ocr.py +0 -0
- {screenshot_vision_algorithm-0.3.5 → screenshot_vision_algorithm-0.3.10}/src/screenshot_vision_algorithm/android/wechat/ocr/text_ocr_adapter.py +0 -0
- {screenshot_vision_algorithm-0.3.5 → screenshot_vision_algorithm-0.3.10}/src/screenshot_vision_algorithm/android/wechat/profiles/__init__.py +0 -0
- {screenshot_vision_algorithm-0.3.5 → screenshot_vision_algorithm-0.3.10}/src/screenshot_vision_algorithm/android/wechat/profiles/android.py +0 -0
- {screenshot_vision_algorithm-0.3.5 → screenshot_vision_algorithm-0.3.10}/src/screenshot_vision_algorithm/android/wechat/profiles/harmony.py +0 -0
- {screenshot_vision_algorithm-0.3.5 → screenshot_vision_algorithm-0.3.10}/src/screenshot_vision_algorithm/android/wechat/profiles/ios.py +0 -0
- {screenshot_vision_algorithm-0.3.5 → screenshot_vision_algorithm-0.3.10}/src/screenshot_vision_algorithm/android/wechat/templates/android/8.0.69/chat_back_chevron.png +0 -0
- {screenshot_vision_algorithm-0.3.5 → screenshot_vision_algorithm-0.3.10}/src/screenshot_vision_algorithm/android/wechat/templates/android/8.0.69/chat_input_emoji_smile.png +0 -0
- {screenshot_vision_algorithm-0.3.5 → screenshot_vision_algorithm-0.3.10}/src/screenshot_vision_algorithm/android/wechat/templates/android/8.0.69/chat_input_plus.png +0 -0
- {screenshot_vision_algorithm-0.3.5 → screenshot_vision_algorithm-0.3.10}/src/screenshot_vision_algorithm/android/wechat/templates/android/8.0.69/chat_input_voice.png +0 -0
- {screenshot_vision_algorithm-0.3.5 → screenshot_vision_algorithm-0.3.10}/src/screenshot_vision_algorithm/android/wechat/templates/android/8.0.69/chat_title_more_dots.png +0 -0
- {screenshot_vision_algorithm-0.3.5 → screenshot_vision_algorithm-0.3.10}/src/screenshot_vision_algorithm/android/wechat/templates/android/8.0.69/favorite_label.png +0 -0
- {screenshot_vision_algorithm-0.3.5 → screenshot_vision_algorithm-0.3.10}/src/screenshot_vision_algorithm/android/wechat/templates/android/8.0.69/new_messages_hint_suffix.png +0 -0
- {screenshot_vision_algorithm-0.3.5 → screenshot_vision_algorithm-0.3.10}/src/screenshot_vision_algorithm/android/wechat/templates/android/8.0.69/unread_divider_hint.png +0 -0
- {screenshot_vision_algorithm-0.3.5 → screenshot_vision_algorithm-0.3.10}/src/screenshot_vision_algorithm/android/wechat/templates/android/8.0.69/unread_divider_hint_v2_textonly.png +0 -0
- {screenshot_vision_algorithm-0.3.5 → screenshot_vision_algorithm-0.3.10}/src/screenshot_vision_algorithm/android/wechat/templates/android/8.0.69/wechat_note_header.png +0 -0
- {screenshot_vision_algorithm-0.3.5 → screenshot_vision_algorithm-0.3.10}/src/screenshot_vision_algorithm/android/xhs/__init__.py +0 -0
- {screenshot_vision_algorithm-0.3.5 → screenshot_vision_algorithm-0.3.10}/src/screenshot_vision_algorithm/android/zhihu/__init__.py +0 -0
- {screenshot_vision_algorithm-0.3.5 → screenshot_vision_algorithm-0.3.10}/src/screenshot_vision_algorithm/logging.py +0 -0
- {screenshot_vision_algorithm-0.3.5 → screenshot_vision_algorithm-0.3.10}/src/screenshot_vision_algorithm/png_utils.py +0 -0
- {screenshot_vision_algorithm-0.3.5 → screenshot_vision_algorithm-0.3.10}/src/screenshot_vision_algorithm.egg-info/SOURCES.txt +0 -0
- {screenshot_vision_algorithm-0.3.5 → screenshot_vision_algorithm-0.3.10}/src/screenshot_vision_algorithm.egg-info/dependency_links.txt +0 -0
- {screenshot_vision_algorithm-0.3.5 → screenshot_vision_algorithm-0.3.10}/src/screenshot_vision_algorithm.egg-info/requires.txt +0 -0
- {screenshot_vision_algorithm-0.3.5 → screenshot_vision_algorithm-0.3.10}/src/screenshot_vision_algorithm.egg-info/top_level.txt +0 -0
- {screenshot_vision_algorithm-0.3.5 → screenshot_vision_algorithm-0.3.10}/tests/test_card_bbox.py +0 -0
- {screenshot_vision_algorithm-0.3.5 → screenshot_vision_algorithm-0.3.10}/tests/test_package.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: screenshot_vision_algorithm
|
|
3
|
-
Version: 0.3.
|
|
3
|
+
Version: 0.3.10
|
|
4
4
|
Summary: Multi-platform screenshot vision algorithms: WeChat (card bbox, speaker bands, avatar detection, OCR helpers), XHS & Zhihu (planned)
|
|
5
5
|
License: MIT
|
|
6
6
|
Requires-Python: >=3.10
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "screenshot_vision_algorithm"
|
|
7
|
-
version = "0.3.
|
|
7
|
+
version = "0.3.10"
|
|
8
8
|
description = "Multi-platform screenshot vision algorithms: WeChat (card bbox, speaker bands, avatar detection, OCR helpers), XHS & Zhihu (planned)"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
license = { text = "MIT" }
|
|
@@ -26,7 +26,7 @@ from screenshot_vision_algorithm.android.wechat.algorithms.template_matching imp
|
|
|
26
26
|
CHAT_BAND_FIRST_AVATAR_TOP_GAP_REJECT_BASELINE = 30
|
|
27
27
|
|
|
28
28
|
#: 首个发言人筒上移量(一个头像高度,防止几何误差),见 PRD §5 补充2。
|
|
29
|
-
CHAT_FIRST_BAND_TOP_EXTEND_BASELINE =
|
|
29
|
+
CHAT_FIRST_BAND_TOP_EXTEND_BASELINE = 20
|
|
30
30
|
|
|
31
31
|
#: 底部输入区预留(与主列表 scanner 一脉,baseline)。
|
|
32
32
|
CHAT_COMPOSER_RESERVE_BOTTOM_BASELINE = 160
|
|
@@ -173,7 +173,7 @@ def detect_chat_side_avatar_ytops(
|
|
|
173
173
|
y1 = max(0, bounds.y_top)
|
|
174
174
|
y2 = min(h, bounds.y_bottom_excl)
|
|
175
175
|
try:
|
|
176
|
-
from
|
|
176
|
+
from screenshot_vision_algorithm.android.wechat.algorithms.avatar_column import (
|
|
177
177
|
detect_left_avatar_column_layout,
|
|
178
178
|
left_avatar_ytops,
|
|
179
179
|
)
|
|
@@ -221,6 +221,7 @@ def build_prd_speaker_vertical_bands(
|
|
|
221
221
|
scale_w: float,
|
|
222
222
|
first_avatar_gap_reject_baseline: int = CHAT_BAND_FIRST_AVATAR_TOP_GAP_REJECT_BASELINE,
|
|
223
223
|
first_band_top_extend_baseline: int = CHAT_FIRST_BAND_TOP_EXTEND_BASELINE,
|
|
224
|
+
avatar_merge_min_sep_baseline: int = 24,
|
|
224
225
|
) -> PrdChatVerticalLayout:
|
|
225
226
|
"""由时间序头像顶构造 PRD §5 顶隙 orphan + 左闭右开发言人筒。
|
|
226
227
|
|
|
@@ -248,13 +249,13 @@ def build_prd_speaker_vertical_bands(
|
|
|
248
249
|
|
|
249
250
|
# 时间轴序去重:过近视为同一人头像重复检出
|
|
250
251
|
merged_starts: list[int] = []
|
|
251
|
-
min_sep = int(round(
|
|
252
|
+
min_sep = int(round(int(avatar_merge_min_sep_baseline) * sw))
|
|
252
253
|
for e in timeline:
|
|
253
254
|
y = int(e.y_top)
|
|
254
255
|
if not merged_starts or y - merged_starts[-1] >= min_sep:
|
|
255
256
|
merged_starts.append(y)
|
|
256
257
|
|
|
257
|
-
# 首个筒上界上移(PRD §5 补充2):上移
|
|
258
|
+
# 首个筒上界上移(PRD §5 补充2):上移 20px @1080,防几何误差与系统变化。
|
|
258
259
|
first_band_extend = int(round(int(first_band_top_extend_baseline) * sw))
|
|
259
260
|
|
|
260
261
|
bands: list[tuple[int, int]] = []
|
|
@@ -275,6 +276,13 @@ def build_prd_speaker_vertical_bands(
|
|
|
275
276
|
elif first_band_start < orphan_top[1]:
|
|
276
277
|
orphan_top = (orphan_top[0], first_band_start)
|
|
277
278
|
|
|
279
|
+
# 无 orphan 时,首个 band 上界对齐 y_ct,保证纵向连续全覆盖
|
|
280
|
+
# (extend 较小且头像紧贴 y_ct 时可能产生小间隙)
|
|
281
|
+
if orphan_top is None and bands:
|
|
282
|
+
first_y0, first_y1 = bands[0]
|
|
283
|
+
if first_y0 > y_ct:
|
|
284
|
+
bands[0] = (y_ct, first_y1)
|
|
285
|
+
|
|
278
286
|
return PrdChatVerticalLayout(
|
|
279
287
|
content_bounds=bounds,
|
|
280
288
|
orphan_top_band=orphan_top,
|
|
@@ -19,7 +19,7 @@ except ImportError:
|
|
|
19
19
|
cv2 = None # type: ignore[assignment]
|
|
20
20
|
|
|
21
21
|
try:
|
|
22
|
-
from
|
|
22
|
+
from screenshot_vision_algorithm.android.wechat.algorithms.avatar_column import AvatarCentroid
|
|
23
23
|
except ImportError:
|
|
24
24
|
AvatarCentroid = None # type: ignore[misc, assignment]
|
|
25
25
|
|
|
@@ -230,6 +230,32 @@ class NicknameOcrConfig:
|
|
|
230
230
|
"""Prefix / substring tags that exclude a block from nickname
|
|
231
231
|
consideration even if all geometric criteria pass."""
|
|
232
232
|
|
|
233
|
+
# ── 以下字段可由 rules_config.yaml 覆盖(housekeeping 胶水层读 yaml → 灌参)──
|
|
234
|
+
|
|
235
|
+
skip_block_patterns: tuple[str, ...] = ()
|
|
236
|
+
"""附加正则列表,逐条编译后传递给 ``_should_skip_block``。
|
|
237
|
+
内置正则(时间戳/纯数字/系统提示等)**始终并行生效**,
|
|
238
|
+
config 提供的是追加规则,不替代内置逻辑。"""
|
|
239
|
+
|
|
240
|
+
inside_y_tolerance_px: float = 4.0
|
|
241
|
+
"""``_completely_inside`` 的 Y 方向容差(px @ 1080 基准 × scale_w)。
|
|
242
|
+
默认 4.0 与 ``card_binding.inside_y_tolerance_px`` 对齐。"""
|
|
243
|
+
|
|
244
|
+
cross_band_distance_threshold_px: float = 50.0
|
|
245
|
+
"""Step C 跨 band 距离门控基线(px @ 1080 × scale_w)。
|
|
246
|
+
文字底部到卡片所属 speaker_band 顶部超过此值则不作为昵称。"""
|
|
247
|
+
|
|
248
|
+
# ── speaker_band 参数(同名传给 build_prd_speaker_vertical_bands)──
|
|
249
|
+
|
|
250
|
+
first_avatar_gap_reject_baseline: int = 30
|
|
251
|
+
"""orphan 间隙阈值(px @ 1080)。"""
|
|
252
|
+
|
|
253
|
+
first_band_top_extend_baseline: int = 20
|
|
254
|
+
"""首个 band 上移量(px @ 1080)。"""
|
|
255
|
+
|
|
256
|
+
avatar_merge_min_sep_px: int = 24
|
|
257
|
+
"""头像合并最小间距(px @ 1080 × scale_w)。"""
|
|
258
|
+
|
|
233
259
|
|
|
234
260
|
_ATTRIBUTION_PREFIX_DROPS: tuple[str, ...] = (
|
|
235
261
|
"撤回了一条消息",
|
|
@@ -351,6 +377,9 @@ _PURE_TIME_RE = re.compile(r"^\d{1,2}:\d{2}(:\d{2})?$")
|
|
|
351
377
|
_WEEKDAY_TIME_RE = re.compile(
|
|
352
378
|
r"^周[一二三四五六日天].*?(?:上午|下午|晚上|凌晨)?\s*\d{0,2}[::]?\d{0,2}"
|
|
353
379
|
)
|
|
380
|
+
_YESTERDAY_TODAY_TIME_RE = re.compile(
|
|
381
|
+
r"^(?:昨天|今天)\s*\d{0,2}:?\d{0,2}$"
|
|
382
|
+
)
|
|
354
383
|
_MOBILE_PHONE_SUFFIX_RE = re.compile(r"1\d{10}\s*$")
|
|
355
384
|
|
|
356
385
|
|
|
@@ -388,6 +417,8 @@ def _is_chat_timestamp_or_divider(text: str) -> bool:
|
|
|
388
417
|
return True
|
|
389
418
|
if _WEEKDAY_TIME_RE.match(t):
|
|
390
419
|
return True
|
|
420
|
+
if _YESTERDAY_TODAY_TIME_RE.match(t):
|
|
421
|
+
return True
|
|
391
422
|
return False
|
|
392
423
|
|
|
393
424
|
|
|
@@ -411,7 +442,7 @@ def _is_avatar_column_nickname_row(
|
|
|
411
442
|
avatar_layout: Any = None,
|
|
412
443
|
) -> bool:
|
|
413
444
|
try:
|
|
414
|
-
from
|
|
445
|
+
from screenshot_vision_algorithm.android.wechat.algorithms.avatar_column import nickname_bbox_in_avatar_column
|
|
415
446
|
except ImportError:
|
|
416
447
|
nickname_bbox_in_avatar_column = None # type: ignore[assignment,misc]
|
|
417
448
|
if avatar_layout is not None and nickname_bbox_in_avatar_column is not None:
|
|
@@ -446,7 +477,7 @@ def _avatar_anchor_for_block(
|
|
|
446
477
|
if avatar_layout is None or getattr(avatar_layout, "empty", True):
|
|
447
478
|
return None
|
|
448
479
|
try:
|
|
449
|
-
from
|
|
480
|
+
from screenshot_vision_algorithm.android.wechat.algorithms.avatar_column import find_avatar_anchor_for_nickname_bbox
|
|
450
481
|
except ImportError:
|
|
451
482
|
return None
|
|
452
483
|
return find_avatar_anchor_for_nickname_bbox(
|
|
@@ -463,9 +494,9 @@ def _load_left_avatar_layout(
|
|
|
463
494
|
if png_path is None:
|
|
464
495
|
return None
|
|
465
496
|
try:
|
|
466
|
-
from
|
|
467
|
-
from
|
|
468
|
-
from
|
|
497
|
+
from screenshot_vision_algorithm.android.wechat.algorithms.speaker_band import compute_chat_content_vertical_bounds
|
|
498
|
+
from screenshot_vision_algorithm.android.wechat.algorithms.avatar_column import detect_left_avatar_column_layout
|
|
499
|
+
from screenshot_vision_algorithm.android.wechat.ocr.avatar_guard import load_png_bgr
|
|
469
500
|
except ImportError:
|
|
470
501
|
return None
|
|
471
502
|
arr = load_png_bgr(str(png_path))
|
|
@@ -595,7 +626,7 @@ def _nickname_passes_avatar_guard(
|
|
|
595
626
|
if bgr_guard is None:
|
|
596
627
|
return not config.require_avatar_guard_for_nickname
|
|
597
628
|
try:
|
|
598
|
-
from
|
|
629
|
+
from screenshot_vision_algorithm.android.wechat.ocr.avatar_guard import avatar_roi_pass
|
|
599
630
|
except ImportError:
|
|
600
631
|
return not config.require_avatar_guard_for_nickname
|
|
601
632
|
|
|
@@ -687,13 +718,13 @@ def _load_prd_layout_optional(
|
|
|
687
718
|
if png_path is None or not config.use_prd_chat_vertical_bands:
|
|
688
719
|
return None
|
|
689
720
|
try:
|
|
690
|
-
from
|
|
721
|
+
from screenshot_vision_algorithm.android.wechat.algorithms.speaker_band import (
|
|
691
722
|
build_prd_speaker_vertical_bands,
|
|
692
723
|
compute_chat_content_vertical_bounds,
|
|
693
724
|
detect_chat_side_avatar_ytops,
|
|
694
725
|
merge_avatar_ytops_time_order,
|
|
695
726
|
)
|
|
696
|
-
from
|
|
727
|
+
from screenshot_vision_algorithm.android.wechat.ocr.avatar_guard import load_png_bgr
|
|
697
728
|
except ImportError:
|
|
698
729
|
return None
|
|
699
730
|
arr = load_png_bgr(str(png_path))
|
|
@@ -705,7 +736,12 @@ def _load_prd_layout_optional(
|
|
|
705
736
|
bounds = compute_chat_content_vertical_bounds(arr, sw, screen_h=oh)
|
|
706
737
|
ly, ry = detect_chat_side_avatar_ytops(arr, sw, bounds)
|
|
707
738
|
timeline = merge_avatar_ytops_time_order(ly, ry)
|
|
708
|
-
return build_prd_speaker_vertical_bands(
|
|
739
|
+
return build_prd_speaker_vertical_bands(
|
|
740
|
+
timeline, bounds, scale_w=sw,
|
|
741
|
+
first_avatar_gap_reject_baseline=config.first_avatar_gap_reject_baseline,
|
|
742
|
+
first_band_top_extend_baseline=config.first_band_top_extend_baseline,
|
|
743
|
+
avatar_merge_min_sep_baseline=config.avatar_merge_min_sep_px,
|
|
744
|
+
)
|
|
709
745
|
|
|
710
746
|
|
|
711
747
|
def _finalize_prd_attribution(
|
|
@@ -882,7 +918,7 @@ def _resume_thumb_bindings_and_orphans(
|
|
|
882
918
|
# OCR bbox 与卡片 bbox 各有 ±2~3px 抖动:卡内标题行可能比卡顶高出 1~3px,
|
|
883
919
|
# 若按严格包含判定会被误认为"卡外文字"而误绑为昵称(2026-06-11 真实案例)。
|
|
884
920
|
# 真昵称行整体位于卡片上方(y2 < 卡顶),不受此容差影响。
|
|
885
|
-
_inside_tol =
|
|
921
|
+
_inside_tol = float(config.inside_y_tolerance_px) * max(scale_w, 1.0)
|
|
886
922
|
|
|
887
923
|
def _completely_inside(block_bbox: tuple, card_bbox: tuple) -> bool:
|
|
888
924
|
bx1, by1, bx2, by2 = block_bbox
|
|
@@ -912,8 +948,25 @@ def _resume_thumb_bindings_and_orphans(
|
|
|
912
948
|
return True
|
|
913
949
|
if _is_favorite_card_label(t):
|
|
914
950
|
return True
|
|
951
|
+
# ── 附加正则(来自 rules_config.yaml skip_patterns,编译后缓存)──
|
|
952
|
+
if _compiled_skip_patterns:
|
|
953
|
+
for _pat in _compiled_skip_patterns:
|
|
954
|
+
try:
|
|
955
|
+
if _pat.search(t):
|
|
956
|
+
return True
|
|
957
|
+
except Exception:
|
|
958
|
+
continue
|
|
915
959
|
return False
|
|
916
960
|
|
|
961
|
+
# ── 编译 config 追加的正则(函数内缓存,避免每次 block 重新编译)──
|
|
962
|
+
_compiled_skip_patterns: list[re.Pattern] = []
|
|
963
|
+
if config.skip_block_patterns:
|
|
964
|
+
for p in config.skip_block_patterns:
|
|
965
|
+
try:
|
|
966
|
+
_compiled_skip_patterns.append(re.compile(p))
|
|
967
|
+
except re.error:
|
|
968
|
+
pass
|
|
969
|
+
|
|
917
970
|
bindings: list[ResumeThumbBinding] = []
|
|
918
971
|
orphan = 0
|
|
919
972
|
excluded_indices: set[int] = set()
|
|
@@ -962,7 +1015,17 @@ def _resume_thumb_bindings_and_orphans(
|
|
|
962
1015
|
_cy1, _cy2 = float(tbb[1]), float(tbb[3])
|
|
963
1016
|
for _bi, (_b0, _b1) in enumerate(_sb_list):
|
|
964
1017
|
if _cy2 > float(_b0) and _cy1 < float(_b1):
|
|
965
|
-
|
|
1018
|
+
band_idx = _bi # 相交 → 候选;持续覆盖 → 最后一个(下方)胜出
|
|
1019
|
+
|
|
1020
|
+
# ── 如果卡片与 orphan_top_band 相交 → 直接记为找不到归属 ──
|
|
1021
|
+
if layout_opt is not None:
|
|
1022
|
+
ot = getattr(layout_opt, "orphan_top_band", None)
|
|
1023
|
+
if ot is not None:
|
|
1024
|
+
oy0, oy1 = float(ot[0]), float(ot[1])
|
|
1025
|
+
_cy1, _cy2 = float(tbb[1]), float(tbb[3])
|
|
1026
|
+
if _cy2 > oy0 and _cy1 < oy1:
|
|
1027
|
+
orphan += 1
|
|
1028
|
+
continue
|
|
966
1029
|
|
|
967
1030
|
matched_nickname: Optional[str] = None
|
|
968
1031
|
|
|
@@ -1053,14 +1116,14 @@ def _resume_thumb_bindings_and_orphans(
|
|
|
1053
1116
|
|
|
1054
1117
|
if best_i is not None:
|
|
1055
1118
|
# PRD §B.1 C:距离检查——文字底部到卡片所属 speaker_band 顶部
|
|
1056
|
-
# 超过阈值(
|
|
1119
|
+
# 超过阈值(config.cross_band_distance_threshold_px × scale_w)则不作为昵称
|
|
1057
1120
|
_use_as_nickname = True
|
|
1058
1121
|
if band_idx is not None and band_idx >= 0:
|
|
1059
1122
|
_sb_list = getattr(layout_opt, "speaker_bands", ())
|
|
1060
1123
|
if band_idx < len(_sb_list):
|
|
1061
1124
|
_band_y0 = float(_sb_list[band_idx][0])
|
|
1062
1125
|
_gap = _band_y0 - best_bottom # positive → block above band
|
|
1063
|
-
_threshold =
|
|
1126
|
+
_threshold = float(config.cross_band_distance_threshold_px) * float(scale_w)
|
|
1064
1127
|
if _gap > _threshold:
|
|
1065
1128
|
_use_as_nickname = False
|
|
1066
1129
|
if _use_as_nickname:
|
|
@@ -1177,7 +1240,7 @@ def first_attrib_verbatim_display_line_and_bbox(
|
|
|
1177
1240
|
scale_w = float(original_width) / 1080.0
|
|
1178
1241
|
if png_path is not None:
|
|
1179
1242
|
try:
|
|
1180
|
-
from
|
|
1243
|
+
from screenshot_vision_algorithm.android.wechat.ocr.avatar_guard import load_png_bgr
|
|
1181
1244
|
|
|
1182
1245
|
bgr_guard = load_png_bgr(str(png_path))
|
|
1183
1246
|
except ImportError:
|
|
@@ -1748,7 +1811,7 @@ def extract_nicknames(
|
|
|
1748
1811
|
bgr_guard = None
|
|
1749
1812
|
if png_path is not None:
|
|
1750
1813
|
try:
|
|
1751
|
-
from
|
|
1814
|
+
from screenshot_vision_algorithm.android.wechat.ocr.avatar_guard import load_png_bgr
|
|
1752
1815
|
|
|
1753
1816
|
bgr_guard = load_png_bgr(str(png_path))
|
|
1754
1817
|
except ImportError:
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: screenshot_vision_algorithm
|
|
3
|
-
Version: 0.3.
|
|
3
|
+
Version: 0.3.10
|
|
4
4
|
Summary: Multi-platform screenshot vision algorithms: WeChat (card bbox, speaker bands, avatar detection, OCR helpers), XHS & Zhihu (planned)
|
|
5
5
|
License: MIT
|
|
6
6
|
Requires-Python: >=3.10
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{screenshot_vision_algorithm-0.3.5 → screenshot_vision_algorithm-0.3.10}/tests/test_card_bbox.py
RENAMED
|
File without changes
|
{screenshot_vision_algorithm-0.3.5 → screenshot_vision_algorithm-0.3.10}/tests/test_package.py
RENAMED
|
File without changes
|