摘要

在《英雄无敌:上古纪元》的战斗里,玩家可以打开一个生物的属性面板,看到攻击、防御、伤害、士气、幸运、先攻、速度等数值。这个面板是半透明的:后面战场上的东西会透过来,其中就包括其他兵堆脚下显示数量的小牌子(下称数量牌)上的数字。

OCR(光学字符识别,把图片里的字读成文字)在这种画面上会把背景里的小数字和前景的大数字拼在一起。前一轮发现了三处真实误读:

正确值 被读成
攻击 32 432
攻击 17 9517
幸运 1 71

本实验利用一个几何事实:前景属性数字字形更大、共用一条较低的底边,背景数量牌数字更小、位置更高。据此重新给每个属性数字取一个更紧的文字框,再用原来的读字流程复核。最终方案只复核已经读出的候选,三处错误全部纠正,原本正确和原本未知的字段都不受影响。另一个更激进的方案——用新框直接替换全部读数——虽然账面正确数更高,但引入了新的错误,被否决。

要回答的问题

  1. 能否在不降低 OCR 阈值、不限制数值范围、不查兵种目录的前提下,排除背景数量牌数字的干扰?
  2. 新方法会不会把原本正确的值改坏,或者把看不全的数字读成完整数字?

为什么不能用那些“捷径”:降低阈值会放进更多垃圾;“攻击不该超过某个值”是对游戏数值的猜测;查目录或借邻近帧会把推断冒充成观察。

数据与来源

  • 训练来源:上一轮已精标的 87 张训练图(原图及其邻近帧),覆盖所有自动请求的攻击/防御/伤害/士气/幸运/先攻/速度格,而不只是那三处错误。
  • 验证:新跑 210 张训练/校准完整原图、45 张邻域原图,最后对 71 张最新开发图跑一次。最新组是 10 月 5 日一个已暴露来源组里的 5 个对局,不是独立盲测。
  • 保留低质量和“该位置不存在”的对照样本;更早冻结的测试来源不读取。
  • 只验证了 2560×1440。

方法

几何提案:在登记好的属性行里找连通的字形块。前景大字形共享较低的底边,大多数背景数字更小、更高。保留负号和完整的多位数字;几何上含糊或贴着边界的一律拒绝提案。提案只返回框和诊断像素,不返回数值。

读字:把新框交给现有的读字器,原图和放大 3 倍两种尺度各读一遍,要求两遍对整个数字串严格一致。绝不从 OCR 结果里挑一个子串,也不推测数值范围。

两种使用方式(先在训练集上比较,再决定):

  1. 全替换:凡有新提案的格子,都用新框的读数替换原读数。
  2. 只复核已接受的候选:只对原本已经读出值的格子做一次复核;新框两种尺度都强一致且完整时可以改值;弱读、冲突、符号变化则保留原值并记录诊断。原本未知或被局部拒收的格子一律不动。

设置

  • 实验入口是显式开关 --stat-foreground,不加就是上一版行为;不改模型权重、默认配置、App 或下游模块。
  • 训练组上决定方案后才动用校准组和最新组;最新组只跑一次,不在最新组上调规则。

结果

训练组上的方案比较

455 个已请求的属性格中,340 个得到几何提案,115 个拒绝提案。

方案 正确 已标误读 新问题 决定
原读数 344 3 — 基线
全替换 353 1 3 个被鼠标遮住的残缺数字被读成完整的“1”;与死亡横幅重叠的 41 被读成 4 否决
只复核已有候选 纠正 3 处,原正确值无损失 0(这三处) 没有把不可判的数字提升为值 采用

全部 13 个变化的原图上下文、原裁图和新裁图都看过像素(模型目视核对,不是独立人工审核)。关键教训:新框“看起来干净”并不证明里面的数字完整、没被遮挡,所以不能用它去恢复原本未知的字段。

完整原图验证

326 张图中唯一的值变化就是那 3 处纠正,全部在上一轮新增的训练精标范围里。因此更早一轮较窄分母上的成绩完全没变:

原声明范围 正确/分母 误读 拒判 漏读 声明字段整帧全对
训练 927/934 0 5 2 70/77
校准 280/280 0 0 0 24/24
最新 356/363 0 7 0 22/27

在上一轮建立的“完整局部 11 格”范围上:

范围 正确 误读 拒判 漏读 不可判、保持未知 不存在的位置没有误请求
42 张训练图、428 个可读格 384→387 3→0 21 20 34 0
45 张邻域图、266 个可读格 188 0 24 54 75 154

完整局部正确率分别是 90.42% 和 70.68%,仍未达到 95%。42 张图里那 3 个被接受的背景错误已清除,但不等于整个系统没有错误。

回归与速度

326 张图的 3,907 个原始数值观察都保留;3,347 个非目标或未知字段逐项一致;528 次前景复核只产生 3 处值变化;界面、身份、空槽、场景和日志输出深度比较一致;读取失败 0 次。

原图范围 热启动 p95(秒) 最大(秒)
训练 165 张 1.320 1.583
校准 45 张 1.362 1.445
最新 71 张 0.999 1.126

计时从解码文件到写出 JSON,包含新增复核,不含 App 截图和显示;依赖模型常驻预热(最新组冷启动界面 2.753 秒、OCR 5.103 秒)。与上一版的耗时差异属于波动,不称为优化。

失败与负结果

  • 全替换方案失败:账面上正确数从 344 升到 353,但新增了把残缺数字读完整、把 41 读成 4 的错误。只展示三处背景错误被修好而隐藏这些新错误,是不诚实的。
  • 最新组里上一轮新增的一个拒判(正确的数量 7/15 被拒)仍然保留。英雄士气 9/11、幸运 8/11、生物数量 14/16,都不到逐字段 95%。
  • 全模块测试 7226 通过、2 个与之前相同的导入顺序失败、6 跳过,没有为了全绿删除或反复重跑。

局限

  • 同一个 OCR 模型的多视图只算一个证据来源,不提供校准概率。
  • 最新组是 5 个对局、1 个已暴露来源组,不能据此完成“十个独立来源”验收。
  • 只在 2560×1440 上验证。

结论与下一步

“只复核、不扩张”的前景字形复核安全地消除了这一类背景数字拼接错误。但现在更大的损失来自覆盖率而不是这类错误:从保存的结果里已定位 8 张训练图、74 个可读字段漏读——其中 7 张根本没有面板提案,1 张面板得分 0.951 但下边界偏离固定模板,整个面板没被请求。下一轮优先复用已有的面板定位和文字锚点补上这个入口,保留局部可读的过渡帧和无面板负例,在训练来源上证明定位和字段完整性后再复测。