017、PDAF 像素技术深度解析:遮罩式、双PD式对焦像素的读出时序与数据处理
一、从一次“对焦抽搐”的现场说起
去年Q3,我接手一个旗舰机项目,Sensor是IMX7xx系列,双PD像素,客户反馈暗光下对焦“像癫痫发作”——画面忽远忽近,偶尔还卡在某个位置不动。我第一反应是AF算法参数没调好,但翻遍log发现PD值波动异常大,相邻帧的相位差能差出3~4个像素。更诡异的是,在均匀光照的灰卡下,PD值居然也有±1.5像素的抖动。
当时团队里有人怀疑是Sensor坏了,有人说是LSC校正没做。我让FAE抓了raw图,逐行看PD像素的读出值,才发现问题:双PD像素的左右半区读出时序有错位,导致相位差计算时混入了行间噪声。这个坑,后来在遮罩式PDAF上也遇到过,只是表现形式不同。
二、遮罩式PDAF:老派但依然能打的方案
遮罩式PDAF,说白了就是在普通像素上盖个“半边帽子”——一半像素遮左,一半像素遮右。读出时,这些遮罩像素和普通像素混在一起,通过MIPI接口一股脑儿传给ISP。
读出时序的“暗坑”
遮罩像素的读出顺序,取决于Sensor的Hblank和Vblank设计。常见做法是:每一行里,遮罩像素和普通像素按固定pattern排列,比如每4个普通像素插1个遮罩像素。读出时,Sensor的ADC会按行顺序扫描,遮罩像素的积分时间与普通像素完全同步——这里有个容易被忽略的点:遮罩像素因为遮光,光电转换效率低,实际信号强度只有普通像素的30%~50%,但读出时间窗口是一样的。
我踩过的一个坑:某款Sensor的遮罩像素在读出时,ADC的增益通道和普通像素共用,但遮罩像素的暗电流特性不同,导致读出值里混入了固定模式噪声(FPN)。当时调试时发现,遮罩像素的左右差值在暗场下居然有0.5%的偏移,后来在ISP里加了逐像素的遮罩校正表才搞定。
数据处理:从raw到相位差
遮罩式PDAF的数据处理,核心是“配对”。假设Sensor输出raw图,我们需要找到同一行里相邻的左遮罩像素和右遮罩像素,计算它们的亮度差值。这个差值就是相位差的基础。
伪代码示意(别这样写,实际要处理边界和坏点):
// 假设raw图是Bayer格式,遮罩像素标记为mask_left和mask_right// 这里踩过坑:遮罩像素的坐标偏移量因Sensor而异,别硬编码for(introw=0;row<height;row++){for(intcol=0;col<width;col++){if(is_mask_left(row,col)){// 找到同一行最近的右遮罩像素intright_col=find_next_mask_right(row,col);if(right_col!=-1){intphase=raw[row][right_col]-raw[row][col];// 这里要除以增益差,遮罩像素的增益通常比普通像素高1~2倍phase=phase*gain_ratio;// 存入相位图phase_map[row][col]=phase;}}}}实际工程中,遮罩像素的密度决定了相位图的分辨率。比如每4x4像素里放1个遮罩像素,那么相位图的分辨率就是raw图的1/16。这个分辨率直接影响AF的精度——分辨率太低,对焦会“跳步”。
三、双PD式PDAF:每个像素都是半个侦探
双PD像素,也叫2x1 OCL(片上透镜),每个像素内部被分成左右两个光电二极管,共享一个微透镜。读出时,左右半区可以独立读出,也可以合并成一个普通像素。
读出时序的“双通道陷阱”
双PD像素的读出,通常有两种模式:
- 独立模式:左右半区分别读出,输出两倍于普通像素的数据量。比如2M的双PD像素,独立模式输出4M数据。
- 合并模式:左右半区在Sensor内部合并,输出2M数据,但保留相位信息(通过差分信号)。
我遇到的那个“对焦抽搐”问题,就是出在独立模式下的读出时序。Sensor的ADC在读取左右半区时,存在一个微小的行间延迟——左半区读完后,右半区要等一个像素时钟周期才开始读。这个延迟在高速读出时(比如60fps)会被放大,导致左右半区的积分时间差了约0.5个像素时钟。对于暗光场景,这个时间差足以让相位差产生1~2个像素的偏移。
解决方案是在ISP里做“时序补偿”:根据Sensor的读出速率和行数,计算每个像素的延迟量,然后在相位差计算时减去这个偏移。这个补偿值需要随帧率、曝光时间动态调整,否则高帧率下会过补偿。
数据处理:差分信号的“去噪艺术”
双PD像素的相位差计算,比遮罩式简单——左右半区是同一个像素内的两个子像素,不需要跨像素配对。但简单不代表容易,噪声问题更突出。
// 双PD像素的相位差计算// 注意:左右半区的增益必须一致,否则相位差会偏// 这里踩过坑:某些Sensor的左右半区增益通道独立,需要校准for(introw=0;row<height;row++){for(intcol=0;col<width;col++){// 假设raw图里,左右半区交替存储uint16_tleft=raw[row][col*2];uint16_tright=raw[row][col*2+1];// 差分信号intdiff=left-right;// 归一化:除以总和,消除光照影响floatphase=(float)diff/(left+right+1);// 这里要加一个阈值,防止除零和噪声放大if(left+right<NOISE_THRESHOLD){phase=0;// 暗场下直接置零,别算}phase_map[row][col]=phase;}}这个归一化操作,看似简单,但暗场下left+right接近0时,phase会剧烈抖动。我见过有人用除法后直接取整,结果暗场下相位图全是雪花点。正确的做法是:先做降噪滤波,再归一化,最后对低信噪比区域做掩膜处理。
四、两种方案的对比与选型建议
遮罩式PDAF的优势在于成本低,普通Sensor加个mask就能实现,但相位图分辨率低,且遮罩像素的FPN校正麻烦。双PD像素的相位图分辨率高(每个像素都有相位信息),但读出数据量大,对ISP带宽和时序要求高。
从调试经验看:
- 如果Sensor是1/2.3英寸以下的小底,遮罩式PDAF够用,但要做好遮罩像素的暗电流校正。
- 如果是1/1.7英寸以上的大底,双PD像素是主流,但必须关注读出时序的抖动,尤其是高帧率场景。
- 还有一种混合方案:部分像素用双PD,部分用遮罩,这种方案在华为Mate系列上见过,但调试复杂度翻倍,不推荐新手尝试。
五、个人经验性建议
别迷信Sensor的datasheet:上面写的读出时序参数,通常是理想值。实际量产时,不同批次Sensor的ADC延迟可能差10%~20%。一定要在产线上做时序校准,用灰卡拍100帧,统计相位差的均值与方差,超出阈值就调整补偿参数。
相位差计算前,先做坏点校正:双PD像素的左右半区,如果有一个是坏点,算出来的相位差会直接带偏AF。我习惯在ISP里加一个“双PD坏点检测”模块:如果左右半区的差值超过正常范围(比如3倍标准差),就把这个像素标记为无效,不参与AF计算。
暗光下的相位差,宁可不算,也别乱算:当信噪比低于某个阈值(比如SNR<10dB),直接让AF切换到对比度检测模式。强行用PDAF只会让对焦抽搐,这是我在项目里用血泪换来的教训。
调试时,一定要看相位图的直方图:正常场景下,相位差应该集中在0附近,呈高斯分布。如果直方图出现双峰或偏态,说明读出时序或校正参数有问题。这个习惯帮我快速定位了至少3个Sensor的时序bug。
最后,别把PDAF当成万能药:它只解决“对焦方向”和“对焦距离”的粗估计,精细对焦还得靠对比度检测。好的AF算法,是PDAF和CDAF的混合体,PDAF负责“快”,CDAF负责“准”。
写到这里,想起那个“对焦抽搐”的项目,最后发现是Sensor的MIPI时钟抖动导致左右半区读出错位。换了晶振后,问题消失。有时候,问题不在算法,而在硬件。做影像系统,得懂点硬件,不然被坑了都不知道怎么死的。