4、对焦评价函数:梯度、方差与熵
各位同学,欢迎来到第四章。前面我们聊了低光照下对焦的难点,也讲了图像预处理怎么把信号质量提上来。那接下来要解决一个核心问题:怎么判断当前画面是不是最清晰的?
说白了,我们需要一把“尺子”,去量每帧图像的清晰度。这把尺子,就是对焦评价函数(Focus Measure)。
我刚开始做嵌入式相机调试时,觉得这玩意儿不就是算个梯度嘛,有啥难的?后来在暗光下跑了一遍,才发现坑多得很。有的函数对噪声敏感得要命,有的算得慢,有的干脆在低对比度场景下“失明”。
今天我们就把这几个常用的评价函数掰开揉碎了讲。你理解透了,后面调AF算法才能做到心中有数。
4.1 梯度类函数:边缘越锐,值越大
图像清晰度,本质上就是边缘的锐利程度。边缘越陡峭,像素之间的灰度变化越剧烈,梯度值就越大。所以,梯度类函数是最直观、最常用的对焦评价手段。
我个人的习惯是,在项目初期先用梯度函数快速验证对焦流程,等系统跑通了再根据场景换更鲁棒的方案。
4.1.1 Brenner 梯度函数
Brenner 是最简单的梯度函数之一。它只计算相隔两个像素的灰度差,然后平方求和。
公式长这样:
F = Σ |I(x+2, y) - I(x, y)|²
为什么只隔两个像素?因为相隔太近(比如1个像素),容易受噪声干扰;相隔太远,又会丢失细节。Brenner 这个“步长2”的设计,算是一种经验折中。
4.1.2 Sobel 与 Laplacian 算子
Sobel 和 Laplacian 是更“正规”的边缘检测算子。它们通过卷积核计算图像的一阶或二阶导数。
Sobel 算子:计算水平和垂直两个方向的梯度,然后取模或平方和。
Gx = [[-1, 0, 1],
[-2, 0, 2],
[-1, 0, 1]]
Gy = [[-1, -2, -1],
[ 0, 0, 0],
[ 1, 2, 1]]
F = Σ (Gx² + Gy²)
Laplacian 算子:直接计算二阶导数,对边缘的响应更敏感。
L = [[ 0, -1, 0],
[-1, 4, -1],
[ 0, -1, 0]]
F = Σ |L|
这两者的区别,我画了张图帮你理解:
4.1.3 Tenengrad 梯度函数
Tenengrad 本质上就是 Sobel 的“升级版”。它用 Sobel 算子算出每个像素的梯度模值,然后对所有像素的模值求和。
F = Σ (Gx² + Gy²)
嗯,公式和 Sobel 一样?对,但 Tenengrad 强调的是全局梯度能量。它不关心边缘方向,只关心整体梯度强度。
我记得在一个安防摄像头的项目中,场景是夜间停车场,光照极低。我们用 Tenengrad 做粗对焦,配合后面要讲的方差函数做精对焦,效果出奇地好。为什么?因为 Tenengrad 对均匀区域的噪声不敏感,它只响应真正的边缘。
4.2 统计类函数:方差与熵
梯度函数依赖边缘,那如果场景本身就没有明显边缘呢?比如拍一面白墙,或者拍天空。这时候梯度函数基本就“躺平”了。
统计类函数就是用来补这个短板的。
4.2.1 灰度方差函数
方差衡量的是图像像素值的离散程度。图像越清晰,像素之间的差异越大,方差就越大。
μ = (1/N) * Σ I(x,y)
F = Σ (I(x,y) - μ)²
这个函数的好处是:不依赖边缘。哪怕是一张纹理很弱的图,只要对焦准确,像素之间的对比度也会提升,方差就会变大。
4.2.2 熵函数
熵来自信息论,衡量的是图像的信息量。熵越大,说明图像包含的信息越丰富,通常意味着更清晰。
p(i) = 灰度值为 i 的像素占比
F = - Σ p(i) * log₂(p(i))
熵函数在纹理丰富的场景下表现很好,但在低光照下有个致命问题:暗光噪声会引入大量虚假的灰度级,导致熵值虚高。你明明没对焦,熵却告诉你“图像很清晰”。
所以,我个人很少在低光照场景下单独用熵函数。它更适合作为辅助判断,比如结合梯度函数一起用。
4.3 各函数对比与选型建议
说了这么多,到底该用哪个?我整理了一张表,方便你对照选择:
| 评价函数 | 计算量 | 低光照鲁棒性 | 对焦峰值锐度 | 适用场景 |
|---|---|---|---|---|
| Brenner | 极低 | 差 | 中等 | 高光照、资源受限平台 |
| Sobel | 低 | 较好 | 中等 | 通用场景,低光照首选 |
| Laplacian | 低 | 差 | 尖锐 | 高 SNR 场景,精度要求高 |
| Tenengrad | 低 | 好 | 中等 | 低光照、边缘丰富场景 |
| 方差 | 极低 | 中等 | 平缓 | 纹理弱、均匀场景 |
| 熵 | 中等 | 差 | 平缓 | 纹理丰富、高光照场景 |
4.4 实战中的避坑指南
最后,分享几个我踩过的坑,你遇到了可以直接绕开:
- 不要只用一种函数——低光照场景变化多端,单一函数很容易失效。至少准备两套方案,根据场景自动切换。
- 注意 ROI 的选择——对焦评价函数只对画面中心区域计算,还是全图计算?我建议用中心权重区域,因为大多数拍摄主体都在画面中央。全图计算容易被背景干扰。
- 预处理不能省——在计算评价函数之前,一定要做降噪处理。否则你算出来的“清晰度”,有一半是噪声贡献的。
- 曲线平滑很重要——原始的评价函数曲线往往有很多毛刺。我习惯加一个滑动窗口平均,或者用卡尔曼滤波做平滑,这样对焦马达才不会来回抖动。