ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

基于MATLAB的11种图像清晰度评价函数实现与对比分析

基于MATLAB的11种图像清晰度评价函数实现与对比分析 做机器视觉这些年和自动对焦、图像质量评估打交道是躲不开的事。不管是工业检测里镜头快速对焦还是显微镜下逐层扫描重建背后都少不了一个核心模块图像清晰度评价函数。这个函数直接决定对焦系统能不能又快又准地找到焦面也决定了批处理图像时能不能自动剔除模糊帧。最近整理项目代码时我把常用的11种清晰度评价函数统一做成了MATLAB版本顺便把原理、实现、实测曲线和踩过的坑一起理了出来今天分享出来给做图像处理、自动对焦、工业视觉的朋友们一个可以直接复用的参考。自动对焦的基本逻辑其实不复杂驱动镜头或载物台在Z轴方向移动每到一个位置采集一帧图像计算一个清晰度评价值评价值最大的位置就是焦点位置。整个过程看似简单真正的难点在于评价函数的选择——不同的函数对图像内容、噪声、光照变化、景深范围的响应完全不同。选错函数对焦曲线可能出现多个峰值或者在大平坦区域完全失效。下面从原理开始把11种函数拆开讲清楚并给出完整的MATLAB实现。1. 评价函数的设计思路与分类框架1.1 清晰图像和模糊图像的本质区别在哪一张图像是否清晰从频域角度看清晰图像包含更多高频成分边缘处灰度变化剧烈失焦相当于低通滤波把高频分量抹掉了图像变得平滑边缘过渡缓慢。因此在评价图像清晰度时最常用的思路就是计算图像灰度变化的剧烈程度包括空域中的梯度强度、灰度方差频域中的高频能量等。这也解释了为什么梯度类评价函数应用最广梯度算子能突出边缘边缘越锐利梯度值越大评价值越高。模糊图像梯度值衰减明显因此能拉开清晰与模糊的差距。归纳起来11种函数大致可分成四类梯度类、统计类、频域类和信息熵类。梯度类占了大半因为这类函数物理意义直观、计算简单、对焦灵敏度高。1.2 评价函数优劣的四个核心指标判断一个评价函数好不好用不能只看能不能区分清晰和模糊还要看下面几个指标。灵敏度是指焦面附近评价值随离焦量变化的剧烈程度灵敏度越高峰值越尖锐对焦精度越高。单峰性是指评价函数在扫描范围内只有一个明显的峰值如果有多个局部极大值搜索算法很容易陷入错误位置。抗噪性是指函数对传感器噪声、光照不均是否敏感噪声会导致评价值抖动严重时产生伪峰值。计算效率则决定了能否满足实时对焦需求对帧率要求高的场景函数必须足够轻量。这四个指标往往互相制约。比如Laplacian算子灵敏度高但对噪声也很敏感方差类函数抗噪尚可但在平坦区域的区分力不足。实际项目里要根据图像内容、对焦平台运动速度、噪声水平综合取舍。2. 11种评价函数原理与MATLAB实现下面的代码统一约定输入为灰度图且先转换为double类型避免uint8做减法时出现负数截断问题。如果输入是三通道彩色图代码里已包含转灰度处理。2.1 Brenner函数最简单高效的梯度阈值法Brenner函数是梯度类函数里最先值得掌握的思路非常直接计算相隔两个像素点的灰度差差值的平方和作为评价值。相隔两个像素而不是相邻像素是为了放宽对微小噪声的敏感度同时保留对边缘强度的响应。function F focus_Brenner(img) img im2double(img); if size(img, 3) 3 img rgb2gray(img); end diffImg img(:, 3:end) - img(:, 1:end-2); F sum(diffImg(:).^2); endBrenner的实现非常轻量单帧百万像素图像在MATLAB里也能跑到毫秒级非常适合嵌入式平台或高速对焦场景。我在实际项目里常用它做粗对焦阶段的评价值计算速度快曲线也比较平滑。它的缺点是抗噪性一般图像纹理过于复杂时评价值波动稍大。2.2 SMD函数灰度差分绝对值之和SMD全称Sum of Modulus of gray Difference计算的是所有相邻像素灰度差的绝对值之和。做法是分别计算水平方向和垂直方向相邻像素的差值把两个方向的绝对值相加。function F focus_SMD(img) img im2double(img); if size(img, 3) 3 img rgb2gray(img); end dx abs(img(:, 1:end-1) - img(:, 2:end)); dy abs(img(1:end-1, :) - img(2:end, :)); F sum(dx(:)) sum(dy(:)); end需要注意这里的双方向累加能捕捉水平边缘和垂直边缘的变化但没有覆盖对角线方向。对于斜向纹理较多的图像SMD的响应会略弱于后面的能量梯度函数。SMD计算量非常低核心代码就两行差分加求和特别适合在FPGA等硬件上做流水线实现。2.3 SMD2函数灰度差分乘积的改进版SMD2是在SMD基础上的改进把水平方向的灰度差分和垂直方向的灰度差分相乘后再累加。边缘处的水平梯度和垂直梯度同时较大时乘积会给一个更突出的响应因此SMD2的灵敏度通常高于SMD。function F focus_SMD2(img) img im2double(img); if size(img, 3) 3 img rgb2gray(img); end dx abs(img(:, 1:end-1) - img(:, 2:end)); dy abs(img(1:end-1, :) - img(2:end, :)); dx dx(1:end-1, :); dy dy(:, 1:end-1); F sum(dx(:) .* dy(:)); end这里有个细节dx和dy的尺寸不完全一样分别是(M, N-1)和(M-1, N)要参与逐元素乘法必须先裁剪成一致尺寸。我在写第一版代码时就因为没对齐尺寸报错过后来干脆直接裁剪到公共部分。SMD2对十字交叉边缘和纹理密集区域响应更明显但对光照梯度变化比较敏感使用时最好在均匀光照条件下评估。2.4 EOG能量梯度平方梯度累加EOG全称Energy of Gradient核心思想是把每个像素的梯度平方后累加。平方运算让大梯度值对最终评价值的贡献以二次方放大从而增强峰值附近的变化幅度对焦曲线会更陡峭。function F focus_EOG(img) img im2double(img); if size(img, 3) 3 img rgb2gray(img); end gx img(:, 2:end) - img(:, 1:end-1); gy img(2:end, :) - img(1:end-1, :); F sum(gx(:).^2) sum(gy(:).^2); endEOG在数字对焦实验中表现一直很稳定它比SMD多了一次平方计算但梯度大区域的权重更明显。个人经验是在传感器噪声较小时推荐优先用EOG它的对焦曲线噪声抑制比SMD更好。缺点是平方运算会放大极少数高亮噪声点的影响如果图像存在异常亮点评价值可能被这些点主导。2.5 Tenengrad函数基于Sobel算子的梯度累加Tenengrad是Sobel边缘检测的一种应用形式。它不是用简单的相邻差分而是用Sobel算子计算水平梯度和垂直梯度然后求平方和。Sobel算子带有平滑加权能够在计算梯度的同时抑制一部分噪声因此Tenengrad的抗噪性普遍优于前几个基于相邻差分的函数。function F focus_Tenengrad(img) img im2double(img); if size(img, 3) 3 img rgb2gray(img); end sobelx [-1 0 1; -2 0 2; -1 0 1]; sobely sobelx; gx imfilter(img, sobelx, replicate); gy imfilter(img, sobely, replicate); F sum(gx(:).^2 gy(:).^2); endimfilter的边界填充方式建议用replicate而不是默认的补零。补零会在图像边缘产生虚假的大梯度特别是当图像内容贴近边界时会显著干扰评价值。用replicate扩展边缘能有效避免这种边界伪响应。Tenengrad计算量适中在工业视觉中很常用我的经验是它对中等噪声图像的鲁棒性比Laplacian好不少。2.6 Laplacian函数二阶微分算子Laplacian算子是二阶微分算子对图像灰度变化的二阶导数进行响应。清晰图像在边缘处灰度变化剧烈二阶导数会出现明显的过零或峰值因此Laplacian输出也能作为清晰度度量。function F focus_Laplacian(img) img im2double(img); if size(img, 3) 3 img rgb2gray(img); end lap [0 -1 0; -1 4 -1; 0 -1 0]; l imfilter(img, lap, replicate); F sum(l(:).^2); end使用3x3拉普拉斯模板时中心系数与周围系数的和为零平坦区域输出接近零边缘区域输出很大。计算评价值时取平方再累加避免正负相互抵消。Laplacian对焦灵敏度很高峰值尖锐但对噪声极其敏感。我测试过含高斯噪声的模糊序列Laplacian评价值曲线会出现明显毛刺必须配合去噪预处理使用。2.7 修正Laplacian只保留水平垂直方向传统Laplacian模板同时包含水平和垂直方向、以及隐含的对角线作用。Nayar提出修正Laplacian只用x方向和y方向的二阶差分绝对值相加减少了对角线方向带来的噪声影响。function F focus_MLAP(img) img im2double(img); if size(img, 3) 3 img rgb2gray(img); end ml abs(-2*img(:, 2:end-1) img(:, 1:end-2) img(:, 3:end)) ... abs(-2*img(2:end-1, :) img(1:end-2, :) img(3:end, :)); F sum(ml(:)); end实现里其实就是计算x方向二阶差分并取绝对值再加上y方向二阶差分绝对值。相比直接使用Laplacian模板MLAP没有平方运算计算量更小对噪声的放大效应也弱一些。这个函数在实时对焦系统里出现频率很高尤其是对焦搜索需要快速反馈的场景。2.8 Roberts函数对角线梯度算子Roberts算子利用两条对角线方向上的像素差来估计梯度计算量非常小在早期数字图像处理中应用广泛。function F focus_Roberts(img) img im2double(img); if size(img, 3) 3 img rgb2gray(img); end g1 img(1:end-1, 1:end-1) - img(2:end, 2:end); g2 img(2:end, 1:end-1) - img(1:end-1, 2:end); F sum(g1(:).^2 g2(:).^2); endRoberts算子窗口只有2x2响应速度极快但对噪声的抑制能力很弱。它主要适合对速度要求极高、图像噪声很小的场景比如部分高速工业检测线中的粗略对焦参考。如果图像噪声明显建议至少使用3x3窗口的Sobel或Tenengrad。2.9 方差函数整体灰度离散程度图像模糊等效于低通滤波会降低灰度的动态范围让像素灰度值向均值靠拢。方差描述的是图像灰度相对于均值的离散程度清晰图像通常对比度更高、灰度分布更分散因此方差可以作为清晰度的统计度量。function F focus_Variance(img) img im2double(img); if size(img, 3) 3 img rgb2gray(img); end mu mean(img(:)); F sum((img(:) - mu).^2); end方差函数的优点是实现简单对光照缓慢变化不敏感。但它的灵敏度通常低于梯度类函数尤其在纹理稀疏的平坦区域清晰和模糊的方差差异可能很小。不过在某些特定场景里比如纯文本文档扫描的清晰度评价方差函数表现意外地好因为文本图像的边缘占比高模糊后对比度下降明显。2.10 信息熵函数基于灰度分布的不确定性信息熵衡量图像灰度分布的不确定性。一般认为清晰图像包含更多细节灰度分布更均匀熵值更高模糊图像灰度分布相对集中熵值较低。但实际使用中这个规律并不总是成立。function F focus_Entropy(img) img im2double(img); if size(img, 3) 3 img rgb2gray(img); end counts imhist(img, 256); p counts / sum(counts); p(p 0) []; F -sum(p .* log2(p)); end实现时要把概率为零的灰度级剔除否则log2(0)会产生无穷大导致结果无效。这是我写这个函数时踩过的坑。信息熵函数在图像模糊到一定程度后熵值变化会趋于平缓灵敏度不足因此一般不作为自动对焦的主评价函数。它更适合做图像质量评估的辅助指标或者作为多特征融合的一部分。2.11 频域高频能量函数基于FFT的清晰度度量频域方法的思路是在傅里叶变换后统计高频分量的能量。图像越清晰高频成分越丰富高频能量越大。实现时通常用高通掩膜把低频部分滤除只保留高频分量。function F focus_FFTEnergy(img) img im2double(img); if size(img, 3) 3 img rgb2gray(img); end FImage fftshift(fft2(img)); amp abs(FImage); [M, N] size(amp); cx floor(M / 2) 1; cy floor(N / 2) 1; radius round(min(M, N) / 8); [X, Y] meshgrid(1:N, 1:M); mask (X - cy).^2 (Y - cx).^2 radius^2; F sum(amp(mask).^2); end这里的半径阈值需要根据图像尺寸和实际内容调整。半径设得太小会把大量低频成分纳入统计导致清晰与模糊区分度下降设得太大会引入过多噪声。我的经验是先统计一组正常图像的频谱分布找一个能保留大部分有效边缘频率的半径再固定使用。FFT方法的计算量明显高于空域方法因此在实时对焦中并不常用更多用于离线的图像质量评估和分析。3. 实测对比同一组模糊序列下的表现3.1 模拟失焦图像序列的生成要对比11种函数的表现我用一张标准测试图生成了一组模拟离焦序列。做法是对同一张清晰图像做不同程度的高斯模糊模拟镜头离焦时的高斯型点扩散函数。模糊程度用高斯核的sigma控制sigma越大图像越模糊。img imread(cameraman.tif); img im2double(img); for k 1:10 sigma 0.5 * k; blurImg imgaussfilt(img, sigma); F_brenner(k) focus_Brenner(blurImg); F_tenengrad(k) focus_Tenengrad(blurImg); F_entropy(k) focus_Entropy(blurImg); % 其余函数同理 end然后把每个函数的输出做归一化处理即每个值除以该序列的最大值让所有函数的曲线落在[0, 1]区间内方便对比。为了模拟传感器噪声我还额外加了一组方差为0.001的高斯白噪声测试。3.2 对焦曲线的灵敏度对比在无噪声条件下梯度类函数的优势非常明显。EOG和Tenengrad的曲线随sigma增大迅速下降在清晰图像处的评价值与模糊图像处的评价值差距可达10倍以上对焦灵敏度最高。Brenner和SMD次之曲线下降趋势也很明显。Laplacian对焦峰最尖锐但曲线尾部波动大。方差函数的下降速度中等在中等模糊程度以后区分力下降。信息熵函数在sigma较小时区分力弱只有在模糊很严重时才有比较明显的变化。加入噪声后结论发生变化。Laplacian和Roberts的曲线出现明显抖动峰值位置可能偏移对焦稳定性变差。方差函数和熵函数对噪声响应较强烈因为噪声本身会引入额外的灰度离散度和熵值。Tenengrad、EOG和MLAP在噪声下的曲线相对平滑抗噪性更好。3.3 计算耗时测试结果我用一张512x512的灰度图在MATLAB里对每个函数跑100次求平均耗时得到大致的结果函数平均耗时(ms)相对排序Brenner约2.5快SMD约2.2快SMD2约2.6快EOG约2.5快Roberts约2.0最快Tenengrad约8.5中Laplacian约7.8中MLAP约3.5较快方差约1.5最快信息熵约4.5中FFT能量约38慢这个耗时和具体机器配置有关但相对排序有参考意义。方差函数因为只做一次均值计算和一次平方累加速度自然最快。FFT方法由于包含二维傅里叶变换慢一个量级实时场景慎用。Tenengrad因为有两次imfilter卷积耗时比简单差分类函数稍高但换来的是抗噪性能提升。3.4 不同场景下的选型建议结合测试结果我一般按场景选函数。工业检测中图像质量稳定、噪声可控优先用EOG或Tenengrad对焦精度和稳定性均衡。高速对焦或者嵌入式平台优先考虑Roberts、方差或Brenner计算量足够小。强噪声环境下梯度差分类的SMD和Laplacian会吃亏Tenengrad仍然是稳妥选择。纯离线图像质量评估可以用信息熵或FFT高频能量作为辅助指标但不要单独作为主判据。4. 常见问题与排查技巧实录4.1 对焦曲线出现多个峰值怎么处理多峰问题在自动对焦里很常见尤其是图像中存在周期性纹理、栅格或者重复图案时。评价函数可能出现多个相近的局部极大值导致对焦搜索算法选错峰。我的做法是对评价值序列做一定程度的平滑处理例如用移动平均消除小抖动保留整体趋势。更实际的方法是采用粗对焦加精对焦策略先用大步长寻找峰值的大致区域再在小范围内用细步长精确定位同时配合评价函数的阈值判断避免在局部小峰处过早收敛。4.2 图像噪声导致评价值毛刺噪声会让梯度值随机增大反映在评价值曲线上就是毛刺。解决思路有两个方向一是从评价函数本身入手尽量选择带平滑作用的算子比如Sobel卷积而非直接差分二是在计算前对图像做轻度高斯预滤波。但要注意预滤波不能过强否则会削弱高频信息导致灵敏度下降。我常用的组合是轻度高斯滤波加Tenengrad在信噪比偏低的场景下效果比较理想。4.3 平坦区域所有函数都失效当视场内大部分是纯色背景比如白纸、平面零件表面所有评价函数计算出来的值都接近零对焦搜索会失去方向。解决办法是在视场中选定一个包含丰富纹理的ROI窗口只在这个窗口内计算评价值。如果整幅图像实在没有纹理那就要考虑在硬件上增加辅助对焦结构比如投影条纹图案或者切换对焦算法。软件层面还可以用边缘点密度作为兜底统计灰度梯度超过阈值的像素占比避免完全失效。4.4 uint8溢出和边界效应MATLAB里uint8图像在做减法时如果结果为负会被截断为0导致差分信息丢失。所以我强调所有代码第一步都用im2double转成浮点。另一个容易被忽略的问题是imfilter的边界填充方式默认补零会让边界出现虚假梯度建议统一用replicate。如果对边界本身不信任也可以计算完滤波后把边缘几行几列裁掉再累加。4.5 评价函数无法比较不同图像的清晰度不同图像的亮度、对比度、纹理分布不同同名函数计算出的绝对值没有横向可比性。做批处理图像筛选时不要直接比较不同图像的评价值而是要用相对变化或者归一化后再比较。同一场景下改变光照时最好先做光照归一化比如除以图像的灰度均值再计算评价函数。4.6 MATLAB代码性能优化方向MATLAB里评价函数最慢的环节往往是多层循环。写这类函数时能用矩阵运算就不要用循环上述代码全部基于向量化操作。如果追求更高速度可以尝试在函数体内临时清空部分变量以释放内存或使用C-MEX混合编译。在连续图像流场景下还可以预先分配结果数组避免动态扩展。FFT类函数如果耗时过高可以考虑只选取图像中心区域做变换损失一部分精度但换来倍数级速度提升。最后分享一个我个人的体会11种评价函数没有绝对的优劣关键看你的应用约束。自己做产品选型时不要只盯着实验室里的无噪声测试数据一定要把真实场景的噪声、光照变化、运动模糊都加进去重新评估。我的习惯是固定三四个候选函数用真实相机采集一组实际对焦序列分别画出评价曲线做对比再结合速度测试最终定方案。这套流程多跑几次之后你对函数特性的理解会比任何教程都深刻。这些MATLAB代码按函数名拆分保存每个文件就是一个独立函数直接放在工作目录下即可调用方便后续扩展和替换。
返回列表