023、RAW域处理:BLC、DPC与LSC的算法原理与调优技巧 📅 2026/7/15 14:30:40 023、RAW域处理BLC、DPC与LSC的算法原理与调优技巧一、从一次产线“翻车”说起去年夏天某款旗舰机型的产线突然爆出问题同一批次模组在暗场下拍出的照片左上角明显偏绿右下角偏紫。产线工程师查了三天怀疑是LSC校准文件出了问题重新烧录了十几次问题依旧。我赶到现场第一件事不是看LSC而是先抓了一帧RAW图用工具扫了一遍BLC值——好家伙四个通道的BLC偏差超过8个DN暗电流补偿根本没对齐。这就是典型的“BLC没调好LSC背锅”的案例。影像系统里RAW域处理是“地基”。BLC、DPC、LSC这三个模块看似基础但任何一个调不好后面ISP的AWB、CCM、Gamma全都会跟着跑偏。今天这篇笔记就聊聊这三个“老伙计”的原理和那些年踩过的坑。二、BLCBlack Level Correction暗电流的“清零”艺术原理一句话传感器在无光条件下像素依然会产生微弱电流这个“暗电流”导致输出值不为零。BLC就是把这个底数减掉让纯黑场景的像素值归零。算法实现最简单的BLC是全局减一个固定值// 别这样写全局减固定值会出大问题uint16_tblc_value64;for(inti0;iframe_size;i){raw_data[i](raw_data[i]blc_value)?(raw_data[i]-blc_value):0;}这里踩过坑不同通道的暗电流不一样R、Gr、Gb、B四个通道的BLC值可能差3-5个DN。更坑的是同一通道在不同行、不同列也有差异——这叫“行噪声”和“列噪声”。所以工业级的BLC必须做“通道独立行列补偿”。实战中我常用的方案是“OBOptical Black区域动态计算”。传感器通常有被遮挡的像素行/列这些像素的均值就是当前帧的暗电流水平。但要注意OB区域本身也有噪声直接取均值会引入随机抖动。我的做法是取中位数或者做3sigma剔除后再取均值。// 推荐写法动态计算OB区域通道独立uint16_tblc_rcompute_ob_median(ob_region_r);// 这里用中位数别用均值uint16_tblc_grcompute_ob_median(ob_region_gr);uint16_tblc_gbcompute_ob_median(ob_region_gb);uint16_tblc_bcompute_ob_median(ob_region_b);// 逐像素处理注意溢出保护for(inty0;yheight;y){for(intx0;xwidth;x){uint16_tvalraw_data[y*stridex];uint16_tblcget_blc_for_channel(x,y);// 根据Bayer pattern选择对应通道的BLCraw_data[y*stridex](valblc)?(val-blc):0;}}调优技巧温度补偿暗电流随温度指数级增长。25°C和60°C下BLC值可能差20个DN。我习惯在模组出厂时做-10°C到60°C的标定存一个温度-BLC查找表运行时根据sensor温度实时插值。增益联动模拟增益和数字增益放大暗电流BLC值要跟着增益线性缩放。这个很多人会忘——增益变大后BLC没跟着调暗场直接偏色。产线校准产线上用暗箱拍100帧取每帧的OB均值再做时域平均。别用单帧单帧的随机噪声会让你校准出来的BLC值忽高忽低。三、DPCDefect Pixel Correction坏点的“美容”手术坏点分类静态坏点出厂就有位置固定。比如死点永远黑、亮点永远白。动态坏点随温度、增益变化出现的坏点。温度高了冒出来温度低了又消失。闪烁坏点时域上随机闪现像星星一样一闪一闪。算法思路DPC的核心是“检测替换”。检测方法有很多我实战中验证过最稳的是“梯度检测法”// 这里踩过坑用固定阈值检测坏点不同场景下误检率极高// 正确做法自适应阈值intdetect_defect(uint16_tcenter,uint16_tneighbors[8],floatgain){floatmediancompute_median(neighbors,8);// 用中位数别用均值floatdiffabs((float)center-median);floatthresholdbase_threshold*gain*noise_model(median);// 阈值随增益和亮度变化return(diffthreshold)?1:0;}替换策略也有讲究。简单替换成邻域均值会模糊细节我习惯用“中值替换边缘保护”// 推荐写法边缘保护的中值替换uint16_tcorrect_pixel(uint16_tcenter,uint16_tneighbors[8]){// 先判断中心像素是否在边缘上floath_gradabs(neighbors[3]-neighbors[4]);// 水平梯度floatv_gradabs(neighbors[1]-neighbors[6]);// 垂直梯度if(h_gradv_grad){// 垂直边缘用垂直方向的两个邻居取中值returnmedian_of_two(neighbors[1],neighbors[6]);}else{// 水平边缘用水平方向的两个邻居取中值returnmedian_of_two(neighbors[3],neighbors[4]);}}调优技巧坏点表管理产线标定出来的静态坏点表要存到模组的OTP里。但注意坏点表会随着时间增长建议在ISP驱动里留一个“在线学习”接口用户使用过程中发现的新坏点可以追加进去。动态坏点检测的“死区”在低亮度下噪声本身就很大容易把正常像素误判为坏点。我习惯在亮度低于某个阈值时把DPC的检测阈值拉高或者直接关闭动态检测只做静态坏点替换。闪烁坏点的处理时域上闪现的坏点单帧检测很难搞定。我的做法是“3帧确认”连续三帧都在同一位置检测到坏点才执行替换。这样能过滤掉大部分随机噪声。四、LSCLens Shading Correction镜头阴影的“匀光”工程现象与原理镜头的光学特性导致边缘亮度低于中心同时不同波长的光衰减程度不同造成边缘偏色。LSC就是给边缘像素“补光”同时做颜色补偿。校准与插值产线校准阶段用均匀光源拍一张灰卡计算每个像素位置的增益系数。但全像素存储增益表太占空间通常用“网格插值”的方式// 网格增益表16x16网格每个网格存R/Gr/Gb/B四个通道的增益typedefstruct{uint16_tgrid_width;// 网格宽度单位像素uint16_tgrid_height;// 网格高度单位像素uint8_tgrid_cols;// 网格列数uint8_tgrid_rows;// 网格行数uint16_t*gain_r;// R通道增益表uint16_t*gain_gr;// Gr通道增益表uint16_t*gain_gb;// Gb通道增益表uint16_t*gain_b;// B通道增益表}lsc_table_t;// 运行时双线性插值floatinterpolate_gain(lsc_table_t*table,floatx,floaty,intchannel){// 计算网格索引intcol(int)(x/table-grid_width);introw(int)(y/table-grid_height);floatfrac_x(x-col*table-grid_width)/table-grid_width;floatfrac_y(y-row*table-grid_height)/table-grid_height;// 四个网格点的增益floatg00get_gain(table,col,row,channel);floatg10get_gain(table,col1,row,channel);floatg01get_gain(table,col,row1,channel);floatg11get_gain(table,col1,row1,channel);// 双线性插值floatg0g00*(1-frac_x)g10*frac_x;floatg1g01*(1-frac_x)g11*frac_x;returng0*(1-frac_y)g1*frac_y;}调优技巧网格密度选择网格太密存储开销大网格太疏插值误差大。我的经验是对于手机镜头16x16网格足够对于车载广角镜头畸变大建议32x32甚至64x64。增益上限保护边缘增益可能达到2.0甚至更高但增益太大会放大噪声。我习惯设置一个最大增益值比如2.5超过的部分截断。如果边缘亮度实在补不回来说明镜头本身的光学设计有问题LSC不是万能的。温度与增益的影响LSC校准通常在25°C、增益1x下进行。但实际使用中温度和增益变化会导致镜头阴影特性偏移。高端方案会做“多温度点多增益点”的LSC表运行时根据当前状态选择最近的表做插值。产线校准的“白场”要求产线拍白场时光源的均匀性要保证在1%以内。曾经有产线用普通灯箱边缘亮度比中心低了5%校准出来的LSC表把边缘增益拉得特别高结果用户拍出来的照片边缘全是噪点。五、三个模块的联动调优BLC、DPC、LSC不是独立工作的它们的处理顺序和参数耦合直接影响最终效果。处理顺序正确的流水线顺序是BLC → DPC → LSC。为什么BLC先做把暗电流底数清掉DPC检测坏点时用的阈值才准确DPC做完坏点被替换LSC计算增益时不会被坏点干扰。参数耦合BLC不准 → DPC误检率升高暗电流没清干净暗场下正常像素被误判为坏点BLC不准 → LSC边缘偏色暗电流在不同通道的差异被LSC的增益放大DPC没做好 → LSC校准表里混入坏点数据导致校准出来的增益表有异常值实战案例回到开头的产线问题。最终排查发现产线更换了sensor批次新批次的暗电流特性变了但BLC校准文件没更新。BLC偏差导致DPC在暗场下疯狂误检把正常像素替换成邻域值这些被替换过的像素又被LSC的增益放大最终表现为边缘偏色。解决方案重新标定新批次sensor的BLC参数同时把DPC的检测阈值调松一点因为新批次sensor的暗电流一致性比旧批次差再重新跑一遍LSC校准。问题解决。六、个人经验总结别迷信“通用算法”BLC、DPC、LSC的参数高度依赖sensor型号、镜头模组、甚至同一型号不同批次的工艺偏差。每换一次sensor或镜头必须重新标定。产线校准是“良心活”很多公司为了省成本产线校准只做单帧、单温度点。结果就是用户拿到手机冬天拍暗场偏色夏天拍暗场也偏色。多花点时间做多温度点标定后期能省大量客诉处理成本。调试工具要趁手我习惯在调试阶段把BLC、DPC、LSC的中间结果都dump出来用Python脚本可视化。比如BLC补偿后的暗场图像、DPC检测到的坏点位置图、LSC的增益曲面图。肉眼一看就知道问题在哪。留有余量BLC的减法、LSC的乘法都会改变数据的动态范围。设计RAW域处理时要保证数据位宽足够比如12bit sensor内部处理用16bit避免溢出或截断。最后一条也是最重要的一条永远不要相信“默认参数”。每个模组出厂前必须用暗场和白场做一次全流程验证。BLC、DPC、LSC这三个模块任何一个参数不对后面ISP的AWB、CCM、Gamma全都会跟着跑偏而且你很难定位到根因——因为症状可能出现在色彩、亮度、噪声等各个方面但病根往往就在RAW域。