25 形状特征
经过前面几章,我们手里已经有了不少”把图像变成数”的工具:blob 分析给出面积与重心(章节 23),轮廓分析给出周长与圆度,边缘检测给出位置与方向。但产线上的问题远不止这些。一条蜿蜒的 PCB 走线,它的”长度”和”宽度”该怎么定义?两块平均亮度完全相同的表面,一块是均匀的合格涂层、一块是布满麻点的次品,用什么数字区分它们?一块板卡要做粗定位,去哪里找几个”既稀疏又稳定”的参考点?本章把三件各自独立、但都回答”如何把形状压缩成可判定的数”的工具收进同一个工具箱:骨架(skeleton)给细长结构提取中轴,灰度与纹理特征给表面外观提取统计指纹,角点(corner)给几何轮廓提取地标。图 25.1 是骨架实验的测试场景——一条 Y 形分叉走线、一条 L 形走线和一个带方孔的团块,都是 5 个像素宽或包含细长/块状结构的典型样本。
25.1 骨架
想象在二值形状内部放一个尽可能大的圆,让它在形状里滚动且始终与边界相切——所有这些最大内切圆圆心的轨迹,就是形状的中轴(medial axis),把每个圆心再附上对应的内切圆半径,就得到中轴变换(medial axis transform)。直觉上,中轴是形状的”脊梁”:对一条等宽走线,中轴恰好是沿走线方向的中心线,而内切圆半径恰好是半宽。这正是细长结构最自然的描述方式——把二维的”一片像素”还原成一维的”一条线加宽度”。
中轴对边界噪声很敏感:边界上一个小凸起就会让中轴长出一条多余的分枝。工程实现(包括细化算法)通常配合 章节 8 的开闭运算先平滑边界,或对短分枝做剪枝(pruning)。
工程上计算骨架最常用的是细化(thinning)算法:迭代地从形状边界剥除像素,但保证每次删除都不改变连通性——既不把一个形状剥成两段,也不把孔剥没了——直到只剩一条单像素宽的中心线为止。它属于 章节 8 介绍的形态学算法家族,输出是中轴的离散近似。
对 图 25.1 运行 SciVision 的骨架算法,结果见 图 25.2。三个形状产出 3 条骨架轮廓;整个场景的前景共 17729 个像素,骨架只剩 738 个像素——24.0 倍的压缩,而细长结构的拓扑与走向信息一点没丢。注意右上角带孔团块的骨架:它围绕方孔形成一个闭环,这正是细化”保拓扑”的体现——形状有一个孔,骨架就有一个环,断不开也缩不掉。
骨架是一条结构化的曲线,它身上的特征点本身就有工程含义:端点(endpoint)是走线的起止,交叉点(junction)是分叉处。对 Y 形走线的骨架做特征点提取,得到 3 个端点、1 个交叉点,与真值完全一致;端点位于 (70, 51)、(120, 190)、(170, 51),交叉点位于 (120, 119),与几何真值 (120, 120) 仅差 1 个像素。对走线检查来说,这两类点就是”线路图”:端点数对不上意味着断路,交叉点数对不上意味着桥连。
骨架最重要的应用范式是一个简单的除法:
\[ \bar{w} = \frac{A}{L}, \]
其中 \(A\) 是走线的前景面积,\(L\) 是骨架长度(像素数),\(\bar{w}\) 即走线的平均宽度。对场景中的 L 形走线(设计宽度恰为 5.00 px)实测:面积 1313 px,骨架长 257 px,平均宽度 \(1313/257 = 5.11\) px。误差从哪来?骨架长度按 4 连通像素计数,在 L 形的拐角处,离散计数会比几何弧长略短(拐角被”抄了近路”),分母偏小自然让宽度略偏大。2% 的系统偏差对趋势监控完全够用,但若要绝对宽度合格判定,应当用弧长(对角步长按 \(\sqrt{2}\) 计)或卡尺测量校准。诚实地报告这类离散化偏差,比假装”实测=真值”更有工程价值。
骨架的典型用途可以列成一张清单:
- 线宽检测:面积/骨架长得平均宽度,沿骨架逐点取内切圆半径得宽度序列;
- 断路与桥连检测:数端点与交叉点,与设计网表比对;
- 路径规划与长度测量:蜿蜒走线、裂纹、纤维的”真实长度”就是骨架长度。
25.2 灰度与纹理特征
第二组工具回答的问题是:一块区域的表面外观怎么变成数?最先想到的是一阶统计——把区域内所有像素的灰度直方图算出来,取均值、标准差、熵(entropy)。它们便宜、直观,但有一个根本盲区:一阶统计只看”有哪些灰度值”,完全不看”这些灰度值怎么排列”。
图 25.3 的四个 100×100 纹理区就是为暴露这个盲区设计的:均匀灰度 140、高对比棋盘(40/240 相间)、水平渐变(40→240)、高斯噪声(\(\mu=140\), \(\sigma=20\))——四个区的均值全部约等于 140(实测 140.00 / 139.84 / 139.51 / 139.76)。靠均值,四个区是同一个区。标准差能拉开一些差距(0 / 100.00 / 58.18 / 20.04),但请看熵:渐变区 6.64、噪声区 6.36,几乎一样——一阶统计眼里,“平滑过渡的渐变”与”乱成一团的噪声”难分彼此,因为两者的直方图都摊得很开。
要看”排列”,就要看像素对。灰度共生矩阵(gray-level co-occurrence matrix, GLCM)统计的是:在给定方向与距离上(最常用:水平相邻),灰度为 \(i\) 的像素旁边出现灰度为 \(j\) 的像素的频率 \(P(i,j)\)——它是相邻像素对灰度的联合分布。把灰度量化成少数等级(本实验为 16 级)后,这个矩阵不大,但纹理的”排列规律”全写在里面:均匀纹理的能量集中在对角线一个格子里,渐变纹理集中在对角线附近的窄带上,噪声纹理摊满整个矩阵。从 \(P(i,j)\) 提炼出四个常用标量:
- 能量(energy) \(\sum_{i,j} P(i,j)^2\):分布越集中(纹理越单一、越规则)值越大,均匀区为 1;
- 对比度(contrast) \(\sum_{i,j} (i-j)^2 P(i,j)\):相邻像素灰度差越大值越大,度量”局部反差”;
- 相关性(correlation):\(i\) 与 \(j\) 的统计相关系数,相邻像素”知道彼此”的程度;
- 同质性(homogeneity) \(\sum_{i,j} \frac{P(i,j)}{1+|i-j|}\):质量越靠近对角线值越大,度量”局部平滑”。
GLCM 是有方向的:本实验统计水平相邻对。对各向同性的纹理(噪声、均匀),方向无关紧要;对各向异性的纹理(划痕、拉丝、本例的水平渐变),换个方向数字会完全不同——垂直方向统计渐变区会得到对比度 0。工程上要么按缺陷的已知走向选方向,要么取四个方向的均值。
四个区的实测全表如下(均值/标准差为自算,原因见 小节 25.4;GLCM 取水平方向、16 灰度级):
| 区域 | 均值 | 标准差 | 熵(一阶) | GLCM 能量 | 对比度 | 相关性 | 同质性 |
|---|---|---|---|---|---|---|---|
| 均匀 140 | 140.00 | 0.00 | 0.00 | 1.000 | 0.000 | 0.000 | 1.000 |
| 棋盘 | 139.84 | 100.00 | 1.00 | 0.393 | 20.485 | 0.758 | 0.879 |
| 渐变 | 139.51 | 58.18 | 6.64 | 0.061 | 0.131 | 0.996 | 0.934 |
| 噪声 \(\sigma=20\) | 139.76 | 20.04 | 6.36 | 0.048 | 3.350 | −0.015 | 0.467 |
逐行读一遍”为什么是这个签名”。均匀区是退化情形:只有一个灰度,能量 1、同质 1、对比 0,相关性因方差为零而无定义(SDK 返回 0)。棋盘区的签名是高对比(20.48,全表最高两个数量级):8 px 的格子让约 1/8 的水平相邻对横跨黑白边界,每跨一次贡献巨大的 \((i-j)^2\);其余 7/8 的像素对同格同灰度,所以相关性仍有 0.758——“反差剧烈但排列有规律”。渐变区的签名是高相关(0.996)配低对比(0.131):水平相邻像素只差约 2 个灰度级,彼此几乎完全可预测——“变化大但变化得有秩序”。噪声区的签名是低同质(0.467,全表最低)加零相关(−0.015):相邻像素相互独立,谁也预测不了谁。四个均值相同的区,在 GLCM 的四个量上各占一个角,清晰可分——这就是二阶统计的价值:它把”排列”变成了数。
25.3 角点
第三组工具找的是几何上的地标。角点之所以特殊,可以用一个小窗口的”自相似性”来理解:把窗口沿任意方向挪一小步,看窗口内容变化多大。在平坦区域,往哪儿挪都没变化;在边缘上,沿边缘挪没变化、垂直于边缘挪变化大——只有一个方向”敏感”;而在角点处,往任何方向挪内容都剧烈变化——两个独立方向上都有强梯度。Harris 角点检测器 (Harris 和 Stephens 1988) 把这个直觉写成窗口内梯度结构矩阵的特征值判据:两个特征值都大,才是角。
实验场景是一块 L 形六边形板(6 个直角,其中 1 个是凹角)加一个干扰件——圆角半径 25 px 的圆角矩形,它没有任何尖角。用 Harris 方法、筛选比例 0.30 提取角点,结果见 图 25.4 (a):恰好检出 6 个角点,与真值一一对应,每个角点距几何真值都是 1.41 px——一个一致的 \((\pm 1, \pm 1)\) 偏置。偏置如此整齐并非巧合:Harris 响应在梯度结构最丰富处达到峰值,而对一个由 5×5 邻域(blockSize=5)评估的直角,这个峰值稳定地落在角的内侧一个对角像素上。系统性偏置可以标定补偿,随机抖动才是精度杀手——这 1.41 px 属于前者。
把筛选比例从 0.30 降到 0.02,检出数从 6 暴涨到 14(图 25.4 (b)),其中 8 个落在圆角矩形的四个圆角上——每个圆角被检出 2 个”角点”。这揭示了一个常被忽略的事实:角点”强度”是连续量,“是不是角”是工程定义。圆角处曲率不为零,两个方向上确实都有梯度,Harris 响应确实大于零——只是比直角弱。阈值定在哪里,“角”的边界就在哪里。这与 章节 13 的阈值整定是同一个哲学:算法输出的是响应图,“判定”永远是人替算法做的。整定方法也相同——用含已知干扰件的样本扫描阈值,找出”全检出真角、零检出圆角”的安全区间。
角点找到之后做什么?三类典型用途:用 2~3 个稳定角点做工件的粗定位(比模板匹配快得多);标定板的方格交叉点本质就是角点,是 相机标定 的输入;以及作为特征匹配的兴趣点——章节 18 的特征匹配内部第一步正是检测这类”往哪儿挪都变化”的点。
25.4 SciVision 实现
骨架由 SCIMV::SciSvSkeleton 提供,两个接口配合使用:
SCIMV::SciSvSkeleton sk;
SciContourArray arr;
sk.Skeleton(src, roi, &arr); // 细化 -> 骨架轮廓数组
SciPointArray ends, juncs;
sk.ExtractJunctions(arr[i], &ends, &juncs); // 单条轮廓的端点/交叉点Skeleton 把 ROI 内每个连通形状细化为骨架,输出为轮廓数组——注意它在分叉处不拆分:Y 形走线的整个骨架是一条轮廓,而不是三条枝。这是刻意的工程设计:拆不拆、在哪儿拆,由 ExtractJunctions 决定——它接收单条骨架轮廓,输出端点数组与交叉点数组,调用方拿着交叉点再按需切分。“细化归细化、解析归解析”的两段式让一个 API 同时服务于只要长度的用户和要完整拓扑的用户。
灰度特征由 SCIMV::SciSvGreyFeature 提供:
SCIMV::SciSvGreyFeature gf;
double ent, aniso;
gf.GetEntropyGreyFeature(src, roi, &ent, &aniso); // 一阶熵 + 各向异性
double ge, en, corr, homog, contr;
gf.GetGLCMFeature(src, roi,
SCI_SV_GREYFEATURE_GLCM_DIRECTION_HORIZONTAL, // 方向:水平相邻对
16, // 灰度量化级数
&ge, &en, &corr, &homog, &contr); // GLCM 熵/能量/相关/同质/对比GetGLCMFeature 的 greyClass=16 是量化级数:级数越多矩阵越大、对噪声越敏感,16 是常用折中。方向参数的意义见 小节 25.2 的边注。提醒一句旧坑:SciSvHistogram::CaculateHist 的 stdValue 是直方图 bin 计数的标准差而不是灰度标准差,所以本章表格中的均值与标准差按定义自算。
角点由 SCIMV::SciSvCornerExtraction 提供:
SCIMV::SciSvCornerExtraction ce;
SciPointArray pts;
ce.ExtractCorners(src, roi,
0, // method: 0 = Harris
5, // blockSize: 梯度结构矩阵的评估邻域
50, // maxnumCorners: 数量上限
0.30, // filterRatio: 响应筛选比例(本章敏感性实验的主角)
10, // minDistance: 角点间最小距离,抑制同一角的重复检出
&pts);minDistance=10 解释了为什么弱阈值下每个圆角”只”检出 2 个点——没有它,整段圆弧都会冒出检出。生成本章全部图像与数据的完整工程位于 code/shape_features/。
工业案例:柔性电路断路检测
某 FPC(柔性电路板)产线在蚀刻后检查走线健康度。最初方案用 blob 连通性分析:走线断成两个 blob 即报断路。它只能抓”完全断开”,而蚀刻过度造成的细颈(走线局部变细但未断)全部漏检——细颈件在弯折测试中批量失效。改进方案引入骨架:对每条走线提取骨架,沿骨架做滑动窗口的”局部面积 / 局部骨架长”,得到一条沿线宽度序列。完好走线的宽度序列平直,细颈表现为序列上一个清晰的谷值——设一条宽度下限,半断的走线在断之前就被拦下。教训:骨架把”二维形状健康度”降成了”一维信号”,而一维信号上,经典的阈值、趋势、SPC 工具全部立即可用——降维不是丢信息,是把信息摆到老工具够得着的地方。
25.5 小结
- 骨架是细长结构的中轴:细化算法迭代剥边界、保拓扑(孔变闭环),把 17729 px 的前景压成 738 px 的中心线(24 倍)而不丢走向与拓扑;端点/交叉点直接对应断路/桥连判据。
- 宽度 = 面积 / 骨架长是骨架最实用的范式(实测 5.11 vs 真值 5.00);像素计数在拐角低估弧长带来约 2% 系统偏差,趋势监控可忽略,绝对判定需校准。
- 一阶统计看不见”排列”:四个均值同为 140 的纹理区靠均值与熵无法完全区分;GLCM 把相邻像素对的联合分布变成能量/对比/相关/同质四个数,四区各得一个唯一签名(棋盘=高对比、渐变=高相关低对比、噪声=低同质零相关)。
- 角点强度是连续量,“是不是角”是工程定义:filterRatio=0.30 恰好检出 6 角、一致偏置 1.41 px(可标定);放宽到 0.02 则圆角干扰件贡献 8 个误检——阈值整定必须用含干扰件的样本扫描。
- 三件工具的共同点是压缩:把一片像素压成一条线、一个分布、几个点,后续的判定逻辑才有抓手。
中轴(medial axis)的原始定义出自 Blum 提出的形状描述变换 (Blum 1967),它正是本章骨架的理论母体;GLCM 纹理特征的经典出处是 Haralick 等人关于纹理图像分类的奠基论文 (Haralick, Shanmugam, 和 Dinstein 1973);与 Harris 角点 (Harris 和 Stephens 1988) 同族、并被广泛用于工件粗定位的 Shi–Tomasi 角点判据见 (Shi 和 Tomasi 1994);位姿无关的形状描述子方面,Hu 的矩不变量 (Hu 1962) 是经久不衰的经典。骨架与中轴变换的严格定义、GLCM 纹理特征的系统工程讨论,可进一步阅读 Steger 等人的著作 (Steger, Ulrich, 和 Wiedemann 2018)。




