19  ROI 生成与位置补正

前面几章解决的都是”找到工件”:模板匹配(章节 16)、形状匹配(章节 17)、几何基元定位(章节 14)各显神通,最终都交出一个位置和一个角度。但产线上的真正问题往往从这里才开始——找到之后呢?一个典型检测工位要在工件上量十几甚至几十个测项:孔径、边距、字符、缺陷……每个测项都有自己的 ROI(region of interest,感兴趣区域),而工件每次到位的位置都不一样。难道每个测项都自带一套定位?工业流水线的标准答案是一种简洁得多的模式:定位一次,处处受益——用一次定位得到工件的当前位姿,再把所有测量 ROI 按同一个变换”搬”到工件的新位置上。这套机制叫位置补正(fixturing)。本章用一组真实样图把它走通:被测件是一枚发光圆环加一只弧形凸耳(tab)的刚性零件(图 19.1 (a),来自 Smart3「ROI 校正示例方案」),凸耳表面有划痕,正是要检测的对象;图 19.1 (b) 是同一零件挪到画面另一处、且整体转过约 90° 的样子。白十字是定位出的圆环中心,细线指向凸耳。检测 ROI(白圆圈)在参考图上套住凸耳——接下来的全部工作,就是让那个圆 ROI 自己追上挪了 234 px 的凸耳。

(a) 参考场景(样图 001):圆环中心(十字)+ 指向凸耳的方向线,检测 ROI(白圆圈)套在凸耳上
(b) 当前场景(样图 002):同一零件平移 (+78.9, +81.0) px 并旋转约 90°,白十字为定位出的圆环中心
图 19.1: 参考位姿与当前位姿。检测 ROI 是在参考场景上定义的;零件移动后,ROI 若原地不动就会彻底脱靶(凸耳挪了 234 px)。

这组样图共 7 张,是同一枚刚性零件在不同来料位姿下的成像:圆环内孔半径在全部 7 张中稳定为 74.0 px、凸耳质心到圆环中心的距离稳定为 116 px、发光像素总数恒为约 29565——这些不变量正是”同一刚体”的指纹。零件呈现三种朝向:001/006(凸耳在左上,二者实为同一张图的重复)、002–005(凸耳转到右上、四张是纯平移)、007(凸耳转到左下)。

19.1 ROI 的类型与生成

ROI 在前面各章已经反复出场,这里做一次系统盘点。SciVision 的 SciROI 通过一组 Gen* 方法生成各种形状:矩形GenRect1,轴对齐)最通用,阈值分割、滤波等区域操作几乎都用它;旋转矩形用于框选倾斜的边缘或字符行;GenCircle)天然匹配孔、轴、焊点这类圆形特征,本章的检测 ROI 就是一个套在凸耳上的圆——章节 14 的径向搜索线也布设在圆 ROI 内;圆环GenTorus)把搜索带约束在内外半径之间,量密封圈、垫片时正合适;多边形则用来贴合不规则轮廓。一个工程事实值得回顾一句:GenRect1 的右下角是排他端点,全图 ROI 应传 (W, H) 而非 (W−1, H−1),否则会留下最外一行一列不处理(章节 7)。

既然 ROI 要跟着工件动,就得问一句:每种 ROI 在刚体变换下会变成什么?圆心动、半径不变,圆仍是圆——本章的圆形检测 ROI 经过带角度的补正后依旧是圆(实测见下文);圆环同理。轴对齐矩形旋转之后则不再轴对齐——它变成旋转矩形,这正是旋转矩形 ROI 在补正流程中无处不在的原因:参考图上随手画的正矩形,经过一次带角度的补正,全都带上了姿态。多边形按顶点逐个变换,形状不变。换句话说,刚体变换下所有这些 ROI 类型都封闭(仿射变换下圆会变成椭圆,但补正用不到那么一般的变换),ROI 跟随工件移动在几何上没有任何障碍。

