4. 特征提取与分类

好,我们进入第四章。前面几章我们讲了怎么把图像处理好,把缺陷区域分割出来。但分割出来的区域只是一堆像素点,怎么让机器知道它到底是划痕、脏污还是气泡?这就靠特征提取和分类了。

说白了,特征提取就是把图像信息转化成数字,分类器根据这些数字做判断。我刚开始做视觉项目时,总觉得这步是纯数学,后来发现,选对特征比调分类器参数重要得多。

核心思路: 分割得到区域 → 提取数值特征 → 训练分类器 → 自动判定缺陷类型
特征提取与分类流程 分割后区域 几何特征 纹理特征 特征向量 分类器(SVM/KNN) 缺陷类型判定 面积、周长、圆度 LBP、GLCM

4.1 Blob分析:先搞清楚区域是什么

Blob分析,其实就是对分割后的连通区域做基础测量。每个连通区域就是一个Blob,我们要知道它的大小、形状、位置。

我个人习惯把Blob分析分成三步:

  1. 连通域标记:给每个独立区域编号
  2. 基础属性提取:面积、质心、外接矩形
  3. 筛选过滤:去掉太小或太大的噪声区域
经验之谈: 我在做金属表面检测时,经常先按面积过滤掉灰尘点(面积<50像素),再分析剩下的区域。这一步能减少90%的误报。

4.2 几何特征:形状就是信息

几何特征是最直观、最稳定的特征。你想想看,划痕通常是细长的,气泡是圆形的,脏污是不规则形状。这些形状差异,用几个数值就能区分。

特征名称 计算公式 典型应用场景
面积 区域内像素总数 区分大缺陷与小噪声
周长 区域边界像素数 判断缺陷边缘复杂度
圆度 4π×面积 / 周长² 区分圆形气泡与不规则划痕
长宽比 外接矩形长边/短边 识别细长裂纹
矩形度 面积 / 外接矩形面积 判断缺陷是否接近矩形

这里我要特别说一下圆度。圆度值越接近1,说明区域越圆。我曾经在检测锂电池极片时,就用圆度区分了电解液气泡(圆度>0.85)和极片划痕(圆度<0.5),准确率直接到了98%。

注意: 周长对边界噪声非常敏感。如果图像边缘有锯齿,算出来的周长会偏大,圆度就会偏小。建议先做一次形态学闭运算再提取几何特征。

4.3 纹理特征:看表面,不只是看轮廓

有些缺陷形状差不多,但表面纹理不同。比如布料上的油污和磨损,形状可能都是不规则的,但油污区域纹理更均匀,磨损区域纹理更粗糙。这时候就需要纹理特征了。

4.3.1 LBP(局部二值模式)

LBP的原理很简单:比较中心像素和周围像素的大小关系。比中心大记1,比中心小记0,然后组成一个二进制数。这个数就代表了局部纹理模式。

我记得有个项目检测木材表面缺陷,用LBP直方图做特征,配合SVM分类器,对活节、死节、虫眼的分类准确率达到了95%以上。LBP对光照变化不敏感,这是它最大的优势。

// LBP特征提取核心步骤(伪代码)
for each pixel in image:
    center = pixel value
    binary_pattern = 0
    for i in 0 to 7:
        neighbor = get_neighbor(pixel, radius, i)
        if neighbor >= center:
            set_bit(binary_pattern, i)
    lbp_image[pixel] = binary_pattern

// 统计直方图作为特征向量
histogram = compute_histogram(lbp_image, bins=256)

4.3.2 GLCM(灰度共生矩阵)

GLCM统计的是图像中特定灰度对出现的频率。它能反映纹理的粗糙度、对比度、方向性等特性。

常用的GLCM特征有:

  • 对比度:纹理的清晰程度,值越大纹理越深
  • 相关性:纹理的方向性,值越大方向性越强
  • 能量:纹理的均匀程度,值越大纹理越规则
  • 同质性:纹理的局部变化程度
选哪个? 如果缺陷区域小(<50×50像素),LBP更稳定;如果区域大且纹理有方向性(如织物、木材),GLCM效果更好。我一般两个都算,然后用特征选择算法挑出最有效的几个。

4.4 基于传统机器学习的分类器

特征提取完了,接下来就是分类。传统机器学习分类器在工业场景中依然很能打,尤其是样本量不大的时候。

4.4.1 SVM(支持向量机)

SVM的核心思想是找一个超平面,把不同类别的样本分开,并且让这个超平面离最近的样本点尽可能远。

为什么SVM在工业视觉中这么受欢迎?

  • 小样本学习能力强:几十个样本就能训练出可用的模型
  • 泛化能力好:不容易过拟合
  • 核函数灵活:线性不可分时,用RBF核映射到高维空间

我曾经用SVM做手机屏幕划痕检测,只用了200个正样本和200个负样本,RBF核+SVM,测试准确率就达到了99.2%。

// SVM训练示例(Python风格伪代码)
from sklearn import svm

// 特征矩阵:每行是一个样本的特征向量
X_train = [[area, roundness, lbp_hist[0], ...], ...]
y_train = [0, 1, 0, 1, ...]  // 0:良品, 1:缺陷

// 创建SVM分类器(RBF核)
clf = svm.SVC(kernel='rbf', C=1.0, gamma='scale')

// 训练
clf.fit(X_train, y_train)

// 预测
result = clf.predict(X_test)

4.4.2 KNN(K近邻)

KNN更简单:新样本来了,找它最近的K个邻居,看这些邻居中哪类最多,新样本就属于哪类。

KNN的优点和缺点都很明显:

对比项 SVM KNN
训练速度 中等(需要求解优化问题) 极快(只需存储样本)
预测速度 快 慢(需要计算所有距离)
样本量要求 小样本即可 需要较多样本覆盖空间
对特征尺度敏感 是(需要归一化) 是(必须归一化)
可解释性 中等(支持向量可解释) 好(直接看邻居)
避坑指南: 我曾经在项目里直接用KNN没做特征归一化,结果面积特征(数值范围0-10000)完全主导了距离计算,纹理特征(数值范围0-255)基本没起作用。后来加了标准化,准确率从60%直接跳到92%。特征归一化,千万别省。

4.5 特征选择:别把所有特征都扔进去

特征不是越多越好。冗余特征会降低分类器性能,还会增加计算时间。

我常用的特征选择方法:

  1. 方差过滤:去掉方差接近0的特征(所有样本都一样,没区分度)
  2. 相关性分析:去掉高度相关的特征(比如面积和周长通常强相关,留一个就行)
  3. 递归特征消除:用模型训练,每次去掉最不重要的特征
我的经验: 对于大多数工业缺陷检测场景,5-10个精心挑选的特征就足够了。我见过有人提取了50多个特征,结果分类准确率还不如只用面积+圆度+LBP三个特征。少即是多,在特征工程里特别适用。

4.6 实战要点总结

好了,这一章的内容就这些。最后我整理几个关键点,你在实际项目中可以对照着用:

  • 先几何,后纹理:能用形状区分的,就别上纹理,计算量小很多
  • 特征归一化是必须的:所有特征统一到[0,1]或[-1,1]区间
  • SVM优先,KNN备选:样本少用SVM,样本多且需要快速迭代用KNN
  • 验证集不能省:至少留20%的数据做验证,别在训练集上自嗨

特征提取和分类是视觉检测的决策核心。选对特征,用对分类器,你的检测系统才能真正从「看到」变成「看懂」。