3. 特征提取与匹配:颜色、纹理、深度特征与匹配方法
大家好,我是老陈。今天咱们聊聊目标跟踪里最核心的一环——特征提取与匹配。说白了,就是让机器知道它盯着的那个目标长什么样,以及怎么在下一帧里把它认出来。
我刚开始做嵌入式视觉那会儿,总觉得特征提取就是调个库函数的事。后来在项目里栽过跟头,才明白这里面的门道有多深。嗯,咱们一个一个来看。
3.1 颜色特征:直方图
颜色特征是最直观的。你想想看,一个红色的小球在绿色草地上跑,人眼一眼就能盯住。机器也一样,它通过颜色直方图来记住目标的颜色分布。
直方图怎么做? 就是把图像从RGB转到HSV空间,然后统计H(色调)和S(饱和度)通道的分布。为什么要转HSV?因为RGB受光照影响太大,而HSV更稳定。
核心要点: 颜色直方图对旋转、缩放不敏感,但对光照变化很敏感。我建议在室外场景里,把直方图的bin数设小一点(比如16×16),这样能抗一点光照干扰。
我的经验: 在无人机跟踪项目中,我遇到过目标颜色和背景颜色相近的情况。这时候单纯靠颜色直方图,跟踪器很容易跟丢。我的做法是:把颜色直方图和位置信息结合起来,给靠近目标中心的像素更高权重。
// 颜色直方图计算示例(简化版)
// 输入:HSV图像,输出:2D直方图
void calcColorHistogram(Mat& hsvImg, Mat& hist) {
int hbins = 16, sbins = 16;
int histSize[] = {hbins, sbins};
float hranges[] = {0, 180};
float sranges[] = {0, 256};
const float* ranges[] = {hranges, sranges};
int channels[] = {0, 1};
calcHist(&hsvImg, 1, channels, Mat(),
hist, 2, histSize, ranges, true, false);
normalize(hist, hist, 0, 1, NORM_MINMAX);
}
3.2 纹理特征:HOG 与 LBP
颜色靠不住的时候,就得看纹理了。纹理特征描述的是图像的局部结构信息,说白了就是「这块区域的明暗变化模式」。
HOG(方向梯度直方图)
HOG的核心思想:物体的外观和形状,可以通过局部梯度方向分布来描述。它把图像分成小格子(cell),在每个格子里统计梯度方向,然后归一化。
HOG的坑在哪里? 计算量大!在嵌入式设备上跑HOG,帧率经常掉到个位数。我建议:
- 把cell size设大一点(比如8×8改成16×16)
- 只计算目标区域附近的HOG,别全图算
- 用积分图加速梯度计算
注意: HOG对遮挡很敏感。如果目标被遮挡超过30%,HOG特征会剧烈变化。我曾经在行人跟踪项目里吃过这个亏——目标被树挡了一半,HOG匹配直接崩了。
LBP(局部二值模式)
LBP比HOG轻量得多。它的原理很简单:把每个像素和它周围的像素比较,大于中心像素的记为1,否则记为0,形成一个二进制数。
我个人习惯用圆形LBP(Circular LBP),因为它对旋转有更好的鲁棒性。在嵌入式平台上,LBP的运算速度比HOG快3-5倍。
// 基本LBP计算
Mat calcLBP(Mat& src) {
Mat dst = Mat::zeros(src.size(), CV_8UC1);
for(int i=1; i<src.rows-1; i++) {
for(int j=1; j<src.cols-1; j++) {
uchar center = src.at<uchar>(i,j);
uchar code = 0;
code |= (src.at<uchar>(i-1,j-1) > center) << 7;
code |= (src.at<uchar>(i-1,j) > center) << 6;
code |= (src.at<uchar>(i-1,j+1) > center) << 5;
code |= (src.at<uchar>(i,j+1) > center) << 4;
code |= (src.at<uchar>(i+1,j+1) > center) << 3;
code |= (src.at<uchar>(i+1,j) > center) << 2;
code |= (src.at<uchar>(i+1,j-1) > center) << 1;
code |= (src.at<uchar>(i,j-1) > center) << 0;
dst.at<uchar>(i,j) = code;
}
}
return dst;
}
3.3 深度特征:CNN特征
这几年深度学习火起来之后,CNN特征成了主流。它通过多层卷积网络自动学习出对跟踪任务最有利的特征表示。
为什么CNN特征强? 因为它能同时捕捉颜色、纹理、形状、语义信息。浅层网络关注边缘和纹理,深层网络关注物体部件和整体结构。
但注意,在嵌入式设备上跑CNN特征提取,有两个大坑:
- 模型太大: 一个VGG16有138M参数,嵌入式设备根本跑不动
- 推理太慢: 即使是用MobileNet,在树莓派上也只能跑5-10fps
我的建议: 在嵌入式平台上,用轻量级网络(如MobileNetV3、ShuffleNetV2)提取特征。如果算力实在不够,可以只在第一帧用CNN提取一次特征模板,后续帧用传统方法匹配。这叫「混合特征策略」。
3.4 特征匹配方法
特征提取完了,怎么比?常用的有两种:余弦相似度和欧氏距离。
| 方法 | 公式 | 特点 | 适用场景 |
|---|---|---|---|
| 余弦相似度 | cos(θ) = (A·B) / (||A|| ||B||) | 关注方向,忽略幅度 | 光照变化大的场景 |
| 欧氏距离 | d = √(Σ(Ai-Bi)²) | 关注绝对差异 | 特征已经归一化的场景 |
选哪个? 我个人习惯:如果特征向量已经做过L2归一化,用欧氏距离和余弦相似度是等价的。但如果没归一化,余弦相似度更稳定。
避坑指南: 我曾经在特征匹配时直接用欧氏距离,结果光照一变,匹配分数剧烈波动。后来改成余弦相似度,稳定性提升了不少。记住:特征匹配前,一定要做归一化处理。
3.5 知识体系总览
下面这张图总结了本章的核心逻辑。你可以看到,特征提取和匹配是一个完整的链路:从原始图像出发,经过不同的特征提取方法,最后通过匹配方法找到目标。
3.6 实战建议
最后,给大家几个实战中的小建议:
- 特征融合: 别只用一种特征。我习惯把颜色直方图和LBP特征拼接起来,效果比单独用任何一种都好。
- 自适应权重: 光照好的时候,颜色特征权重高一点;光照差的时候,纹理特征权重高一点。这个权重可以动态调整。
- 缓存特征模板: 在嵌入式设备上,每次重新提取特征很费时。我建议每隔5-10帧更新一次特征模板,而不是每帧都更新。
- 阈值设置: 匹配分数低于0.6(余弦相似度)时,基本可以认为目标丢失了。这时候需要启动重检测机制。
曾经踩过的坑: 我在一个安防监控项目里,用了纯颜色特征做跟踪。结果一到傍晚,光照变化剧烈,跟踪器直接放飞自我了。后来改成颜色+纹理特征融合,才稳住了。记住:单一特征在复杂环境下就是定时炸弹。
好了,特征提取与匹配就聊到这儿。记住一句话:没有最好的特征,只有最合适的特征组合。下一章咱们聊聊目标检测和跟踪的协同工作,到时候见。