ROI 有两个身份,初学者常常只看到第一个。第一个身份是限定计算范围:图像 640×480 共三十万像素,而凸耳周围半径 46 的圆 ROI 只有不到七千像素——算法只在 ROI 内跑,速度差一两个数量级。第二个身份更重要:ROI 绑定了测量语义——“这个圆 ROI 里应该有那只凸耳,要查它表面有没有划痕”。算法的参数(期望灰度、极性、阈值)都是按”ROI 内是什么”配置的;一旦 ROI 没有罩住它该罩的特征,这些参数全部失去前提,算法要么失败,要么——更危险地——在错误的内容上算出一个看似正常的结果。本章实验会专门演示后一种情况。

19.2 位姿与刚体变换

位姿(pose)是”位置 + 朝向”的合称:平面上一个刚性工件的位姿由基准点坐标 \(\mathbf{p}=(x, y)\) 和角度 \(\theta\) 三个量完全确定。基准点可以是匹配算法报告的模板原点,也可以像本章一样,由一个圆形特征的圆心充当;角度则取某个方向基准。本章把圆环内孔的圆心当基准点(平移原点),把圆心指向凸耳质心的方向当角度——圆心给出平移、凸耳给出旋转,二者合起来锁定整枚零件的位姿。设工件局部坐标系中一点 \(\mathbf{f}\)(比如凸耳质心),它在图像中的位置由位姿决定:

\[ \mathbf{x} = R(\theta)\,\mathbf{f} + \mathbf{p}, \qquad R(\theta)=\begin{bmatrix}\cos\theta & -\sin\theta\\ \sin\theta & \cos\theta\end{bmatrix}. \]

参考位姿 \((\mathbf{p}_r, \theta_r)\) 与当前位姿 \((\mathbf{p}_c, \theta_c)\) 给出同一点的两个图像坐标 \(\mathbf{x}_r\)\(\mathbf{x}_c\),消去 \(\mathbf{f}\) 即得从参考图像坐标到当前图像坐标的刚体变换(rigid transform)

\[ \mathbf{x}_c = R(\Delta\theta)\,(\mathbf{x}_r - \mathbf{p}_r) + \mathbf{p}_c, \qquad \Delta\theta = \theta_c - \theta_r. \]

写成齐次矩阵就是 章节 2 里熟悉的 \(3\times 3\) 形式:旋转部分 \(R(\Delta\theta)\),平移部分 \(\mathbf{p}_c - R(\Delta\theta)\,\mathbf{p}_r\)。位置补正的全部数学就是这一个矩阵:它对参考图上任何一点都成立,所以一次定位可以驱动任意多个 ROI。

这里藏着一个非常实用的性质,正好被本章的”粗”角度估计验证了。我们的角度并非来自高精度边缘拟合,而是凸耳区域亮像素的质心方向——一个会被光照不均、划痕分布拉偏若干度的带系统偏置的估计量。但只要这个偏置在零件坐标系下近似恒定(同一只凸耳、同一种成像),把它写成 \(\hat{\mathbf{p}} = \mathbf{p} + R(\theta)\,\mathbf{b}\),代入相对变换并对参考图上的真实特征点展开:

\[ R(\Delta\theta)\,(\mathbf{x}_r - \hat{\mathbf{p}}_r) + \hat{\mathbf{p}}_c = R(\theta_c)\,\mathbf{f} - R(\theta_c)\,\mathbf{b} + \mathbf{p}_c + R(\theta_c)\,\mathbf{b} = \mathbf{x}_c . \]

两个 \(R(\theta_c)\,\mathbf{b}\)精确抵消——只要偏置随工件一起动,它对补正没有任何影响。实验印证了这一点:尽管角度估计本身是粗糙的质心方向,但因为参考图与当前图用的是同一个估计量,补正后 ROI 中心与当前图独立测得的凸耳质心残差始终 ≤ 0.27 px(最差为样图 004,0.266 px)。这是位置补正最容易被忽视的教学点:补正用的是相对变换,定位特征的系统偏置是”两边各错一样多”,相减归零;真正吃精度的是角度的随机抖动和零件本身的非刚性。

“fixturing”一词来自机械加工的夹具(fixture):夹具把工件锁定在固定位姿,让刀具路径无需感知工件。视觉补正反其道而行——工件随意放,让”刀具路径”(ROI)去适应工件,相当于一套软件夹具

