Files
climperor/README.md
T
vosonandCursor f32d24b8f8 Initial commit: 上分帝(Climperor)
从 dota2-draft-vision 迁出并定名,作为天梯选将识别项目起点。

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-26 11:47:39 +08:00

229 lines
9.9 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 上分帝(Climperor
Dota 2 天梯选将识别:从「决策时间」截图中识别双方 10 个英雄(模板匹配,本地、离线、秒级)。
用于验证可行性,成熟后可移植到 [dota2-hex](../dota2-hex) 或独立发布。
方案背景、技术选型与推进计划见 [DESIGN.md](DESIGN.md);本文是操作手册。
## 原理
```
决策时间截图(PNG
→ 按相对坐标裁出 10 个头像格(分辨率无关)
→ 每格纠裁、去 UI 边饰、缩放到统一尺寸
→ 与模板库做归一化相关匹配(TM_CCOEFF_NORMED
→ Top-1 分数 + Top1-Top2 分差 双阈值门控 → hero_key 或 null
```
模板库分两层:
- `templates/real/{hero}/*.png` —— 真实截图裁出的**默认脸**格子(高精度;不收皮肤变体)
- `templates/cdn/{hero}.png` —— Steam CDN 官方头像(全覆盖兜底,匹配时降权)
## 安装
```powershell
pip install -r requirements.txt
```
## 使用流程
### 1. 下载 CDN 兜底模板(一次性)
```powershell
python fetch_cdn_templates.py
```
生成 `heroes.json`(英雄 id/key 对照)和 `templates/cdn/`(全英雄头像)。
### 2. 采集截图
程序可自行截屏,无需手动按 PrintScreen
```powershell
python capture.py # 单张,存入 samples/raw/
python capture.py --loop 300 3 # 每 3 秒一张,持续 300 秒
```
GSI 自动跟踪时,截图按对局写入 `samples/raw/<matchid>/``draft_*.png``draft_best_*.png` 等)。
手动 `capture.py` 仍落在 `samples/raw/` 根目录。
Dota 2 需运行在**无边框窗口**或窗口模式(独占全屏可能截出黑帧)。
也可以交给 GSI 自动触发,见下方「自动运行」。
### 3. 标定 ROI(一次性)
拿一张**原始分辨率、未裁剪**的决策时间全屏截图,自动标定:
```powershell
python autocalibrate.py samples/raw/draft_141704.png
```
靠顶栏那 10 条固定玩家颜色条定位,无需手动框选,任何分辨率都适用。
输出 `preview/autocalibrate_check.png`(整条顶栏带框)和
`preview/autocalibrate_slots.png`(10 格裁切拼图)供核对,坐标以相对值存入 `config.json`
只核对不写配置:加 `--check`
手动标定仍可用(自动失败时的退路):
```powershell
python calibrate.py samples/full.png
```
在弹出窗口中依次框选 10 个英雄头像格(每框完一个按空格,全部完成按 ESC)。
### 4. 建真实模板库(可多次,逐步积累)
先预览裁切是否正确:
```powershell
python build_library.py samples/shot1.png
# 查看 preview/slot_1.png ... slot_10.png
```
确认无误后带标注入库(10 个 hero_key 从左到右,跳过用 `?`):
```powershell
python build_library.py samples/shot1.png tinker,earthshaker,juggernaut,dazzle,vengefulspirit,axe,sniper,slark,lion,drow_ranger
```
hero_key 见 `heroes.json`(即 Steam 内部名去掉 `npc_dota_hero_` 前缀)。
### 5. 识别 + 验证准确率
```powershell
python recognize.py samples/shot2.png
python recognize.py samples/shot2.png --sheet # 输出带预测标签的对照图
python recognize.py samples/shot2.png --truth tinker,earthshaker,... # 对答案
```
`--sheet` 生成 `preview/recognize_sheet.png`:10 格裁切并排,每格标注预测英雄与
分数/分差,绿色表示过阈值、橙色表示存疑(前缀 `?`)。核对时比读 JSON 快得多。
批量评测所有已标注截图(真值写在 `samples/labels.json`):
```powershell
python evaluate.py # 完整模板库
python evaluate.py --cdn-only # 只用 CDN 层,衡量开箱即用的表现
```
`--truth` 时输出每格对错与总准确率;认错的格子自动存入 `failures/`
(文件名含正确 key)。仅当裁切是**选人阶段默认脸**时再挪进 `templates/real/{key}/`
皮肤/至宝头像不要入库(靠会话多帧 + 决策阶段禁改判处理)。
## 自动运行(GSI 触发)
装好后全程零操作:进入决策时间自动截图、识别、输出 JSON。
### 一次性配置
```powershell
python gsi_setup.py # 自动找到 Dota 2 并写入 GSI 配置
```
然后在 **Steam 库 → Dota 2 → 属性 → 启动项**中加上 `-gamestateintegration`,重启游戏。
其他用法:`--check` 只查看状态,`--remove` 卸载配置,
`--path "D:\Steam\steamapps\common\dota 2 beta"` 手动指定目录。
### 开着它打游戏
```powershell
python gsi_watch.py
```
监听 `127.0.0.1:3223`,游戏一进入英雄选择就开始跟踪整局选将,每秒轮询一次,
每有新英雄揭晓就打印一行,结束后把完整时间线存入 `results/draft_<时间戳>.json`
每局只跟踪一次;中途启动程序会从决策时间兜底接入。
天梯全英雄选择是**分 3 轮成批揭晓**的(每队 2 / 2 / 1,本轮结束前互相不可见),
所以输出长这样:
```
[draft] grid: 16 heroes unavailable (contrast margin 11.0) - 术士, 殁境神蚀者, ...
[draft] + 4.2s round1 radiant slot2 冥魂大帝 [优势路]
[draft] + 4.2s round1 dire slot7 幻影刺客
[draft] + 31.5s round2 radiant slot3 狙击手 [中路]
[draft] ~ 33.0s slot2 幽鬼 -> 主宰 (score 0.52 -> 0.86)
...
you : slot 5 radiant 沉默术士 - position 5 (纯辅助)
lanes : 1:冥魂大帝, 2:狙击手, 3:军团指挥官, 4:祈求者, 5:沉默术士
bans : 16 - 术士, 殁境神蚀者, 拉比克, 冥界亚龙, 沉默术士, ...
```
`~` 开头的是**改判**。刚揭晓的那一帧最不适合下判断——立绘还在淡入、天梯段位条又盖住下半张脸。
已确认的槽位仍可被更高分的稳定读数覆盖(`revise_gain`,默认 0.15)。
顶栏在**所有人选完之前**用默认头像,皮肤要等全员锁定后才上。因此视觉会读完选人阶段,
并在决策阶段继续读一段时间(`strategy_tail_polls`),直到凑齐 10 人或超时——避免你已进
决策界面、别人还没选完时漏掉最后一人。凑齐后才停视觉,再等 GSI 公布你自己的英雄。
选人界面顶部计时器下方的模式字(如「全英雄选择」)也会识别,写入结果的 `mode` 字段。
定位匹配局里,我方 5 格头像下方的位置文字会被一并识别。「我」是哪一格直接取自
GSI 的 `player.team_slot`;GSI 还没开始推送时,靠姓名颜色兜底(自己的名字是亮白,
其余九人偏蓝)。非定位局没有位置文字,就只报槽位不报位置。
天梯 AP 固定禁用的 16 个英雄从英雄选择网格里读出,不依赖任何图像匹配:网格按
主属性分四块、块内按客户端英雄名排序行优先填充,位置可直接推算;被禁或已被选走的
卡片整张压暗,灰度对比度会塌到 21 以下(正常卡 33 以上),据此判定。
不想开游戏调试时:`python gsi_watch.py --once`(立即截一次并识别)。
想改触发时机:`python gsi_watch.py --states HERO_SELECTION,STRATEGY_TIME`
只看某张截图的位置识别:`python roles.py samples/raw/<matchid>/<帧>.png`
只看某张截图的禁用识别:`python grid.py samples/raw/<matchid>/<帧>.png`
> **主菜单里收不到数据是正常的。** Dota 的 GSI 与 CS 不同,客户端**第一次载入对局后**
> 才开始发 HTTP 请求,挂在主菜单时不会有任何推送。看到 `[gsi] connected` 才算链路通。
> 一直没有的话,先确认启动项里有 `-gamestateintegration`。
人机对战(Play with bots)同样会推送,HUD 顶栏与天梯一致,适合反复测试。
**尚未标定时**会自动进入「只截图」模式——按 `matchid` 存到 `samples/raw/<matchid>/`
拿其中一张跑 `calibrate.py` 即可完成标定。这是当前推荐的第一步。
相关参数在 `config.json``gsi` 段:
| 字段 | 含义 | 默认 |
|------|------|------|
| `port` | 监听端口 | 3223 |
| `trigger_states` | 启动跟踪的游戏状态 | `HERO_SELECTION` + `STRATEGY_TIME` |
| `poll_interval` | 选将期间的轮询间隔(秒) | 1.0 |
| `confirm_polls` | 连续几帧认出同一英雄才算数 | 2 |
| `session_timeout` | 单局跟踪上限(秒) | 300 |
| `keep_event_frames` | 是否为每次揭晓存一张原图 | true |
| `dump_selection_every` | 选将网格开着时每几秒存一帧(0 关闭) | 4 |
| `target_slots` | 认满几格就提前停 | 10 |
## 截图要求
- **PNG 格式、原始分辨率**(不要经聊天工具/微信转发,会被压缩)
- 画面为对局内「决策时间」阶段,顶栏 10 个英雄完整可见
- 无边框窗口或窗口模式截图均可;直接把文件放入 `samples/`
## 判定标准(demo 验收)
| 指标 | 目标 | 实测(6 局 / 60 格,含 1 局天梯) |
|------|------|-----------------------------------|
| real 模板命中的格子准确率 | ≥ 95% | 100%(分数恒为 1.000 |
| 仅 CDN 兜底的格子准确率 | ≥ 70% | 人机 100%;天梯 9/10(皮肤差异) |
| 单张图识别耗时 | < 1 秒 | 270~520ms |
天梯顶栏会叠段位条和勋章:程序检测到后自动屏蔽底部/右侧遮挡区再匹配,
人机局不受影响。带皮肤的英雄仍需一张 real 模板。
## 已知限制 / 后续方向
- 斜切头像目前按矩形内缩裁切,未做仿射纠正(够用则不加)
- 分辨率靠相对坐标适配;宽高比差异大(21:9)时需重标定或做锚点自动定位(玩家颜色条 + 中央倒计时)
- 英雄皮肤(至宝/身心)可能改变头像,需为常见皮肤补充 real 模板;
计划加入「识别有误时手动校正」的入口,校正结果直接沉淀为 real 模板
- 禁用识别依赖网格默认排序(子类=属性)。若在客户端里改了排序或筛选方式,
格数会对不上英雄表,此时 `grid.py` 会直接报 `ok=False` 而不是给错名单
- 新英雄上线后需重跑 `python fetch_cdn_templates.py` 刷新 `heroes.json`
- 位置文字模板取自简体中文客户端,换语言需重新执行
`python roles.py <帧>.png --build off,safe,mid,soft_support,hard_support`