第1章:图像清晰度评价函数(上)
各位同学好,我是你们这门课的老朋友。今天咱们聊聊自动对焦里最基础、也最核心的一块——图像清晰度评价函数。
说白了,自动对焦就是让镜头动一动,找到画面最清楚的那个位置。但问题来了:怎么判断一张图到底清不清楚? 你总不能让算法像人一样看一眼说“嗯,这个锐利”吧?
所以我们需要一个数学工具,把“清晰度”这个主观感受,变成一个可以计算的数值。这就是清晰度评价函数干的事。
我个人习惯把评价函数分成三大类:基于梯度的、基于频域的、基于统计的。今天先讲前两类,第三类留到下节课。
一、基于梯度的评价函数
先问大家一个问题:一张清晰的图像,和一张模糊的图像,最直观的区别在哪?
答案是边缘。清晰的图像边缘锐利,像素值变化剧烈;模糊的图像边缘平滑,像素值变化缓慢。而梯度,就是用来描述这种变化快慢的数学工具。
梯度越大,说明边缘越锐利,图像越清晰。这个逻辑很直接,对吧?
1.1 Brenner 函数
Brenner 是最简单的梯度评价函数之一。它只计算相隔两个像素的灰度差的平方和。
公式长这样:
F = Σ |f(x+2, y) - f(x, y)|²
为什么是相隔两个像素?不是相邻?
嗯,这里有个小门道。我刚开始做对焦算法时也纳闷过。后来在实际项目中调试才发现:相邻像素的差值太小了,容易受噪声干扰。相隔两个像素,既能捕捉到边缘信息,又能滤掉一部分高频噪声。说白了,这是一种简单的带通滤波思想。
1.2 Tenengrad 函数
Tenengrad 比 Brenner 更“正规”一些。它使用 Sobel 算子计算水平和垂直方向的梯度,然后求平方和。
代码实现大概是这样的:
// 水平 Sobel 核
int sobel_x[3][3] = {
{-1, 0, 1},
{-2, 0, 2},
{-1, 0, 1}
};
// 垂直 Sobel 核
int sobel_y[3][3] = {
{-1, -2, -1},
{ 0, 0, 0},
{ 1, 2, 1}
};
// 对每个像素计算梯度幅值
for (int y = 1; y < height-1; y++) {
for (int x = 1; x < width-1; x++) {
int gx = 0, gy = 0;
for (int ky = -1; ky <= 1; ky++) {
for (int kx = -1; kx <= 1; kx++) {
gx += sobel_x[ky+1][kx+1] * img[y+ky][x+kx];
gy += sobel_y[ky+1][kx+1] * img[y+ky][x+kx];
}
}
sum += gx*gx + gy*gy;
}
}
Tenengrad 的优点是方向性更全面,水平和垂直边缘都能捕捉到。我在做手机摄像头对焦时,Tenengrad 是默认选项之一。
1.3 Laplacian 函数
Laplacian 用的是二阶导数。一阶导数看变化快慢,二阶导数看变化加速度。边缘处的一阶导数是极值,二阶导数是过零点。
常用的 Laplacian 核是 3x3 的:
Laplacian 核:
[ 0, -1, 0]
[-1, 4, -1]
[ 0, -1, 0]
或者带对角线的版本:
[ 1, 1, 1]
[ 1, -8, 1]
[ 1, 1, 1]
Laplacian 对孤立点、细线、边缘端点特别敏感。但它的缺点也很明显:对噪声极其敏感。你想想看,二阶导数放大了高频成分,而噪声恰恰也是高频的。
| 函数 | 计算量 | 抗噪性 | 适用场景 |
|---|---|---|---|
| Brenner | 极低 | 中等 | 嵌入式、实时系统 |
| Tenengrad | 中等 | 较好 | 通用场景 |
| Laplacian | 低 | 较差 | 纹理丰富、低噪声场景 |
二、基于频域的评价函数
梯度方法是在空间域里看问题。但换个角度想:清晰图像的高频成分多,模糊图像的高频成分少。这不就是频域分析最擅长的事吗?
频域方法的核心思想:把图像从空间域变换到频率域,然后看高频分量的能量有多大。
2.1 FFT(快速傅里叶变换)
FFT 是频域分析的经典工具。对图像做二维 FFT 后,低频分量集中在中心,高频分量在四周。清晰度越高,高频区域的能量越强。
评价函数可以这样设计:
1. 对图像做二维 FFT,得到频谱 F(u, v)
2. 计算频谱的幅值 |F(u, v)|
3. 设计一个高通滤波器,只保留高频部分
4. 对高频部分的幅值求和,作为清晰度得分
但说实话,FFT 在嵌入式系统里不太实用。为什么?计算量太大了。一张 1080p 的图像,做一次 FFT 要几毫秒甚至几十毫秒,对焦过程中可能要算几十次,这谁顶得住?
我在一个工业相机项目里试过 FFT 做对焦,最后放弃了。不是因为不准,而是因为太慢了。后来换成了 Tenengrad,效果差不多,速度快了一个数量级。
2.2 DCT(离散余弦变换)
DCT 是 FFT 的“近亲”,但更实用。JPEG 压缩用的就是 DCT,原因很简单:DCT 是实数变换,计算量比 FFT 小。
DCT 的评价思路和 FFT 类似:
1. 对图像分块(比如 8x8 或 16x16)
2. 对每个块做 DCT
3. 提取高频系数(比如右下角的系数)
4. 对所有块的高频系数求和
分块的好处是:可以局部评价清晰度。比如画面中心区域清晰、边缘模糊,分块 DCT 能捕捉到这种空间变化。而全局 FFT 只能给出一个整体值。
我记得有一次做车载摄像头对焦,场景里经常有大面积天空(低频区域)。用全局 FFT 时,天空部分的低频能量淹没了边缘的高频信息,导致对焦不准。后来改成 16x16 分块 DCT,只取高频系数最大的几个块做评价,效果立竿见影。
- FFT:复数变换,计算量大,频率分辨率高,适合离线分析
- DCT:实数变换,计算量适中,可分块处理,适合嵌入式实现
- 共同点:都能有效区分清晰和模糊图像,对噪声鲁棒性优于梯度方法
三、实际项目中的选择建议
讲了这么多,你可能会问:那我到底该用哪个?
我的建议是分三步走:
- 先看平台:如果是单片机、低端 DSP,老老实实用 Brenner 或简单的 Laplacian。别想着上 FFT,跑不动的。
- 再看场景:室内灯光稳定、纹理丰富,Tenengrad 就很好。户外光线变化大、有噪声,考虑 DCT 或带预处理的梯度方法。
- 最后看精度:如果对焦精度要求极高(比如显微镜、工业检测),可以考虑组合使用——先用 Brenner 粗调,再用 Tenengrad 精调。
好了,今天的内容就到这里。梯度类和频域类的评价函数,是自动对焦算法的基石。下一节课我们继续聊基于统计的评价函数,以及如何在实际项目中做选择。
记住:没有最好的函数,只有最合适的函数。多动手试试,你会有自己的体会。
公众号:蓝海资料掘金营,微信deep3321