偏置抵消的前提是偏置在零件坐标系下恒定。若偏置来自照明方向、镜头畸变等图像坐标系下的因素,它不随工件旋转,就不会抵消——这正是定位特征要选对比度高、各向同性好的结构(比如本章的发光圆环内孔)的原因之一。

19.3 补正实验

完整链路是四步:定位 → 求补正矩阵 → 变换 ROI → 测量。定位分两支:圆环中心由内孔边缘求出——沿径向逐角度找暗→亮跳变得到内孔边缘点,再做最小二乘圆拟合,全部 7 张图的内孔半径稳定收敛到 74.0 px;角度则取圆心指向凸耳质心(半径 [134, 210] 内的亮像素质心)的方向。注意定位范围与测量 ROI 的分工不同:定位扫的是整枚零件的大尺度结构(直径百余像素的圆环),大到能在任何来料位置锁住工件;检测 ROI 才是又小又准、需要补正去搬的那一个。以样图 001 为参考,定位结果是圆心 (255.56, 194.54)、凸耳方向 −154.5°;检测 ROI 则定义在参考图实测的凸耳质心 (150.93, 144.67) 上(半径 46 的圆)——补正搬运的是”参考图上看到的位置”,让 ROI 的定义与定位结果同源,是工程上的惯例。

本想用 SciVision 的 SciSvEllipseLocator 拟合内孔圆,但它在这组样图上选边不稳定:对 001/006 直接返回 123501012,对 002–005 锁到外环边(半径约 87 px)而非内孔(74 px)、圆心偏约 24 px。跨图不一致的圆心会把相对位姿彻底污染,故改用确定性的”径向边缘 + 圆拟合”,内孔半径全图稳定为 74.0 px。

第二步把两个位姿交给 SciAxisTransform::Generate2DAlignMatrix,得到参考位姿到当前位姿的 \(3\times 3\) 补正矩阵。与按上节公式手算的刚体矩阵逐元素对比,最大差异在 \(10^{-3}\) 以内——SDK 做的就是那个数学。再用 TransformPointAndAngle 自检:把参考圆心过一遍矩阵,落点与当前圆心的残差 0.0000 px,自洽。

第三步 AlignROI 把检测 ROI(挂在参考图凸耳上的半径 46 px 圆)变换到当前位姿。刚体变换不改变形状,圆 ROI 变换后仍是圆。把这套流程跑遍全部 7 张样图,恢复的位姿与对齐残差列在 表 19.1:002–005 是绕参考约 +90° 的旋转叠加不同平移,007 是约 −90°,006 与 001 重合(重复图,恢复为恒等变换)。每一张的补正后 ROI 中心与当前图独立测得的凸耳质心都吻合到亚像素——最差残差仅 0.266 px

表 19.1: 7 张真实样图的恢复位姿与补正残差(参考 = 001)。“ROI 内亮像素率”是检测 ROI 内灰度 ≥100 的像素占比,反映 ROI 是否罩住发光的凸耳。
样图 恢复平移 (px) 恢复旋转 \(\Delta\theta\) ROI→凸耳残差 ROI 内亮像素率(补正/未补正)
001 (0.0, 0.0) 0.00° 0.000 px 0.718 / 0.718
002 (+78.9, +81.0) +89.36° 0.164 px 0.718 / 0.000
003 (+128.9, +81.0) +89.84° 0.097 px 0.718 / 0.000
004 (+128.9, +131.0) +89.73° 0.266 px 0.718 / 0.000
005 (+128.9, −19.0) +89.80° 0.202 px 0.719 / 0.000
006 (0.0, 0.0) 0.00° 0.000 px 0.718 / 0.718
007 (−51.0, −21.1) −89.81° 0.119 px 0.720 / 0.388

核心对照就在最后一列。以样图 002 为例,凸耳从参考位置挪了 234 px;补正后的圆 ROI 内亮像素率 0.718(凸耳满满当当地落在 ROI 里),而原地不动的 ROI 亮像素率 0.000——它落在了画面左上角全黑的背景上,一个发光像素都没罩住。图 19.2 是两者的直观对比。

