🔍搜索⌘K

对比图上的框歪了一半,检测其实一次都没错

问题

我在做一个管道转动的检测,用 Python 加 OpenCV 那套。每次判定完会顺手出一张对比图:把开头和结尾两帧并排摆一起,在上面画出检测框,方便人眼复核到底框到了什么。

有天我自己翻出这张图看,越看越不对:黄框压在远处岸上那片结构上,没落在水面的管子上。

现象

图确实就是这样,黄框明明白白在岸上。可同一批数据跑出来的判定是「在转」,置信度 0.95,十六帧全检出,测出来的角速度还跟设备真实转速对得上。

这就别扭了:框错了地方,判定怎么可能对?

我第一反应是模型框错了目标,一上午顺着这个方向查下去,越查越像那么回事:

  • 先查几何门控,发现确实有个脚本忘了传门控配置,用的是内置兜底值。修完再跑,这个框还是拦不住。
  • 放大看框里的内容,是远岸水边一条深色横带加蓝管。于是怀疑模型把固定管线当成旋转臂了,这条路要是坐实,就得重新标注、重新训练。
  • 还做了只读的位移分析:背景两块参照物模板匹配都是 0 像素,说明相机没动;框内左段匹配 0 像素、中段 28 像素、右段 55 像素,位移随着离左端越来越远而递增——这是绕着一个支点转动的样子,平移不会这样,静止更不会。
  • 速率 0.122°/s,正好等于转一圈 49 分钟。静止的植被噪声不可能这么巧。

到中午我的结论已经变成「框其实是对的,框的是旋转臂远半段,只是缩略图上远端臂跟岸边植被长太像了」。为此还专门整理了一套判「框是真动还是假动」的办法。

怎么解决

下午我不死心,把模型单独拎出来,在原始图上重新画了一遍框。这才发现模型画的蓝框和对比图上的黄框压根不在一块儿——真管在水面,黄框却在岸上。

这才回头去查画框的代码。真正的原因在一行缩放上:

cv2.resize(fr, (960, 540), dst=fr)

我当时的理解是:传了 dst=fr,就是把 fr 这张图就地缩到 960 宽。但 OpenCV 的 resize 是返回一张新图的,不会去改传进去的那个数组。缩放结果只在返回值里,而我没接返回值,等于白调了一次。

于是两件事一起错:

  1. 图还是原来的全分辨率,两张拼起来 3848×1080,根本没缩;
  2. 可我画框用的是 int(v * 0.5),按「已经缩了一半」算出来的坐标往图上画。

框自然就落在真实位置的一半处,整体偏到左上角,看起来正好压在岸上建筑那儿。检测从头到尾没框错,错的只是那张给人看的图。

改的时候又踩了两个连环坑:

  • 我把那行改成 fr = cv2.resize(...),但它写在 for 循环里,只是把循环变量重新绑了一下,真正存起来待拼接的两张图还指着原来的大数组,拼出来照样 3848 宽;
  • 要是保留「先画框、后缩放」的顺序,框又会跟着图再缩一半,还是不对。

最后把顺序整个重排成「先缩放、再画框」,并且把缩放后的新数组放进列表再去拼。

验证也没敢再用眼睛看。直接把新图里的黄色像素抠出来读坐标,跟真实检出框乘以 0.5 逐个对上,才算确认画对了。判定输出没变,还是「在转」。

结论

有些函数是返回新东西,有些是改传进去的那个对象,别凭直觉当成后者用。这类调用最坑的地方是不报错、也不生效,程序照跑、图照出,只是内容全不对——我根本不会怀疑到它头上。

还有一点我记了挺久:看到的现象跟程序输出对不上时,先确认我看的那个东西本身是不是真的。这次我盯着一张画歪的对比图,一上午都在怀疑模型、门控、标注,差点把模型重训一遍,到头来只是出图脚本里少接了一个返回值。

可视化这类辅助产物,一定要用坐标回读去验,不能目测。我当时凭缩略图把 y 坐标 370 估成了 140,越看越觉得框有问题。凡是「看着像」的判断,落到像素上量一遍再下结论。