2、数学基础回顾:图像卷积操作、归一化原理、相似度度量(SAD、SSD、NCC)、傅里叶变换简介
各位同学,欢迎来到第二章。
说实话,很多做嵌入式视觉的朋友,一听到「数学基础」四个字就想关页面。我当年也一样。但后来被现实狠狠教育了一顿——有一次我在STM32上跑模板匹配,结果死活对不准,折腾了两天,最后发现是归一化没做对。嗯,从那以后我再也不敢跳过这些基础了。
这一章,咱们不搞天书。我会用最直白的方式,把卷积、归一化、相似度度量、傅里叶变换这几个东西讲清楚。你只要跟着走一遍,后面写代码的时候心里就有底了。
2.1 图像卷积操作
卷积是什么?说白了就是「加权求和」。你拿一个小窗口(叫卷积核),在图像上滑来滑去,每个位置都算一遍加权和。
我习惯这么理解:图像是二维数组,卷积核是另一个小二维数组。卷积核滑到哪,就跟图像对应位置做点乘,然后求和。
核心公式:
输出图像(x, y) = Σ Σ 输入图像(x+i, y+j) * 卷积核(i, j)
其中 i, j 遍历卷积核的所有位置。
举个例子,一个 3x3 的均值滤波核:
核 = [
[1/9, 1/9, 1/9],
[1/9, 1/9, 1/9],
[1/9, 1/9, 1/9]
]
这个核滑过图像,每个像素都被周围9个像素的平均值替代。效果就是图像变模糊了。
实战小技巧:
我在做模板匹配时,经常先用高斯卷积核把图像模糊一下。为什么?因为模板和原图之间总有细微的噪声差异,模糊一下能提高匹配的鲁棒性。代价就是边缘信息会丢失一点,但匹配成功率能提升不少。
嵌入式部署时要注意什么?卷积的计算量跟核大小成正比。3x3的核,每个像素要做9次乘加。如果是7x7,那就是49次。在资源受限的芯片上,我建议优先用3x3或5x5的核。
避坑指南:
我曾经在FPGA上做卷积加速,没注意边界处理。结果图像边缘出现了一圈黑边。后来才想起来,卷积操作默认在边界补零(zero padding)。如果你的应用对边缘精度要求高,记得检查边界处理方式。
2.2 归一化原理
归一化,就是把数据拉到同一个尺度上。为什么要做?你想想看,如果一张图很亮,另一张图很暗,直接比像素值肯定比不准。
我常用的归一化方式有两种:
| 方法 | 公式 | 适用场景 |
|---|---|---|
| Min-Max归一化 | (x - min) / (max - min) | 像素值范围已知,简单粗暴 |
| Z-score归一化 | (x - μ) / σ | 数据分布近似正态,抗异常点 |
在模板匹配里,我最常用的是「均值归一化」——把图像减去均值,再除以标准差。这样不管图像是亮是暗,都能拉到差不多的范围。
归一化后的图像特点:
- 均值为0,标准差为1
- 不受整体亮度变化影响
- 匹配时对光照变化有很强的鲁棒性
有一次我在做工业视觉项目,车间灯光忽明忽暗。模板匹配老是误报。后来加了归一化预处理,问题就解决了。说白了,归一化就是给你的算法加了一层「抗干扰护甲」。
2.3 相似度度量:SAD、SSD、NCC
模板匹配的核心问题:怎么判断模板和图像某一块「像不像」?
有三种经典方法,我一个个说。
SAD(绝对差值和)
公式很简单:
SAD = Σ |模板像素 - 图像像素|
SAD越小,说明越像。计算量最小,嵌入式上跑得飞快。但缺点也很明显——对光照变化敏感。你想想,整体亮度一变,所有像素值都变了,SAD自然就大了。
我的经验:
SAD适合在光照稳定的场景下用。比如你做一个固定光源的检测工位,SAD完全够用,而且速度快。
SSD(差值的平方和)
SSD = Σ (模板像素 - 图像像素)²
SSD跟SAD类似,但做了平方。好处是放大了差异——两个像素差1,平方还是1;差10,平方就是100。这样匹配时对「大差异」更敏感。
但坏处也一样:对光照敏感。而且平方运算在嵌入式上比绝对值慢一点点(不过现代MCU基本感觉不到差别)。
NCC(归一化互相关)
这才是重头戏。公式长这样:
NCC = Σ ( (模板-μ模板) * (图像-μ图像) ) / (σ模板 * σ图像)
别被公式吓到。说白了,NCC就是先把模板和图像都归一化,再算它们的相关性。结果在-1到1之间,1表示完全匹配,-1表示完全相反。
NCC的优点:
- 对光照变化不敏感(因为做了归一化)
- 对对比度变化也不敏感
- 结果直观,0.9以上基本就是匹配上了
但NCC也有代价——计算量大。每个位置都要算均值、标准差,再做乘加。在嵌入式上,我一般只在关键位置用NCC,其他地方先用SAD或SSD快速筛选。
| 方法 | 计算量 | 光照鲁棒性 | 推荐场景 |
|---|---|---|---|
| SAD | 低 | 差 | 光照稳定、追求速度 |
| SSD | 中 | 差 | 需要放大差异时 |
| NCC | 高 | 好 | 光照变化大、精度要求高 |
避坑指南:
我曾经在ARM Cortex-M4上直接跑NCC,结果一帧图像处理了3秒。后来改成先下采样,再用NCC,速度提升到0.2秒。记住:嵌入式上,精度和速度永远是 trade-off。
2.4 傅里叶变换简介
傅里叶变换,听起来高大上,其实就干一件事:把图像从「空间域」变到「频率域」。
空间域就是像素坐标,频率域就是「图像变化的快慢」。比如一张纯色图,频率很低;一张纹理丰富的图,频率很高。
公式我就不写了(网上到处都有),我直接说在模板匹配里怎么用:
- 快速匹配:用FFT(快速傅里叶变换)把模板和图像都变到频域,在频域做相关运算。计算量从O(N²)降到O(N log N)。图像越大,优势越明显。
- 频域滤波:在频域把高频噪声滤掉,再变回空间域。效果比空间域滤波好,但计算量大。
我的建议:
傅里叶变换在嵌入式上不太常用,因为FFT需要浮点运算,很多MCU不支持。但如果你用的是带FPU的芯片(比如STM32F4系列),或者有硬件FFT加速器,那就可以试试。我曾在i.MX RT上用过FFT做模板匹配,速度确实快了不少。
嗯,这一章的内容就到这里。数学基础看着枯燥,但后面写代码的时候你会发现,每一步都离不开这些概念。下一章咱们就开始动手,把模板匹配算法一步步搬到嵌入式平台上。
本章要点回顾:
- 卷积就是加权求和,核大小影响计算量
- 归一化能消除光照影响,是匹配前的必备步骤
- SAD/SSD快但不抗光照,NCC慢但鲁棒
- 傅里叶变换适合大图像加速,但嵌入式上慎用
有问题欢迎交流。咱们下章见。