摘要
项目的战斗记录 App(Battle Notes)最初要靠玩家在对局中按截图键,把想分析的画面交给视觉理解模块(Perception)读取,再由状态估计(SE,把读到的观察累积起来推断对局状态和对手构筑)给出对方信息。用户的目标是:战斗中不按截图键,程序自动挑出值得读的画面(关键帧)去分析;选秀阶段的画面低延迟丢弃;离线回放用虚拟时间;生产精度测试和有界队列测试分开。
本实验交付了三件事:
- 流式关键帧选择 + 阶段门:边播边选关键帧,选秀 / 菜单阶段的关键帧直接跳过。
- 行动条兵种识别提速:在“生产读数完全不变”的前提下,4 个测试局 51 张截图的识别总耗时从约 265 秒降到 103 秒。
- 手动与自动的对比:两局测试中,各种自动方式认出敌方英雄都比手动截图早约 1.5–3 分钟,而且自动模式在一局里多找到了 2 次真实的敌方施法;代价是有界队列会丢帧。
要回答的问题
不依赖截图键、完全自动地选帧和读取,能否得到不少于手动截图的对手信息?读取速度跟不上画面时会发生什么?
数据与来源
- 测试录像:10 月 5 日两局(21:20 和 22:02 开始的录像),属于测试用途;另一段 10 分钟录像用于阶段门统计。
- 真值:只来自这两局的“截图包”(玩家当时手动截的 15 张和 10 张图)及其标注。截图包没覆盖的画面,没有真值。
- 用户第一次实战(10 月 9 日)的自动读取记录,用于检查重复关键帧。
方法
流式关键帧选择。 把原来的批处理关键帧选择器改成流式版本:逐帧输入、内存有界,单元测试确认与批处理逐帧结果相等。
阶段门。 选秀和菜单阶段的关键帧直接跳过,开战前的最终确认页保留,标签不可靠的帧照常分析。
选帧服务。 一个常驻服务接收 640×360 的 JPEG 帧,输出关键帧和每帧进度;App 端有背压控制:服务落后 8 帧就暂停送帧。
行动条兵种识别提速。 战斗画面上有一条“行动条”,按行动顺序列出各个兵堆的小卡片。原来每张卡片都要和全部兵种模板做精确匹配(每张约 0.2 秒)。尝试了几种提速,启用条件都是“4 个测试局 51 张截图的战场读数逐字段不变”:
- 近乎相同的卡片复用上次结果(相似度 NCC ≥0.985,只在常驻服务里);
- 由粗到细:先用便宜的粗匹配排出前 K 名,再只对这 K 名做精确匹配。K 根据数据定:在 289 张卡片、两种模板上,全量匹配的第 1/2/3 名在粗排中最差排在第 1/14/17 名,所以保留 24 名可以全覆盖。最后把粗排改写成矩阵运算,粗排每卡 4.5 毫秒(原约 90 毫秒),名次分布与原粗排逐项相同。
重复关键帧判定(实战后补充)。 160×90 灰度缩略图分成 16×9 格,与同场景上一张已读帧比较:变化超过 8 个灰度级的格子少于 10% 就跳过。
设置
对比实验在 CPU 限 8 核的条件下运行。两局的“全部关键帧”分别有 466 和 553 张,超过 SE 单次 256 帧的上限,所以等间隔去掉多余的战场 / 布阵帧,其余场景全部保留。行动条提速是在机器满载时测的,绝对时间偏高。
结果
阶段门(10 分钟录像):149 个选秀 / 菜单关键帧被丢弃,162 个送去分析;决定延迟中位数 0.2 秒。
真实 App 视频模式(一段 5 分钟测试录像,3,001 帧):
| 版本 | 用时 | 自动识别次数 | 结果 |
|---|---|---|---|
| 全量行动条匹配 | 278 秒 | 13 | 敌方英雄蕾亚正确,取到我方基准;所有学派法术费用 −1,并据此给出来源推断(报告记为“消耗 / 召魂戒指”) |
| 发布候选(由粗到细 + 复用) | 173 秒 | 13 | 同上;退出无报错 |
此前一个发布候选在退出时有顺序问题(Qt 已删除的进程对象仍触发错误回调),已修复。
行动条提速(51 张截图):
| 方法 | 读数一致 | 总耗时(全量 → 新) |
|---|---|---|
| 近似相同卡片复用 | 51/51 | 253 → 206 秒 |
| 由粗到细,保留 12 | 51/51 | 282 → 103 秒(第 2/3 名个别不同,未启用) |
| 由粗到细,保留 24 | 51/51 | 271 → 129 秒 |
| 由粗到细,矩阵粗排,保留 24(采用) | 51/51 | 265 → 103 秒 |
手动与自动对比:
| 局 | 方式 | 读取张数 | 识别用时 | 敌方英雄(最早认出) | 敌方法术 | 我方基准 |
|---|---|---|---|---|---|---|
| 10-05 21:20 | 手动截图 | 15 | 98 秒 | 对(514 秒) | 无(真值也无) | 有 |
| 10-05 21:20 | 全部关键帧 | 466 | 3,169 秒 | 对(419 秒) | 无 | 有 |
| 10-05 21:20 | 有界队列(App 实时) | 159 | 922 秒 | 对(424 秒) | 无 | 有 |
| 10-05 21:20 | 自动 + 手动 | 167 | 1,012 秒 | 对(418 秒) | 无 | 有 |
| 10-05 22:02 | 手动截图 | 10 | 26 秒 | 对(606 秒) | 无(真值也无) | 有 |
| 10-05 22:02 | 全部关键帧 | 553 | 3,499 秒 | 对(417 秒) | 召唤原始残骸、暗影军队 | 有 |
| 10-05 22:02 | 有界队列(App 实时) | 141 | 642 秒 | 对(417 秒) | 召唤原始残骸、暗影军队 | 有 |
| 10-05 22:02 | 自动 + 手动 | 142 | 642 秒 | 对(442 秒) | 无 | 有 |
第二局“多出来”的两个法术是真的:在录像第 1,178 秒的日志帧上按像素核对过两行,敌方英雄咕噜先后施放了“召唤原始残骸”和“暗影军队”。截图包的 10 张截图没覆盖这段日志,所以截图包真值里没有。也就是说,自动模式比手动截图多找到了 2 次对方施法。
重复关键帧:用户第一次实战那局自动读取了 23 次,其中有同一生物面板的重复读取。新规则下,同一面板的变化格占 5–6%,不同面板、日志、战场至少 17%;跳过了第 6、14、15、23 张,其余保留。会话里的原始截图不删除。
失败与负结果
- 有界队列会丢帧。 两局分别丢了 307 和 412 张关键帧,这是读取速度慢于画面的代价。
- “自动 + 手动”反而丢了那张关键日志帧:截图键帧占了队列名额,把这张关键帧挤掉了。
- 第一次对比运行(全量匹配、机器过载)在第二局读到 164/563 帧时为释放负载而停止,缓存的读取结果保留。
- 由粗到细只保留 12 名时,第 2/3 名个别结果不同,未启用。
- 重复帧判定第一版用“每格平均差 <3 灰度级才算重复”,结果一张都判不出来:战场和头像有待机动画,最大格差在 12–80 之间。
局限
- 只有两局测试录像;真值只来自截图包,“法术”列不能直接当作对真值的漏报或误报。
- 全部关键帧方式的识别用时(约 3,000 秒以上)远不能实时。
- 行动条提速是在机器满载时测的;不是延迟验收。
结论与下一步
不按截图键的自动分析在这两局里认敌方英雄更早,也能发现手动截图漏掉的施法。主要问题是读取跟不上画面导致有界队列丢帧,而且手动截图会和自动关键帧争抢队列名额。后续工作集中在实时模式下怎样把更多帧送进选帧服务、以及缩短每次读取的耗时。
Comments