(a) 不补正:ROI 留在参考位置,落在全黑背景上,亮像素率 0.000
(b) 补正后:ROI 正中套住凸耳,亮像素率 0.718,十字为补正后 ROI 中心
图 19.2: 同一当前场景(样图 002)下未补正与补正 ROI 的对比。补正后 ROI 中心与真实凸耳位置仅差 0.164 px。

第四步在补正 ROI 内做检测——本章以”亮像素率”作为凸耳是否到位的可观测量(实际产线会在此 ROI 内查划痕、量轮廓):补正 ROI 给出 0.718,与参考图上同一 ROI 的 0.718 完全一致,补正几乎没有吃掉任何检测预算。

真正值得警惕的是不补正那一侧的结果。直觉上 ROI 偏了二百多像素,检测应该干脆失败报警;但若检测算法只看返回码,它会在那片全黑背景上正常返回——亮像素率 0.000、“未发现划痕”,一个看似完美的合格结论。这是典型的静默失败(silent failure):流程不报错,字段也填满,数值却是在错误的内容上算出来的。它比干脆报错危险得多——报错会停线,静默的好结论会让坏件一路放行。工程对策是给测量加结构性门槛:ROI 内的有效信号量(如亮像素率过低)、特征是否存在、与期望模型的偏差,任何一项越界即判定测量无效,绝不只看返回码。本例中 0.000 与 0.718 的鸿沟,正是这种门槛的现成判据。

19.4 两条路线:动 ROI 还是动图像

补正还有第二条路线,也是 章节 10 里讨论过的那条”诱人的歧路”:不动 ROI,而是把整幅当前图像按补正矩阵的逆变换转回参考位姿,然后用原 ROI 照常测量。本章用手写的双线性逆映射实现了这条路线(与 章节 10 的手写旋转同款),结果见 图 19.3:样图 002 回转后,零件几乎复刻了参考图 001 的布局,原 ROI 重新套住凸耳。

图 19.3: 当前图像(002)按补正矩阵回转到参考位姿后,用原 ROI 测量。零件归位到 001 的布局,原 ROI 重新罩住凸耳。

两条路线在补正 ROI 内的亮像素率几乎相等:动 ROI 得 0.718,回转图像得 0.722——数学上它们本来就是同一个变换,差异只剩插值噪声(0.004)。但代价完全不对等:动 ROI 只变换几个几何参数(圆心两个数、半径一个数),耗时可以忽略;动图像要对全幅三十万像素做一次重采样,且双线性插值给边缘叠加一层平滑,边缘定位的重复性随之退化(章节 10 的案例量化过这种退化)。结论与那一章一致:能动 ROI 就别动图像。动图像只在两种情形下值得:算法只接受轴对齐 ROI、无法表达带姿态的检测区域;或者多个工位、多种算法要共享同一个基准坐标系,统一回转一次比每个环节各自补正更可控。

19.5 SciVision 实现

补正的核心是 SciAxisTransform 的两个接口:

SCIMV::SciAxisTransform xform;
SciMatrix M;
SciPoint refPt(poseRef.cx, poseRef.cy), curPt(poseCur.cx, poseCur.cy);
long rc = xform.Generate2DAlignMatrix(refPt, curPt,
    (float)(-poseRef.deg), (float)(-poseCur.deg), &M);   // 角度取负,见下文

SciROI fixedROI;
rc = xform.AlignROI(inspROI, M, &fixedROI);              // 圆 ROI 变换后仍是圆

Generate2DAlignMatrix 的四个输入依次是参考基准点 refPt、当前基准点 curPt、参考角度与当前角度,输出参考位姿→当前位姿的 \(3\times 3\) 矩阵 MAlignROI 把任意 SciROI 过一遍矩阵得到补正后的 ROI。验证矩阵正确性可用 TransformPointAndAngle,它同时变换一个点和一个角度,适合做”参考圆心应落在当前圆心上”的自检。

