# 上分帝(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](https://github.com/ValveSoftware/Dota2-Gameplay/issues/9562)、 [#7193](https://github.com/ValveSoftware/Dota2-Gameplay/issues/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 帧优先选人阶段默认脸 --- ## 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。 ### 发现三:自动标定靠玩家颜色条 10 条固定玩家色定位槽位;中心用中位数槽距拟合(剔除被身后头像污染的宽条); 头像下沿用「格内列 vs 格间空隙」亮度差,勿用逐行差分(易误判到名字行)。 ### 发现四:天梯段位条用遮罩 GSI 不含可靠 lobby 类型时,用右下金色勋章检测(`has_ranked_overlay`), 匹配时屏蔽底部 32% + 右侧 22%。人机无勋章走全图匹配。 ### 发现五:禁用名单不靠模板 网格按主属性分块 + 客户端本地化名行优先排布;禁用/已选卡片对比度塌陷 (std 约 8–21 vs 正常 ≥33)。`heroes.json` 需含 `attr` / `name_loc`。 格数对不上时 `ok=False`,不返回残缺名单。 ### 发现六:位置与「我」 定位局位置字用二值 IoU(非 OCR)。「我」优先 GSI `team_slot`; 否则姓名亮度相对差(最亮比次亮 ≥ 25)兜底。 --- ## 4. 代码结构 ``` climperor/ ├── config.json / heroes.json ├── common.py / recognize.py / draft_session.py ├── gsi_watch.py / gsi_setup.py ├── fetch_cdn_templates.py ├── autocalibrate.py / calibrate.py / capture.py ├── evaluate.py / roles.py / grid.py / modes.py ├── templates/cdn|roles|modes/ └── samples/labels.json # 评测真值;raw/ 见 .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 | --- ## 5. GSI 自动化 ``` Dota 2 (-gamestateintegration) → POST → gsi_watch.py :3223 → DraftSession 轮询 recognize + roles/grid/modes → results/draft_*.json ``` 要点:按 `matchid` 去重;识别在工作线程;未标定时降级为只截图。 天梯 AP 选人按 2/2/1 成批揭晓,本轮结束前互不可见——因此必须在 **HERO_SELECTION** 跟踪,不能只在决策时间截一张终局图。 --- ## 6. 与 dota2-hex 独立验证项目。可选:Rust 重写并入,或本地旁路 HTTP 回传。 合规同 `dota2-hex`:**屏幕可见信息 only**。 --- ## 7. 参考 - [Valve #9562](https://github.com/ValveSoftware/Dota2-Gameplay/issues/9562) - [Valve #14915](https://github.com/ValveSoftware/Dota2-Gameplay/issues/14915)(开发者 OCR ≈85%) - [Overwolf Dota 2 GEP](https://dev.overwolf.com/ow-native/live-game-data-gep/supported-games/dota-2/) - Steam CDN:`https://cdn.cloudflare.steamstatic.com/apps/dota2/images/dota_react/heroes/{key}.png`