4. 对焦窗口选择

任务目标

在自动对焦系统中,对焦窗口的选择直接影响对焦速度与精度。本案例将画面划分为3×3共9个区域,计算每个区域的对比度值(Tenengrad梯度),并通过加权策略综合评估,选出最佳对焦区域作为后续对焦窗口。实现后可在标准测试图上验证区域选择结果。

核心实现

import numpy as np
import cv2

def compute_tenengrad(gray_img):
    """计算图像的Tenengrad对比度(Sobel梯度平方和)"""
    gx = cv2.Sobel(gray_img, cv2.CV_64F, 1, 0, ksize=3)
    gy = cv2.Sobel(gray_img, cv2.CV_64F, 0, 1, ksize=3)
    return np.sum(gx**2 + gy**2)

def select_focus_region(image, grid=(3, 3), weight_mode='center'):
    """
    多区域对焦窗口选择
    image: 灰度图或BGR图
    grid: (rows, cols) 区域划分
    weight_mode: 'center'中心权重 / 'uniform'均匀权重
    返回: (best_idx, region_scores, weighted_scores)
    """
    if len(image.shape) == 3:
        gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
    else:
        gray = image.copy()
    
    h, w = gray.shape
    rows, cols = grid
    cell_h, cell_w = h // rows, w // cols
    
    # 中心加权矩阵(中心区域权重高)
    if weight_mode == 'center':
        weight_mat = np.array([[0.5, 0.8, 0.5],
                               [0.8, 1.5, 0.8],
                               [0.5, 0.8, 0.5]], dtype=float)
        # 若grid不是3x3则退化为均匀权重
        if (rows, cols) != (3, 3):
            weight_mat = np.ones((rows, cols), dtype=float)
    else:
        weight_mat = np.ones((rows, cols), dtype=float)
    
    region_scores = np.zeros((rows, cols))
    for i in range(rows):
        for j in range(cols):
            y0, y1 = i * cell_h, (i + 1) * cell_h
            x0, x1 = j * cell_w, (j + 1) * cell_w
            region = gray[y0:y1, x0:x1]
            region_scores[i, j] = compute_tenengrad(region)
    
    weighted_scores = region_scores * weight_mat
    best_idx = np.unravel_index(np.argmax(weighted_scores), weighted_scores.shape)
    
    return best_idx, region_scores, weighted_scores

def draw_result(image, best_idx, grid=(3, 3)):
    """在图像上绘制网格与最佳区域标记"""
    vis = image.copy()
    if len(vis.shape) == 2:
        vis = cv2.cvtColor(vis, cv2.COLOR_GRAY2BGR)
    h, w = vis.shape[:2]
    rows, cols = grid
    cell_h, cell_w = h // rows, w // cols
    
    # 绘制网格线
    for i in range(1, rows):
        cv2.line(vis, (0, i * cell_h), (w, i * cell_h), (0, 255, 0), 1)
    for j in range(1, cols):
        cv2.line(vis, (j * cell_w, 0), (j * cell_w, h), (0, 255, 0), 1)
    
    # 高亮最佳区域
    br, bc = best_idx
    y0, y1 = br * cell_h, (br + 1) * cell_h
    x0, x1 = bc * cell_w, (bc + 1) * cell_w
    cv2.rectangle(vis, (x0, y0), (x1, y1), (0, 0, 255), 3)
    cv2.putText(vis, f"Best: ({br},{bc})", (x0 + 5, y0 + 25),
                cv2.FONT_HERSHEY_SIMPLEX, 0.7, (0, 0, 255), 2)
    return vis

if __name__ == "__main__":
    # 生成测试图:中心区域纹理丰富,四周平滑
    test_img = np.full((300, 300), 128, dtype=np.uint8)
    # 中心区域添加随机纹理
    center = test_img[100:200, 100:200]
    noise = np.random.default_rng(42).integers(0, 50, center.shape).astype(np.uint8)
    test_img[100:200, 100:200] = cv2.add(center, noise)
    
    best_idx, scores, weighted = select_focus_region(test_img, grid=(3, 3))
    print(f"最佳区域索引(行,列): {best_idx}")
    print(f"区域对比度:\n{scores.round(1)}")
    print(f"加权后对比度:\n{weighted.round(1)}")
    
    # 保存可视化结果
    result = draw_result(test_img, best_idx)
    cv2.imwrite("focus_region_result.png", result)
    print("可视化结果已保存: focus_region_result.png")

运行与验证

环境依赖:Python 3.8+,NumPy,OpenCV(pip install numpy opencv-python)。

运行方式:

python focus_region_select.py

验证方法:

  1. 程序输出最佳区域索引应为(1, 1),即中心区域——因为测试图中心区域包含随机纹理,Tenengrad梯度值显著高于四周平滑区域。
  2. 加权后中心区域权重最高(1.5),进一步确保中心区域被选中。
  3. 打开生成的focus_region_result.png,应看到红色矩形框标记在图像正中央的3×3网格单元内。

预期输出(基于代码逻辑与给定输入可直接推出):

最佳区域索引(行,列): (1, 1)
区域对比度:
[[0. 0. 0.]
 [0. 高值 0.]
 [0. 0. 0.]]
加权后对比度:
[[0. 0. 0.]
 [0. 更高值 0.]
 [0. 0. 0.]]

其中“高值”为具体数值,中心区域因纹理存在其Tenengrad值远大于四周的0值。

关键说明

  • 权重矩阵采用中心加权策略,模拟相机对焦时优先考虑画面中央区域的实际需求;若需均匀对待各区域,可将weight_mode设为'uniform'。
  • 区域边界处理使用整除划分,当图像尺寸不能被网格整除时,边缘区域会略小,但不影响对比度计算与区域选择逻辑。
  • 实际ISP集成时,可将select_focus_region的输入替换为传感器输出的RAW图转灰度数据,并将返回的best_idx映射为对焦窗口的像素坐标范围。