本章在真实样图上踩出了两个坑,如实记录。第一个坑最值得划重点:角度符号Generate2DAlignMatrix 的角度约定是视觉逆时针为正;而像素坐标系 y 轴向下,atan2(dy, dx) 算出的角度是视觉顺时针为正——两者差一个负号。直接把 atan2 的结果传进去,旋转方向相反,补正后的 ROI 会偏出上百像素,比不补正还离谱。正确做法是传 -poseRef.deg-poseCur.deg。坐标系手性问题在 章节 2 强调过,这里是它最疼的一次现身。

第二个坑在定位环节SciSvEllipseLocator 在这组发光圆环样图上选边不稳定——对其中两张直接返回 123501012,对另外四张锁到了外环边缘(半径约 87 px)而非目标内孔(74 px),圆心偏约 24 px。补正吃的是两次定位的差,定位源一旦在图与图之间漂移、不自洽,再精确的矩阵也会被喂进垃圾输入。本章因此改用确定性的”径向暗→亮边缘点 + 最小二乘圆拟合”求内孔圆心,全部 7 张图收敛到同一半径 74.0 px,自洽性才有保障。这条经验比任何单点精度都重要:补正系统的精度上限由定位的可重复性、而非单帧定位的绝对精度决定。生成本章全部图像与数字的完整工程位于 code/roi_and_fixturing/

工业案例:多测项共享一次定位

某连接器检测工位有 23 个测项:引脚间距、塑壳边距、卡扣轮廓……最初每个测项各自做一次模板匹配定位,节拍严重超标,且各测项的定位误差互相独立,“引脚到塑壳边”这类跨测项尺寸抖动明显。改造方案是一次形状匹配定位整个连接器,23 个测量 ROI 通过同一个补正矩阵统一变换:节拍下降约 60%,更重要的是所有测项共享同一坐标基准——测项之间的相对关系不再被各自的定位误差污染。代价是定位质量成为全局单点依赖:定位一旦失败或漂移,23 个测项全军覆没。这正是本章样图给出的教训——定位的可重复性是补正系统的命门。落地时给定位加了分数门槛与失败重试(换参数、换搜索区),定位分数随产品批次的趋势也纳入监控。

19.6 小结

  • 位置补正的标准模式是”定位一次,处处受益”:一次定位给出当前位姿,一个刚体变换矩阵驱动所有测量 ROI 跟随工件——本章在 7 张真实样图上,补正后 ROI 与凸耳的残差全部 ≤ 0.27 px,而不补正的 ROI 直接脱靶(凸耳挪了 234 px,ROI 内亮像素率从 0.718 跌到 0.000)。
  • 补正用的是相对变换,定位特征的系统偏置自动抵消:本章角度估计只是凸耳的质心方向(带系统偏置),但参考图与当前图用同一估计量,偏置在 \(R(\theta_c)\mathbf{b}\) 项的相减中归零,残差仍稳定在亚像素。
  • 静默失败比报错更危险:未补正的 ROI 落在全黑背景上,若检测只看返回码就会”正常”报出”无缺陷”。测量必须用信号量、特征存在性、参数合理性等结构性门槛守护,绝不只看返回码——本例 0.000 与 0.718 的鸿沟即是现成判据。
  • 能动 ROI 就别动图像:动 ROI 与回转图像两条路线的检测结果一致到 0.004,但后者多付一次全幅重采样和插值平滑。只有算法不支持带姿态 ROI、或多工位需共享基准系时才动图像。
  • 补正的精度上限是定位的可重复性,不是单帧绝对精度SciSvEllipseLocator 在本样图上跨图选边不一致(偏 24 px)会污染相对位姿;改用全图自洽的内孔圆拟合(半径恒为 74.0 px)后补正才稳。角度符号是另一坑——Generate2DAlignMatrix 取视觉逆时针为正,像素 atan2 须取负再传入。

关于位姿估计、刚体与仿射对齐在工业测量中的系统论述,可进一步阅读 Steger 等人的著作 (Steger, Ulrich, 和 Wiedemann 2018);对齐与图像配准的更广算法背景,可参阅 Szeliski 的教材 (Szeliski 2022)