摘要
前几轮实验分别做了“等目标区域稳定再选帧”的选帧器、日志和提示框的“内容变化才读”、以及一个有界的读取队列,但验证方式是用历史记录里的读取耗时回放——并没有真的在视频播放过程中同时跑检测和 OCR(光学字符识别)。真实情况下,检测、排队和慢速深读会互相挤占时间,可能导致帧来不及处理或短暂内容过期。
本实验把它们组合成一个真实的时序流程:按 1 倍速把原生视频帧一张张“送到”,当场检测、排队、在后台真正做 OCR 深读。8 段真实录像窗口共 2,133 帧,实际检测 716 次、实际区域读取 48 次。
| 要点 | 结果 |
|---|---|
| 冻结评估:已标清楚展示的覆盖 | 9/10(另有一次提前约 0.47 秒读到,单独记录) |
| 人工精审的有限字段 | 面板 89/89、提示 9/9、书页数量 5/5、日志 29/29 与 28/28 |
| 读错 | 评估日志的回合标题“第 2 回合”被读成“一第2回合” |
| 帧到达 → 结果交付 p95 | 训练 1161.4 ms,评估 1209.4 ms |
流程只是视觉模块内部的显式实验路径,没有接入 App,也没有改公共接口。
要回答的问题
- 在真实时间压力下(检测与深读并行、只有一个读取 worker),“等稳定再读”还能及时抓住清楚的界面内容吗?
- 队列满了或内容过期时,会不会丢掉独有的信息?
- 从帧到达到结果交付需要多久?
数据与来源
- 8 段真实录像窗口:训练 3 个来源组、4 个窗口;参数冻结后,在另一个已暴露的来源组的 4 个窗口上评估。不是新的盲测;更早冻结的测试来源没有打开。
- 窗口沿用前两轮实验的定义;人工标签只在运行结束后用于评分,不进入运行时。
方法
把整套流程想成一条流水线:
- 帧到达:每个原生 2560×1440 帧按真实时间到达,时间严格递增。程序只能看到已到达的帧,绝不读取未来帧。
- 检测:通常每 100 毫秒检测一次界面;一旦出现提示框,短时间内改为每 25 毫秒请求一次。晚到超过 100 毫秒的帧单独记为“晚到跳过”。
- 选帧:英雄、生物、魔法书面板用“局部区域稳定”判断;日志和提示框用“局部内容有差异”判断。在一次读取被确认之前,如果出现更清楚的后续候选,就保留更新的那张。
- 有界队列:五类内容(英雄、生物、魔法书、日志、提示)各最多 2 个等待项,等待超过 1000 毫秒就过期;五类共用 1 个真正干活的读取 worker。所以最多是 10 个等待 + 1 个在读。溢出、过期、替换、去重都记录下来。
- 深读:只读调用方传入的当前检测面板,不回退去读别的英雄/生物/顶部状态条;提示框文字作为“区域原文”输出,不伪装成战斗日志。
worker 用训练来源的四种清楚页面预热,并在窗口之间复用;OCR 子进程常驻。没有使用缓存的框、缓存的 OCR 或任何标签。上一轮的一个“边缘反馈”选项还没有充分跨来源验证,本轮关闭。
设置
- 训练运行完成后才冻结决定(保存源文件快照),然后跑评估,不再用评估数据调参。
- 每次读取保存当时的原图和 JSON,记录捕获、检测、派发、开始读取、结束、交付及原图保存的时间。
- 另做一次回归:13 张旧的完整单图,除两个计时字段外,全部输出与默认路径一致(这是实现一致性检查,不是准确率)。
结果
负载与覆盖
| 范围 | 原生到达 | 实际检测 | 晚到跳过 | 节奏跳过 | 实际读取 | 已标清楚区间覆盖 |
|---|---|---|---|---|---|---|
| 训练:3 来源组、4 窗口 | 1,220 | 404 | 180 | 636 | 35 | 5/5(连续日志不在这 5 项分母内) |
| 冻结评估:另 1 组、4 窗口 | 913 | 312 | 35 | 566 | 13 | 9/10 |
评估中的一个姿态技能提示框,在原标注“清楚区间”开始前约 0.47 秒就已经读到了完整名字和效果;进入区间后因为内容已读而被去重。按原规则这一次仍记为未覆盖(9/10),没有悄悄改成 10/10,提前读取另行记录,原图已查看。这是有限展示的覆盖,不是整场召回率;那 180 / 35 个晚到帧也没有逐帧标注其中有没有有用信息。
队列溢出的核对
训练中发生了 1 次队列溢出:一个日志候选被丢弃。从同一窗口起点重新解码核对后,28 条完整可见的日志事件都能在它前后两个被保留的原图里看到(顶部和底部各有一行被裁断,单独列出)。这个案例没有发现独有事件丢失,但不能据此推断所有溢出都无损。评估运行没有溢出或过期;两组结束时所有等待项、在读项和上下文都已释放。队列中原图的峰值内存为 33,177,600 字节(训练)和 22,118,400 字节(评估),不含模型和解码器。
人工精审的有限字段
| 字段范围 | 正确 | 错误/漏读 | 限制 |
|---|---|---|---|
| 7 张英雄/生物面板:84 个数值 + 5 个生物名 | 89/89 | 0 | 不含英雄身份、技能/装备身份和面板全部文字 |
| 2 个提示框的 9 个声明字面段 | 9/9 | 0 | 不代表全部 8 次提示读取都完整 |
| 5 张书页的图标占用数(11/11/6/11/11) | 5/5 | 0 | 只是数量,不是逐槽身份或定位 |
| 训练日志第二次读取:29 条事件(时间、玩家名、正文) | 29/29 | 0 | 其余连续日志没有整页精审 |
| 评估日志:28 条事件 | 28/28 | 0 | 来源独立于训练,但已暴露 |
| 评估日志标题“第 2 回合” | 0/1 | 1 处错读且未解析 | OCR 读成“一第2回合”,反馈机制没发现 |
小范围内的 100% 不能外推为“全界面 ≥ 95%”。上一轮固定的困难分母、未实现和未知字段仍保留;一次极短的法术展示上 7 个没读到的字段,本轮也没有补回。
时延
| 热启动 worker,从原生帧到达开始计时 | 训练 35 次读取 | 评估 13 次读取 |
|---|---|---|
| 到达 → 结果交付 p95 | 1161.4 ms | 1209.4 ms |
| 同口径最大值 | 1358 ms | 1264 ms |
| 到达 → 原图 PNG 保存完成 p95 | 1245.0 ms | 1293.2 ms |
包含当前检测、真实排队、深读和结果序列化;不含解码(逐帧另记)、模型冷启动、App 截图快捷键、进程间通信、状态累积和界面显示。因此不能宣称 App 端到端 p95 ≤ 3 秒已达成。
失败与负结果
- 第一次训练运行只在主线程预热、且每个窗口新建 worker,英雄面板深读花了 2.616 秒,导致一个短提示过期。改成在工作线程预热并复用后,该提示被保留;旧失败记录保留。
- 第二次训练运行因为预热用的场景名写错(少写了一段),在输入前就失败了;修正后另起新运行。没有隐藏这些尝试,也没有用评估数据调参。
- 仍存在的问题:主线程同步保存 PNG 造成晚到;英雄面板上的提示框覆盖引起额外的英雄重读;姿态提示同内容额外读取;1 秒期限可能丢掉短内容;反馈机制不能判断字段是否全部读完;回合标题错读未被发现。
- 只有一个读取 worker、每类最多 2 个等待,不提供无限吞吐,也不保证抓住所有短提示。
局限
- 评估来源已暴露,不是独立盲测;精审字段范围有限。
- 读取结果仍是单帧观察候选;反馈只用于决定是否允许同内容去重,不能认证字段完整、敌我归属或隐藏状态。
- 自动的英雄/生物/魔法书/日志/提示组合,还不等于覆盖了准备、布阵、开场、顶部状态条、攻击和法术预览的全部内容。
- 软件测试 266 项通过,是代码正确性检查,不是识别准确率。
结论与下一步
“等稳定再读”第一次在真实时间压力下接上了真正的检测和 OCR,能及时抓到大多数已标清楚内容,单次读取约 1.2 秒交付。覆盖仍需扩展。下一步:减少同步输出造成的错过和覆盖层引起的重复读取,按“独特可读内容”检查被丢弃的帧;然后把稳定选中原图上的技能、装备、英雄身份和尚未覆盖的预览字段纳入评分分母。不需要精读每一帧动画;冻结的测试来源不会因为小范围高分而提前打开。项目的十场独立对战、逐场景逐字段 95% 和 App 3 秒目标都尚未达成。
Comments