038、自动对焦AF技术对比:反差对焦CDAF、相位对焦PDAF与双像素对焦的工程实现
一、一个让我熬夜三天的对焦问题
2019年某旗舰机项目,客户反馈暗光下拍照经常“拉风箱”——镜头来回伸缩就是合不上焦。我拿着样机在实验室反复测试,发现一个诡异现象:在低照度场景下,PDAF(相位对焦)检测到的相位差数值跳动剧烈,从+20跳到-15再跳到+8,导致马达像无头苍蝇一样乱窜。更麻烦的是,当场景中有LED灯或荧光灯时,这种跳动会加剧——因为50Hz/60Hz的频闪干扰了相位检测像素的积分时间。
这个案例让我意识到,很多工程师对AF的理解停留在“知道三种对焦方式的名字”,但真正遇到工程问题时,往往不知道如何权衡和调试。今天这篇笔记,我就从底层原理到工程实现,把CDAF、PDAF和双像素对焦(Dual Pixel AF)的坑和技巧全盘托出。
二、反差对焦CDAF:最笨但最稳的“爬山算法”
CDAF的原理简单到让人想笑:镜头移动过程中,计算每一帧图像的对比度(通常用Sobel算子或拉普拉斯算子提取高频分量),找到对比度最大的位置就是合焦点。这就像爬山——你闭着眼睛摸山顶,每走一步摸一下坡度,坡度最大时就是山顶。
工程实现上的坑:
搜索策略不能太死板。很多新手直接写“全局搜索”,从微距到无穷远扫一遍,这在手机模组上至少需要2-3秒,用户早骂娘了。实际工程中用的是“粗搜+细搜”两段式:先大步长找到对比度峰值的大致区域,再小步长精确定位。这里踩过坑——粗搜的步长如果超过景深范围,会直接跳过峰值,导致对焦失败。我的经验是:步长取景深范围的1/3,比如景深是100个马达步,步长设30步。
对比度计算的窗口选择。别傻乎乎用全图计算,计算量大不说,背景的纹理还会干扰对焦。正确做法是:在画面中心区域(通常取10%-20%面积)开一个矩形窗口,但要注意——如果用户把主体放在画面边缘(比如三分法构图),中心窗口就废了。所以高端方案会做多窗口加权,或者结合人脸检测动态调整窗口位置。别这样写死窗口坐标,要留接口给上层算法调整。
低照度下的信噪比问题。暗光下图像噪声大,对比度曲线会变得“毛刺”很多,峰值不明显。我常用的技巧是:对对比度值做滑动平均滤波(窗口大小3-5帧),或者用高斯滤波平滑曲线。但滤波太强会引入延迟,导致对焦慢——这里需要根据场景动态调整滤波强度,比如照度低于10lux时加大滤波,高于100lux时减小。
三、相位对焦PDAF:又快又准,但暗光下会“瞎”
PDAF的原理是在sensor上放置一些“相位检测像素”——这些像素被遮挡了一半(左半或右半),形成一对“左眼”和“右眼”像素。当镜头合焦时,左右像素的亮度一致;离焦时,左右像素的亮度存在相位差,这个差值正比于离焦量。马达根据相位差直接驱动到合焦位置,一步到位,不需要像CDAF那样来回搜索。
工程实现上的坑:
相位检测像素的排布方式。早期方案是在sensor上稀疏地放置几组相位像素(比如每16x16像素中放一组),这导致一个问题:相位像素的密度太低,遇到纹理稀疏的区域(比如白墙、天空),相位差计算会失败。我调试过一款sensor,在纯色场景下相位差输出全是0,AF直接“失明”。解决方案是:当相位差置信度低于阈值时,自动回退到CDAF模式。这个切换逻辑要平滑,否则用户会看到镜头突然“抽搐”一下。
相位差与马达步数的映射关系。相位差是一个无量纲数值,需要转换成马达的驱动步数。这个映射不是线性的,因为镜头的光学特性(像差、畸变)会影响相位差-离焦量的关系。我踩过的坑是:直接用线性映射,结果在微距端对焦不准。正确做法是:在产线标定阶段,采集不同物距下的相位差-马达步数对应表,然后用多项式拟合(通常3阶就够了)。别用高阶拟合,过拟合会导致边缘场景崩溃。
暗光下的相位差噪声。回到开篇那个案例,暗光下相位差跳动是因为:相位检测像素的尺寸通常比普通像素小(为了留出遮挡区域),导致灵敏度下降,信噪比恶化。我当时的解决方案是:在低照度下,对相位差做时间域滤波(比如卡尔曼滤波),同时增加相位差计算的积分时间(从1帧增加到2-3帧)。但积分时间增加会降低帧率,影响预览流畅度——这里需要权衡,我最终的做法是:照度低于20lux时,帧率从30fps降到15fps,换取稳定的相位差输出。
四、双像素对焦Dual Pixel AF:全像素相位检测的“降维打击”
双像素对焦是PDAF的终极进化版——每个像素都被分成左右两个子像素(或者上下),相当于整个sensor都是相位检测像素。这意味着:无论画面中哪个区域,都能计算相位差,而且精度更高(因为每个像素都参与)。索尼的IMX260(三星S7用)是第一个大规模商用的双像素sensor,后来几乎所有旗舰机都跟进了。
工程实现上的坑:
双像素的读出带宽压力。每个像素要读出左右两个子像素的数据,数据量翻倍,对MIPI接口的带宽要求极高。我调试过一款双像素sensor,在4K 60fps模式下,MIPI带宽直接爆了,导致画面出现撕裂。解决方案是:在sensor内部做binning(合并子像素),或者只在高分辨率拍照模式下启用双像素,预览模式用普通模式。别这样写死——要根据场景动态切换,比如用户打开“专业模式”时强制启用双像素。
左右子像素的增益匹配。由于工艺偏差,左右子像素的灵敏度可能不一致,导致即使合焦时,左右子像素的亮度也不相等,产生“伪相位差”。这需要在产线做标定:对均匀光源拍摄,计算每个像素的左右子像素增益比,然后写入sensor的OTP(一次性可编程)寄存器。我遇到过一批sensor,OTP数据写错了,导致整批手机对焦不准——后来加了产线校验环节,标定后立即验证对焦精度。
双像素与HDR的冲突。很多sensor支持HDR模式(比如交错式HDR:长曝光帧和短曝光帧交替输出),但双像素的左右子像素读出时序和HDR的帧时序可能冲突。我踩过的坑是:在HDR模式下启用双像素,结果相位差计算用了长曝光帧的左子像素和短曝光帧的右子像素,时间不对齐,相位差完全错误。解决方案是:在HDR模式下,强制双像素只使用同一帧的左右子像素,或者干脆禁用双像素,回退到CDAF。
五、三种对焦方式的工程选择与混合策略
在实际产品中,没有哪个方案是完美的,都是混合使用:
PDAF主导,CDAF兜底:这是最常见的方案。PDAF负责快速粗调(比如从无穷远调到目标位置附近),然后CDAF做精调(微调几个步长找到对比度峰值)。这里有个细节:PDAF的粗调精度要保证落在CDAF的搜索范围内,否则CDAF找不到峰值。我一般要求PDAF的粗调误差小于CDAF搜索范围的1/2。
双像素+深度学习:这是目前旗舰机的方向。双像素提供密集的相位差信息,然后输入一个轻量级神经网络(比如MobileNetV2的变体),预测最佳对焦位置。这个方案的好处是:可以处理复杂场景(比如透过玻璃、有雾气的场景),但坏处是:模型训练需要大量数据,而且推理延迟要控制在5ms以内(否则影响预览帧率)。我调试过一个方案,模型在GPU上跑没问题,但移植到DSP上时,量化精度不够,导致对焦精度下降——后来改用混合精度量化(部分层用int8,部分层用float16)才解决。
激光/ToF辅助:在极暗光下(<1lux),PDAF和双像素都失效,这时候需要激光测距或ToF传感器。但激光的缺点是:只能测中心点,而且遇到玻璃会反射错误。我见过一个方案:激光测距+双像素融合,激光给出粗距离,双像素做精调,效果不错,但成本高。
六、个人经验性建议
别迷信“全像素对焦”。双像素虽然好,但sensor的读出带宽和功耗是硬约束。如果产品定位中端,用稀疏PDAF+CDAF混合就足够了,把省下的成本用在更好的镜头上,效果可能更好。
产线标定是AF的灵魂。无论哪种方案,标定不好都是白搭。我见过太多项目,算法写得天花乱坠,但产线标定环节偷工减料——比如只标了5个物距点,结果在中间物距段对焦不准。我的建议是:至少标定20个物距点(从5cm到无穷远均匀分布),然后用分段线性插值,比多项式拟合更鲁棒。
调试AF时,先看“置信度”。很多工程师一上来就看相位差数值,但忽略了置信度。置信度低时(比如纹理稀疏、光照不足),强行使用相位差只会让情况更糟。我的调试习惯是:在log中同时打印相位差和置信度,当置信度低于0.3时,自动切换到CDAF。
用户场景比实验室数据重要。实验室里用标准灯箱测试,AF可能完美无缺,但到了用户手里——拍LED屏幕、拍玻璃反光、拍快速移动的宠物——各种问题就冒出来了。我的经验是:在调试阶段,每天花1小时拿着样机去真实场景(地铁、商场、公园)拍照,记录AF失败的case,然后针对性优化。
最后,别忘记“手感”。AF的速度和精度固然重要,但用户感知到的“对焦手感”同样关键。比如:对焦过程中镜头移动要平滑,不能有“咔哒”声(马达的步进声);合焦后要有轻微的“锁定感”(可以通过马达的阻尼控制实现)。这些细节,往往决定了用户是否愿意为你的产品买单。