- Add relations/item/abilities preview (serve_relations.py + web/relations/) - Add fetch scripts: hero_items, item_shop, items_meta, hero_abilities, ability_videos, patches, stratz, matchups, portraits - Add overlay.py (role tags + Top-3 cyan marks), recommend.py - Add http_utils.py, loc_format.py, hero_tags.py, item_fears.py - GSI: full payload JSONL dump, foreground window detection - Drop real template library; CDN-only matching - Update docs: CHANGELOG 0.2.0, DESIGN config table, AGENTS module table - .gitignore: exclude large regenerable assets (icons/portraits/videos)
11 KiB
上分帝(Climperor)—— 方案与实施细节
README.md 是操作手册;本文记录背景、选型与关键决策。协作约束见 AGENTS.md。
1. 背景与目标
要解决的问题
在 Dota 2 选将阶段(以及进入游戏后)几秒内自动获取双方 10 个英雄,服务天梯选将辅助或赛后分析。
为什么 GSI 做不到
| 场景 | GSI 可获得的阵容数据 |
|---|---|
| 排位 / 普通 All Pick | 仅自己的 hero.id;draft 通常为空 |
| Captains Mode | 历史上有部分 pick/ban,不稳定 |
| 观战 / 裁判视角 | 阵容字段较全 |
| 赛后 | 需依赖 OpenDota 等外部 API |
Valve 已明确关闭普通玩家视角的实时 draft (#9562、 #7193,not planned)。
候选方案
| 方案 | 结论 |
|---|---|
| 官方 GSI | 拿不到双方 pick |
| Overwolf GEP | 备选,偏重 |
| 读游戏内存 | 排除(合规) |
| 截屏 + 模板匹配 | 选定 |
| 整图问多模态大模型 | 排除为主路径(实测整图几乎全错;裁顶栏后仍不如白名单匹配) |
社区同类工具共同点:裁固定 ROI + 模板匹配 / 小模型,输出约束在英雄白名单内。
2. 技术方案
处理流程
截图 → 相对坐标裁 10 格 →(天梯则遮罩段位条)→ CDN NCC 匹配
→ score + margin 双门控 → hero_key | null
关键决策
① 相对坐标
横向 (cx - W/2) / H,纵向与宽高 / H。同宽高比下分辨率无关;21:9 等需锚点或重标定。
② 单一 CDN 模板库
templates/cdn/{hero}.png 来自 Steam 官方头像,按顶栏实际窗口裁切后写入。
曾尝试「CDN 兜底 + real 实拍」双层库。实测修正 CDN 裁切后 CDN-only 即可全对, 继续攒 real 库性价比低,已放弃(见 §3 发现二)。皮肤靠会话策略,不堆变体模板。
③ 宁可不认,不可乱认
score >= min_score(默认 0.45)margin = Top1 − Top2 >= min_margin(默认 0.04)
④ 会话层处理皮肤与淡入
- 选人阶段:多帧确认、允许更高分改判(
revise_gain) - 决策阶段:只补空槽、禁止改判(避免皮肤顶栏覆盖默认脸结论)
- best 帧优先选人阶段默认脸(
recognized相同时) - 本人英雄以 GSI
hero覆盖视觉结果(只覆盖自己的槽)
⑤ 截图按对局归档
GSI 会话写入 samples/raw/{matchid}/;手动 capture.py 仍写 samples/raw/ 根目录。
结果 JSON 仍为 results/draft_<时间戳>.json(内含 match_id)。
⑥ GSI 全量落盘
gsi.dump_payloads(默认开)把每包 POST body 追加到 samples/raw/{matchid}/gsi.jsonl,
行格式 {"t": <unix>, "payload": <原文>}。内容上限仍是 cfg 订阅字段 + 普通玩家视角;
不能靠落盘补出双方 pick。CLI:--dump-gsi / --no-dump-gsi。
3. 关键实测(2026-07-25)
环境:2560×1440 无边框;人机 + 天梯。
发现一:CDN 裁切窗口决定上限
初版按正方形取中心,比例与顶栏 111×88 不符,均分约 0.65。
网格搜索得到正确窗口 x0=38, w=182, 全高(源 256×144),均分约 0.94。
写入 fetch_cdn_templates.py 的 CROP_X0/CROP_X1 后,CDN-only 30/30 / 40/40。
兜底层差时先查素材处理,再考虑堆数据。
发现二:不必维护 real 库
CDN-only 开箱即用后,「打几十局攒默认脸」不再是主路径。项目改为只维护 CDN。
后续审计(迁出前原型库)进一步确认:
- 标注集上大量
score=1.000往往是「从同帧裁进 real 再评同帧」的自证,夸大了 real 贡献。 - 剩余 real 与 CDN(天梯遮罩)多数 ≥0.85,近乎 CDN 副本;明显偏离的极少(如军团指挥官一类边缘格)。
- 近期问题局(宙斯缺槽、斯拉达 CDN 分低、敌法末位、皮肤帧风行者 0.415)主要靠 多帧会话 + 决策禁改判 + GSI 补自己 凑齐,不是靠 real。
- 皮肤变体极多,入库皮肤模板不合理;段位条/徽章每人不同,匹配时已用遮罩忽略, 也不该为不同段位重复存图。
发现三:自动标定靠玩家颜色条
10 条固定玩家色定位槽位;中心用中位数槽距拟合(剔除被身后头像污染的宽条); 头像下沿用「格内列 vs 格间空隙」亮度差,勿用逐行差分(易误判到名字行)。
发现四:天梯段位条用遮罩
GSI 不含可靠 lobby 类型时,用右下金色勋章检测(has_ranked_overlay),
匹配时屏蔽底部 32% + 右侧 22%。人机无勋章走全图匹配。
发现五:禁用名单不靠模板
网格按主属性分块 + 客户端本地化名行优先排布;禁用/已选卡片对比度塌陷
(std 约 8–21 vs 正常 ≥33)。data/heroes.json 需含 attr / name_loc;
另含手工维护的中文口语 aliases(重拉 CDN 表时合并保留,暂未接入推荐展示)。
格数对不上时 ok=False,不返回残缺名单。
发现六:位置与「我」
定位局位置字用二值 IoU(非 OCR)。「我」优先 GSI team_slot;
否则姓名亮度相对差(最亮比次亮 ≥ 25)兜底。
发现七:客户端顶栏时机(会话策略前提)
这些是实战纠正后的事实,改 DraftSession 时不要违背:
| 现象 | 含义 |
|---|---|
| 顶栏默认脸 → 皮肤 | 全员选完后才换皮肤立绘,不是一进决策就换 |
本机已进 STRATEGY_TIME |
别人可能还在选;不能一进决策就永久停视觉 |
| 空槽红旗倒计时 | 只出现在未选槽;揭晓后消失,不是挡脸主因 |
| GSI | 没有「皮肤已加载」字段;无法精确卡「人选完、皮肤前」单帧 |
因此正确节奏是:选人 + 决策前期持续读,直到确认 10/10 或 strategy_tail_polls 用尽;
凑齐后停视觉,再短等 GSI 补本人英雄。单帧皮肤弱识别(如决策 3D 模型挡顶栏)属预期,
靠会话回填,不靠降阈值或堆皮肤模板。
发现八:揭晓淡入需要可改判
首帧揭晓常半透明 + 段位条,易误认(例:剑圣揭晓瞬间被认成幽鬼)。
confirm_polls 后若仍禁止改判会锁死错误。选人阶段允许
score ≥ was_score + revise_gain 的稳定新结果覆盖;决策阶段则关闭改判。
4. 代码结构
climperor/
├── config.json
├── data/ # heroes / relations / items / abilities / fears …
├── common.py / recognize.py / draft_session.py
├── gsi_watch.py / gsi_setup.py / overlay.py
├── recommend.py / relations.py / roles.py / grid.py / modes.py
├── http_utils.py / loc_format.py / hero_tags.py / item_fears.py
├── fetch_cdn_templates.py / fetch_hero_portraits.py / fetch_hero_items.py
├── fetch_item_shop.py / fetch_items_meta.py / fetch_hero_abilities.py
├── fetch_ability_videos.py / fetch_stratz.py / audit_relations.py
├── fetch_patches.py / import_relations_xlsx.py / serve_relations.py
├── autocalibrate.py / calibrate.py / capture.py / evaluate.py
├── web/relations/ # 关系 / 物品 / 版本只读预览页
├── templates/cdn|roles|modes/
├── assets/ # portraits / icons / videos(部分 gitignore)
└── samples/labels.json # 评测真值;raw/<matchid>/ 见 .gitignore
运行时目录 preview/、results/、failures/、samples/raw/(含按对局子目录)不入库。
主要配置
| 字段 | 含义 | 默认 |
|---|---|---|
match.min_score / min_margin |
识别门控 | 0.45 / 0.04 |
match.ranked_mask |
天梯遮罩比例 | bottom 0.32, right 0.22 |
gsi.port |
监听端口 | 3223 |
gsi.confirm_polls |
连续同结果帧数 | 2 |
gsi.revise_gain |
选人阶段改判所需分数增益 | 0.15 |
gsi.strategy_tail_polls |
决策阶段继续视觉轮询 | 8 |
gsi.strategy_gsi_wait |
视觉结束后等待本人 GSI 英雄 | 3.0 |
gsi.require_foreground |
仅当前台为 dota2.exe 时截屏识别 |
true |
gsi.dump_payloads |
全量 GSI JSONL 落盘 | true |
recommend.enabled |
选将 Top-3 推荐 | true |
recommend.top_n |
推荐数量 | 3 |
recommend.relations_path |
定性关系文件 | data/relations.json |
recommend.role_tags |
分路→角色标签过滤(1–5 号位) | 见 config.json |
overlay.enabled |
选将顶栏角色标签悬浮层 | true |
overlay.y_gap_rel / icon_h_rel / icon_gap_rel |
标签相对屏幕高的间距与尺寸 | 0.008 / 0.016 / 0.002 |
overlay.mark_size_rel / mark_pad_rel |
网格青标方标尺寸与内边距 | 0.018 / 0.004 |
overlay.rec_color / rec_text_color |
青标底色 / 名次文字色 | #2ec4b6 / #0b1220 |
5. GSI 自动化
Dota 2 (-gamestateintegration)
→ POST → gsi_watch.py :3223
→ samples/raw/{matchid}/gsi.jsonl(可选)
→ DraftSession 轮询 recognize + roles/grid/modes
→ results/draft_*.json
要点:按 matchid 去重;截图进 samples/raw/{matchid}/;识别在工作线程;
未标定时降级为只截图。
天梯 AP 选人按 2/2/1 成批揭晓,本轮结束前互不可见——因此必须在 HERO_SELECTION 跟踪,不能只在决策时间截一张终局图;决策前期仍读几帧 以吃到最后一人揭晓(见发现七)。
6. 选将 Top-3 推荐
- 分路:定位匹配画面字(
roles.py)+ GSIteam_slot定位本人;不做截屏认自己。 - 关系模型:机制克制/搭档(
data/relations.json),不用胜率/场次——版本会变,机制边相对稳。 - 三视图:克制(我克谁)/ 被克制(谁克我)/ 搭档;被克制由有向克制边反查。
- 打分:候选 +1×克到的敌 / −1×被敌克 / +1×己方搭档;再按分路角色标签过滤。
- 展示:网格左上角青色方标 + 名次 1/2/3(区别于 Plus 右上三角)。
- 预览:
serve_relations.py仿选将网格只读查看;数据手改data/relations.json或从 xlsx 导入。 另有顶级 物品 页(基础/升级,网格↔竖式布局),目录见data/item_shop.json。 - 不做:避用标、Steam 登录页、爬 Dotabuff;关系不写入
data/heroes.json。
7. 与 dota2-hex
独立验证项目。可选:Rust 重写并入,或本地旁路 HTTP 回传。
合规同 dota2-hex:屏幕可见信息 only。
8. 参考
- Valve #9562
- Valve #14915(开发者 OCR ≈85%)
- Overwolf Dota 2 GEP
- Steam CDN:
https://cdn.cloudflare.steamstatic.com/apps/dota2/images/dota_react/heroes/{key}.png