4、窗口权重分配:均匀权重、中心权重、高斯权重、自适应权重算法
权重分配这事儿,说白了就是决定窗口里每个像素的「话语权」。
你想想看,一个对焦窗口少说也有几十乘几十个像素。如果每个像素都平等投票,那边缘的噪点、低反差的区域,就会跟中心的高频细节抢风头。我早年做手机相机调试时,就吃过这个亏——明明中心有纹理,窗口边缘一片天空,结果对焦马达来回拉风箱,就是锁不住。
所以,权重分配不是数学游戏,它直接决定了你的对焦系统「听谁的」。下面这四种算法,我按从简单到智能的顺序讲。
4.1 均匀权重:最朴素的方案
均匀权重,也叫矩形窗或Box窗。窗口内每个像素的权重都是1/N,N是像素总数。
// 均匀权重示例
float uniform_weight(int x, int y, int win_w, int win_h) {
return 1.0f / (win_w * win_h);
}
优点很明显:计算量极小,适合硬件实现。缺点也很致命:边缘像素和中心像素一样重要。
避坑:我曾经在一个夜景场景里用过均匀权重,结果窗口边缘的暗电流噪声被平等对待,对焦评分曲线出现大量毛刺。后来换成中心权重,曲线立刻平滑了。
什么时候用均匀权重?我个人习惯是:
- 场景纹理分布均匀(比如拍书本、白墙上的文字)
- 窗口尺寸很小(比如16x16以下)
- 硬件资源极度受限(比如低端MCU)
4.2 中心权重:聚焦画面核心
中心权重,顾名思义,越靠近窗口中心的像素权重越大。最常见的实现是线性衰减或二次衰减。
// 中心权重示例(线性衰减)
float center_weight(int x, int y, int cx, int cy, int win_w, int win_h) {
float dx = (float)(x - cx) / (win_w / 2);
float dy = (float)(y - cy) / (win_h / 2);
float dist = sqrtf(dx*dx + dy*dy);
return fmaxf(0.0f, 1.0f - dist); // 线性衰减到0
}
中心权重的核心思想是:用户通常把主体放在画面中央。所以中央像素的「意见」更重要。
| 权重类型 | 中心像素权重 | 边缘像素权重 | 计算开销 |
|---|---|---|---|
| 均匀权重 | 1/N | 1/N | 极低 |
| 线性中心权重 | 1.0 | 0.0~0.3 | 低 |
| 二次中心权重 | 1.0 | 0.0~0.1 | 低 |
经验:我建议中心权重的衰减半径不要设得太小。如果只有中心10%的区域有高权重,一旦主体稍微偏移,对焦就会失败。一般取窗口宽高的30%~50%作为有效半径比较稳妥。
4.3 高斯权重:平滑且自然
高斯权重,就是用二维高斯函数作为权重分布。它比线性衰减更平滑,也更符合光学系统的特性——镜头本身的MTF就是中心高、边缘低。
// 高斯权重示例
float gaussian_weight(int x, int y, int cx, int cy, float sigma) {
float dx = (float)(x - cx);
float dy = (float)(y - cy);
float dist2 = dx*dx + dy*dy;
return expf(-dist2 / (2.0f * sigma * sigma));
}
sigma的取值很关键。sigma太小,权重集中在中心几个像素,浪费了窗口信息;sigma太大,又退化成均匀权重。
核心参数:sigma一般取窗口宽度的1/6到1/4。比如64x64的窗口,sigma取10~16。这样既能保证中心主导,又能让边缘像素贡献少量信息。
我记得有一次调试4K视频的对焦,用线性中心权重时,画面边缘的轻微运动会导致评分抖动。换成高斯权重后,曲线平滑了很多。为什么?因为高斯函数的导数连续,评分曲线不会出现「台阶」。
4.4 自适应权重:动态调整
自适应权重,是让权重分布根据图像内容自己调整。这是目前旗舰手机和高端相机的主流方案。
核心思路:
- 纹理检测:计算每个像素或子块的梯度/方差
- 权重映射:纹理丰富的区域给高权重,平坦区域给低权重
- 归一化:保证所有权重之和为1
// 自适应权重示例(基于局部方差)
void adaptive_weights(uint8_t* img, int w, int h, float* weights) {
// 第一步:计算每个像素的局部方差(3x3窗口)
float* variance = (float*)malloc(w * h * sizeof(float));
for (int y = 1; y < h-1; y++) {
for (int x = 1; x < w-1; x++) {
float mean = 0, var = 0;
for (int dy = -1; dy <= 1; dy++)
for (int dx = -1; dx <= 1; dx++)
mean += img[(y+dy)*w + (x+dx)];
mean /= 9.0f;
for (int dy = -1; dy <= 1; dy++)
for (int dx = -1; dx <= 1; dx++)
var += powf(img[(y+dy)*w + (x+dx)] - mean, 2);
variance[y*w + x] = var / 9.0f;
}
}
// 第二步:方差映射为权重(加一个小的epsilon防止除零)
float eps = 1e-6f;
for (int i = 0; i < w*h; i++)
weights[i] = variance[i] + eps;
// 第三步:归一化
float sum = 0;
for (int i = 0; i < w*h; i++) sum += weights[i];
for (int i = 0; i < w*h; i++) weights[i] /= sum;
free(variance);
}
注意:自适应权重的计算量比前三种大一个数量级。如果每帧都重新计算权重,对实时性要求高的场景(比如60fps视频)可能扛不住。我一般建议:
- 每5~10帧更新一次权重
- 或者只在场景切换时重新计算
- 或者用硬件加速(GPU/DSP)做局部方差计算
4.5 四种算法对比
| 算法 | 计算量 | 抗噪性 | 场景适应性 | 硬件友好度 | 典型应用 |
|---|---|---|---|---|---|
| 均匀权重 | ★☆☆☆☆ | ★☆☆☆☆ | ★☆☆☆☆ | ★★★★★ | 低端摄像头、工业检测 |
| 中心权重 | ★★☆☆☆ | ★★★☆☆ | ★★☆☆☆ | ★★★★☆ | 手机主摄、监控 |
| 高斯权重 | ★★☆☆☆ | ★★★★☆ | ★★☆☆☆ | ★★★☆☆ | 单反、微单、视频 |
| 自适应权重 | ★★★★☆ | ★★★★★ | ★★★★★ | ★★☆☆☆ | 旗舰手机、专业相机 |
嗯,这里要注意:表格里的「抗噪性」一栏,均匀权重得分最低,是因为它把噪声和纹理同等对待。自适应权重得分最高,是因为它天然会忽略平坦区域的噪声。
4.6 知识结构图
下面这张图,把四种权重算法的核心逻辑和适用场景串起来了。
4.7 实战建议
最后,我根据实际项目经验,给几个选型建议:
- 如果你在做入门级产品:用均匀权重就够了,把精力放在窗口位置选择上。
- 如果你在做中端手机:用高斯权重,sigma取窗口宽度的1/5,效果和性能平衡得很好。
- 如果你在做旗舰机:上自适应权重。但记得加一个「保底机制」——当纹理检测失效时(比如全黑或全白场景),回退到高斯权重。
- 如果你在做视频对焦:千万别用均匀权重。我踩过这个坑,画面边缘的微小变化会让评分曲线剧烈抖动,导致对焦频繁误触发。
小技巧:自适应权重可以和中心权重结合。先计算纹理权重图,再乘以一个中心高斯的衰减系数。这样既保留了中心优先的直觉,又能让纹理丰富的边缘区域获得一定话语权。我在一个项目中用这个方案,对焦成功率从87%提升到了96%。