1. 自动对焦基础
任务目标
实现一个基于对比度检测的自动对焦算法:输入一组在不同镜头位置采集的图像序列,算法通过计算每帧图像的梯度幅值(Tenengrad算子)作为清晰度评价指标,搜索最大值对应的镜头位置,即为最佳对焦位置。
核心实现
import numpy as np
from scipy import ndimage
def tenengrad_focus_measure(image_gray):
"""计算单帧灰度图像的Tenengrad清晰度评价值(梯度幅值平方和)"""
# Sobel梯度算子
sobel_x = np.array([[-1, 0, 1],
[-2, 0, 2],
[-1, 0, 1]], dtype=np.float32)
sobel_y = sobel_x.T
gx = ndimage.convolve(image_gray.astype(np.float32), sobel_x)
gy = ndimage.convolve(image_gray.astype(np.float32), sobel_y)
# 梯度幅值平方和作为清晰度分数
magnitude_sq = gx**2 + gy**2
return float(magnitude_sq.sum())
def auto_focus_by_contrast(image_sequence):
"""
基于对比度检测的自动对焦主函数
参数:
image_sequence: list[np.ndarray] 或 np.ndarray
形状为 (N, H, W) 的灰度图像序列,N为镜头位置采样数,
索引顺序对应镜头从近到远(或从远到近)的移动方向
返回:
best_index: int 最佳对焦位置索引(0-based)
scores: np.ndarray 每个位置的清晰度评分
"""
if isinstance(image_sequence, list):
image_sequence = np.stack(image_sequence)
n_frames = image_sequence.shape[0]
scores = np.zeros(n_frames, dtype=np.float64)
for i in range(n_frames):
scores[i] = tenengrad_focus_measure(image_sequence[i])
# 搜索评分最大值对应的镜头位置
best_index = int(np.argmax(scores))
return best_index, scores
# ===== 最小可运行示例 =====
if __name__ == "__main__":
# 构造模拟图像序列:用高斯模糊模拟不同离焦程度
# 中心位置(索引5)最清晰,两侧逐渐模糊
rng = np.random.default_rng(42)
sharp_img = rng.integers(0, 255, (64, 64)).astype(np.uint8)
# 生成不同模糊程度的图像序列(模拟镜头移动)
sigma_values = [3.0, 2.2, 1.5, 0.8, 0.3, 0.0, 0.3, 0.8, 1.5, 2.2, 3.0]
sequence = []
for sigma in sigma_values:
if sigma == 0.0:
sequence.append(sharp_img.copy())
else:
sequence.append(ndimage.gaussian_filter(sharp_img, sigma=sigma).astype(np.uint8))
best_idx, scores = auto_focus_by_contrast(sequence)
print(f"模拟序列长度: {len(sequence)}")
print(f"各位置清晰度评分: {scores}")
print(f"算法选出的最佳对焦位置索引: {best_idx}")
print(f"预期最佳位置索引: 5(最清晰处)")
print(f"判定结果: {'通过' if best_idx == 5 else '失败'}")
运行与验证
依赖安装:
pip install numpy scipy
运行方式:直接执行脚本即可:
python auto_focus_contrast.py
验证方法:程序构造了一个已知最佳对焦位置为索引5的模拟图像序列(中心位置无模糊,两侧高斯模糊程度递增)。运行后观察输出:
- 清晰度评分曲线应在索引5处出现明显峰值;
- 程序输出“判定结果: 通过”表示算法正确找到了最佳对焦位置。
若使用真实相机图像序列,只需将灰度帧按镜头移动顺序放入列表传入auto_focus_by_contrast(),返回的best_index即为最佳镜头位置。
关键说明
- Tenengrad算子使用Sobel核计算水平和垂直梯度,梯度幅值平方和越大表示图像边缘越锐利,即对焦越准确;
- 本实现采用全局搜索策略,遍历所有采样位置取最大值,适用于离焦搜索范围已知的场景;
- 实际镜头控制中,可将
best_index映射为步进电机位置指令,驱动镜头移动到该位置完成对焦。