目标检测程序拿到模型输出后,还要做一件事:把框画到原照片上。这里也可能出错——即使模型返回的数字完全没变,只要位置换算写错,框就会跑到旁边。
上一篇排查 NV12 输入时,我检查的是“模型实际读到了什么图片”。这次在 Codex 的协助下,我继续检查后半段:模型在缩小、补边后的图片上给出了位置,程序怎样把这个位置搬回原图?
我保存了真实 J6E 的模型输出,再针对每份输出分别使用按记录还原、漏减补边和错误拉伸三种方式画框。下面这张对比图中,背景相同、模型输出相同,框的位置却明显不同。

图1|左侧按实际缩放和补边记录还原;中间故意漏减补边;右侧故意按拉伸换算。两个错误版本是受控对照,用来展示接入错误的后果。
这篇要解决的就是这种位置错位:先看它怎样发生,再把缩放和补边参数传给画框代码,最后用保存的输出检查修正结果。
一、为什么不能直接把模型坐标画到原图?
这份 YOLOv5s 要求输入 672×672 的正方形图片,原照片却可能是横图或竖图。SDK 示例的处理方式是保持比例缩小,再在空出的地方补灰边,避免把物体拉变形。
以本轮一张宽 853、高 363 的横图为例:

图2|真正的图片内容只有672×285,其余是补边。193与194的差别来自整数尺寸分配。
模型返回的位置属于这张带灰边的方形图片,不属于853×363的原照片。
如果漏掉减193这一步,直接用200除以缩放比例,就会算出约254.7。同一个点,被画到了完全不同的位置。
检测框由左上角和右下角组成,两端都要做这种还原。
二、修正方法:缩图时记下来,画框时读出来
这次我让预处理多输出一份记录,保存原图尺寸、实际缩放尺寸和四侧补边。后处理直接读取它,不再临时猜图片经历了什么。
刚才那张横图的记录是:
比例根据实际缩放后的整数尺寸计算:
图片缩放时要取整数尺寸,因此横、纵比例可能略有不同。使用实际尺寸,可以让预处理和坐标还原依据同一份记录。
左上角和右下角都使用这组公式。计算中保留浮点数,还原完成后再按原图范围裁剪,最后绘图时才取整。
本轮工具的接入方式如下:
三、怎样确认变化只来自坐标换算?
实验在 J6E MatrixB 上完成,使用 OE 3.9.1、UCP 3.15.8、HBRT 4.11.11,以及前面已跑通的 KL 校准 YOLOv5s HBM。
基础画布的高宽为359×853、853×359和513×513;补充画布为363×853和853×363,后者会产生193/194的不对称补边。照片内容都保持比例放入画布。
上板前,按记录实现的预处理与官方 SDK 生成的 NV12 输入逐字节比较,28组全部一致。随后在板上执行全部视图并保存三个检测头的整数输出;首个案例还单独使用官方工具运行,三个有效输出与自有执行器逐字节一致。
拿到输出后,我按模型的量化参数解码,在672×672模型坐标中统一筛选,并执行一次 NMS(去掉重复检测框),共得到111个框。此后的各个版本都使用这些框,只改变画回原图的公式,不重复推理或重新筛选。
这次为了单独比较坐标还原,固定了模型空间中的筛选和NMS。前面图片应用沿用SDK先还原、裁剪再做NMS的顺序,因此这里的框总数不与上一篇直接比较。
四、两种写错的方法,分别偏了多少?
我比较了四种方法:按实际记录还原、使用SDK示例的理想比例公式,以及故意漏减补边、故意按拉伸换算。
“按拉伸换算”是直接用原图宽高与672的比例调整坐标,相当于假设照片被强行拉成了正方形。实际预处理却保留了长宽比并补边,两者对应不上。
下表均以“按实际记录还原”为比较基准。对每个框的左上、右下四个坐标取最大绝对差,再汇总;在裁剪到图像边界之前计算。
还原方法 | 全部框中的最大坐标差,像素 | 每框最大坐标差的平均值,像素 |
|---|---|---|
按实际记录还原 | 0,比较基准 | 0 |
SDK示例的理想比例公式 | 1.243 | 0.242 |
故意漏减补边 | 248.245 | 140.765 |
故意按拉伸换算 | 107.084 | 42.066 |
方图也有作用:没有补边时,漏减补边或按拉伸换算可能恰好得到相同结果。只用方图检查,错误就可能藏起来;横图、竖图和不对称补边才能走到不同的处理路径。
五、怎样拿去排查自己的程序?
遇到检测框偏移,可以先保存模型输出,检查模型画布上的框,再检查还原到原照片的框。如果前一步位置正常、后一步发生偏移,就重点核对实际缩放尺寸、补边和还原公式。
解压附件后进入 geometry_tool 目录,环境中需要Python和NumPy,运行:
这轮解决的是画框位置的接入问题。模型输出没变,错误的坐标解释仍然会改变显示结果。把缩放和补边记录传下去,再用横图、竖图和方图检查,能让这类错误更容易发现和复现。
参考资料
- OE 3.9.1:samples/onnx_ptq/examples/detection/yolov5x/preprocess.py、postprocess.py,以及 PadResizeTransformer 的实际执行结果。
- 本轮 geometry_transform.py、28个真实J6E案例、逐例坐标对照及84个已知矩形的往返核对记录。

