2. Sensor特性与RAW图解析:Bayer阵列、黑电平校正、坏点校正的原理与工程实现
好,我们直接进入正题。上一章聊了ISP的整体流水线,这一章我们得把镜头拉到最前面——Sensor本身。说白了,你算法再牛,Sensor喂进来的数据是脏的、乱的,后面全是白搭。
我个人习惯,拿到一款新Sensor,第一件事不是跑算法,而是先看RAW图。看什么?看Bayer排布对不对,看黑电平稳不稳,看有没有坏点。这就像厨师做菜前先检查食材,肉有没有异味,菜新不新鲜。这一步省了,后面Debug能让你怀疑人生。
2.1 Bayer阵列:Sensor的“色盲”与猜色
先问一个问题:为什么Sensor拍出来的是RAW图,而不是直接是彩色图?
因为Sensor本身是“色盲”的。每个像素点只能感知光的强度,它分不清红光还是蓝光。那怎么办?工程师们想了个办法——在每个像素前面盖一层滤光片,只让特定颜色的光通过。这就是Bayer阵列。
核心概念:Bayer阵列是一种2x2的重复单元,通常包含1个红色(R)、2个绿色(G)、1个蓝色(B)像素。人眼对绿色最敏感,所以绿色采样率是红蓝的两倍。
常见的Bayer模式有四种:RGGB、BGGR、GRBG、GBRG。别看只是字母顺序不同,搞错了,整张图颜色就全乱了。我在项目中遇到过,Sensor数据手册写的是RGGB,结果实际输出是BGGR,调试了三天才发现是这里配错了。
| Bayer模式 | 第一行 | 第二行 | 常见Sensor厂商 |
|---|---|---|---|
| RGGB | R G R G ... | G B G B ... | Sony IMX系列 |
| BGGR | B G B G ... | G R G R ... | OmniVision部分型号 |
| GRBG | G R G R ... | B G B G ... | Samsung部分型号 |
| GBRG | G B G B ... | R G R G ... | 较少见,需注意 |
你想想看,每个像素只有一种颜色信息,那最终要输出彩色图像怎么办?这就得靠“猜”——也就是去马赛克(Demosaic)算法。用周围像素的颜色信息,插值计算出每个像素缺失的另外两个颜色通道。这部分我们后面章节会详细讲,这里先记住:Bayer阵列是一切的基础。
2.2 黑电平校正(Black Level Correction)
好,Bayer阵列搞清楚了,接下来看RAW图里一个很隐蔽但极其重要的问题——黑电平。
理论上,Sensor完全遮光时,输出应该是0。但实际上,由于Sensor的电路噪声、暗电流等因素,全黑状态下输出的并不是0,而是一个固定的偏移值。这个偏移值就是黑电平(Black Level)。
注意:不同Sensor的黑电平值可能不同,甚至同一个Sensor在不同增益下黑电平也会漂移。如果不做校正,暗部细节会发灰、偏色,动态范围也会被压缩。
校正公式很简单:
// 黑电平校正
pixel_out = pixel_raw - black_level;
// 如果Sensor有多个通道独立黑电平
pixel_out = pixel_raw - black_level_channel;
// 防止下溢
if (pixel_out < 0) pixel_out = 0;
嗯,这里要注意。有些Sensor会提供OB(Optical Black)区域,也就是Sensor边缘被遮挡的像素。我们可以实时从OB区域统计出当前帧的黑电平值,而不是用一个固定值。我个人建议,如果Sensor支持OB统计,优先用动态黑电平,因为温度变化、曝光时间变化都会影响黑电平。
| 校正方式 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|
| 固定黑电平 | 实现简单,计算量小 | 无法适应环境变化 | 低端Sensor,固定场景 |
| OB区域统计 | 实时自适应,精度高 | 需要OB区域,占用Sensor面积 | 主流手机Sensor |
| 全局统计+滤波 | 抗噪性好 | 响应慢,有延迟 | 高帧率场景 |
我曾经在一个项目中,Sensor的黑电平在高温下漂移了将近10个DN(Digital Number),结果暗部区域出现了明显的绿色色偏。查了两天才定位到是黑电平校正没跟上温度变化。后来改成了每帧从OB区域重新统计,问题就解决了。
2.3 坏点校正(Defect Pixel Correction)
接下来是坏点。Sensor生产过程中,不可能保证几千万个像素点全部完美。总会有一些像素点对光的响应异常——要么一直很亮(亮点),要么一直很暗(暗点),要么响应曲线不对。
这些坏点如果不处理,在图像上就是一个个固定位置的噪点,非常显眼。尤其是亮点,在暗光环境下像星星一样,用户一眼就能看到。
经验之谈:坏点校正分为两个阶段:静态坏点校正(基于出厂标定)和动态坏点校正(基于实时检测)。两者结合效果最好。
静态坏点校正很简单。Sensor出厂时,厂商会提供一个坏点列表(Defect Pixel List),包含坏点的坐标和类型。我们在ISP初始化时把这个列表加载进来,对这些位置的像素做特殊处理。
动态坏点校正就复杂一些。它需要在每一帧中实时检测哪些像素可能是坏点。常用的方法是:
- 梯度检测法:计算当前像素与周围同色像素的差值,如果差值超过阈值,判定为坏点。
- 中值替代法:用周围同色像素的中值替换坏点值。
- 边缘保护:在纹理区域要小心,别把边缘误判为坏点。
// 动态坏点检测与校正(简化版)
// 以当前像素为中心,取3x3同色通道像素
uint16_t neighbors[9];
// ... 从RAW图中提取同色通道像素 ...
// 计算当前像素与周围像素的差值
uint16_t min_val = min(neighbors);
uint16_t max_val = max(neighbors);
uint16_t current = neighbors[4]; // 中心像素
// 如果当前像素远大于最大值或远小于最小值
if (current > max_val + threshold || current < min_val - threshold) {
// 判定为坏点,用中值替代
uint16_t median = get_median(neighbors);
pixel_out = median;
} else {
pixel_out = current;
}
避坑指南:我曾经在动态坏点校正上吃过亏。阈值设得太小,正常纹理被当成坏点抹掉了,图像细节丢失严重;阈值设得太大,坏点又漏检了。后来我采用自适应阈值——根据局部亮度和噪声水平动态调整阈值,效果好了很多。
2.4 工程实现中的关键考量
好了,原理讲完了。但工程落地和原理之间,还隔着一条河。我总结几个实际开发中容易踩的坑:
- Bayer排布确认:不要只看数据手册,一定要实际抓一帧RAW图,手动解析确认。我见过数据手册写错的情况。
- 黑电平与增益的耦合:模拟增益和数字增益都会影响黑电平。增益改变后,黑电平要重新校正。
- 坏点列表的更新:Sensor使用一段时间后,可能会出现新的坏点。建议在产线或用户使用过程中定期更新坏点列表。
- 性能优化:黑电平校正和坏点校正都是在RAW域进行的,数据量巨大(比如1200万像素)。在移动端,要充分利用NEON指令集或GPU并行加速。
一句话总结:Bayer阵列是Sensor的“语言”,黑电平校正是让RAW图“归零”,坏点校正是给RAW图“祛斑”。这三步做好了,后面的ISP处理才有意义。
嗯,这一章的内容就到这里。Sensor的特性远不止这些,但Bayer、黑电平、坏点是最基础也最关键的三个点。搞懂了它们,你就拿到了读懂RAW图的钥匙。