摘要

战场上每个兵堆脚下有一块“数量牌”:一条彩色边框(绿色或暖色)加上白色数字。读取流程分几步:先在整张图上找边框形状的连通区域(候选“轨”),按颜色和形状过滤,再在牌内找浅色的字形,最后裁图交给 OCR。上一轮(A27)在三张原图上暴露了四个失败:漏掉 50 和 26、把一个 215 读出两次、把 150 读成 4150。

本实验(A28)不改任何代码、不跑 OCR,只用 Python 的执行追踪功能,观察原有生产函数在这三张图上实际走过的每个分支,确定每个失败最早发生在哪一步。结论:三个失败的第一失误点都在几何阶段,各不相同;第四个(4150)裁图本身正确,OCR 为何多读一个 4 仍未证明。

要回答的问题

四个已知失败,各自在流水线的哪一步第一次出错?是边框没找到、字形被过滤、候选重复,还是 OCR 本身?

数据与来源

上一轮的三张原生截图(两张来自 10 月 1 日的录像、一张熔岩地图开局),每张在隔离的 CPU 进程里只解码一次。参考区域是之前审核过的、带边距的字面框,不是精确的字形遮罩。

方法

  1. 原样调用生产函数,用执行追踪记录每次轮廓循环、跳过分支、水平/暖色分组、外观与字形判断、剪枝和输出,不修改函数源码或返回值。
  2. 原函数会调用边框候选子程序两次(一次用于空身体关联、一次用于独立提案),两次追踪都保留并要求相等。
  3. 先要求重新生成的整份几何结果与上一轮保存的结果完全相等,才去看参考区域附近的像素。
  4. 像素诊断用 1 像素 = 1 字符的文字阈值图,不生成新图片、不做 OCR。

协议、代码和来源清单在解码原图前冻结;只运行一次,不重跑。

设置

三次解码各约 0.038 秒;被追踪的完整调用 0.057–0.094 秒;整个审计主体 0.358 秒。这是带插桩的诊断,不是性能基准。OCR、GPU、模型调用均为 0。

结果

三张图上的完整调用结果与上一轮保存的几何结果逐项完全相等。

每个失败的第一失误点:

失败 实际上游证据 第一次丢失/重复
红方 50 两段断开的绿色边框碎片(12×6 和 18×6) 第一段宽度不够 17;第二段宽高够但宽高比 3.0 低于 3.8。两段都没进入字形检查
蓝方 26 绿色边框通过,牌内蓝色占比 0.567 颜色通过;但两个浅色字形高度只有 3 和 6,低于最小值 7,被“没有清晰字形”规则移除
红方 215 两段绿色边框各自通过,成为两个已保存的候选 原始绿色候选之间从不互相分组;两个候选接受了同一个浅色字形,于是重复
蓝方 150 → 4150 裁图完整包含参考区域(100%) 提案和裁图都成功;识别器为什么多读一个 4 仍未证明

几个细节:

  • 50 的两段碎片之间隔了 22 像素,原有的形态学闭运算核无法把它们连成一段;水平救援规则要求宽度 ≥40,也接不上。数字丢失发生在边框生成阶段,与后面的位置或 OCR 策略无关。
  • 26 的浅色像素里混有较暗的低饱和像素(参考区域中位亮度 111,阈值 145),但这描述的是带边距的混合区域,不能证明只改亮度阈值就够。
  • 215 的两个牌框 IoU 为 0.365;已有的 IoU>0.5 分组只在添加救援/暖色候选时使用,不在原始绿色候选之间使用。画面上只有一块牌,不是第二个兵堆。

整图统计(每张图第二次子程序调用):

图 绿色闭运算组件 绿色形状通过 暖色通过/加入 v3 前 颜色/字形拒绝 输出
10-01 第一张 385 15 4 / 3 18 1 / 3 14
熔岩开局 287 16 23 / 23 39 18 / 8 13
10-01 第二张 219 14 7 / 7 21 2 / 5 14

失败与负结果

  • 观察器自身的 bug:最初的追踪器记录的是水平救援子程序返回列表的引用,而调用者随后往同一个列表里追加了暖色候选,导致保存的“救援返回值”实际显示的是后来的列表。实际组件、分组和最终几何比较不受影响。根据已记录的候选和未改的源码逻辑,可以证明救援实际新增为 0;更正单独记录,原始审计文件不改。观察器已改为深拷贝,并新增回归测试。没有重新解码原图。
  • 4150 的原因没有找到;没有做模型反事实或裁剪实验。

局限

只有三张已曝光的图,不构成盲测;只跑了 3 个聚焦测试,没跑完整模块套件。阈值图是文字诊断,不识别游戏机制。

结论与下一步

四个失败在流水线里分属三个不同的几何阶段,不能用一个“调大阈值”的改动同时解决。本实验冻结了一个尚未实现、尚未评估的候选:对颜色相同、边框上下沿完全相等、且接受的字形框列表完全相同的提案去重,只保留一个。它只针对 215 的重复问题,不处理 50 和 26,也不解决 4150;它可能会把真实存在、但恰好共享全部字形的重叠提案也合并掉,因此需要另行授权,在已有数据上做有界比较。