传感器基础:CMOS/CCD感光原理、Bayer阵列与CFA
做图像处理这么多年,我始终觉得一个道理特别朴素:你连传感器怎么工作的都不清楚,就别谈什么校正了。镜头阴影校正也好,AWB白平衡也罢,根儿都在传感器上。今天咱们就把这块硬骨头啃下来。
从光子到数字:感光原理
说白了,无论是CMOS还是CCD,核心就一件事:把光子变成电子,再把电子变成电压,最后量化成数字。这个过程叫光电转换。
我当年刚入行时,总以为传感器是直接输出RGB值的。后来被老工程师骂了一顿才明白——传感器其实是个“色盲”,它只感知光的强度,不感知颜色。颜色?那是后期算出来的。
| 特性 | CCD | CMOS |
|---|---|---|
| 读出方式 | 逐行转移,统一输出 | 每个像素独立读出 |
| 功耗 | 较高(需要多组电压) | 较低(单电压供电) |
| 噪声 | 读出噪声低 | 固定模式噪声较明显 |
| 速度 | 较慢 | 快(支持高帧率) |
| 成本 | 高 | 低 |
| 主流应用 | 工业、科研、天文 | 手机、安防、消费电子 |
Bayer阵列:为什么是RGGB?
你想想看,传感器只感知亮度,那颜色信息从哪来?答案就是色彩滤波阵列(CFA)。最经典的CFA就是Bayer阵列,1976年由柯达的Bryce Bayer发明。
Bayer阵列的排列规律很简单:每4个像素一组,2个绿色、1个红色、1个蓝色。为什么绿色多一倍?因为人眼对绿色最敏感,这么做可以模拟人眼的亮度感知特性。
核心知识点: Bayer阵列不是随机排列的。标准Bayer有四种变体:BGGR、GBRG、GRBG、RGGB。不同传感器厂商用的顺序不一样,做ISP时第一步就是确认Bayer顺序,否则颜色全乱套。
我曾经接手过一个项目,上游给的RAW图颜色怎么调都不对。折腾了两天,最后发现是Bayer顺序搞反了——把RGGB当成了BGGR处理。从那以后,我养成了一个习惯:拿到RAW图第一件事,先画个4x4像素的灰度图,看棋盘格纹理的方向,确认Bayer顺序。
# 一个简单的Bayer顺序检测思路(伪代码)
def detect_bayer_order(raw_4x4_block):
# 假设raw_4x4_block是4x4的RAW像素值
# 计算四个2x2子块的平均值
# 最亮的子块对应绿色通道(因为绿色有两个)
# 最暗的子块对应蓝色通道
# 中间亮度的对应红色通道
# 通过位置关系反推Bayer顺序
pass
色彩滤波阵列(CFA)的更多玩法
Bayer阵列虽然经典,但它不是唯一的选择。这些年我见过不少奇奇怪怪的CFA设计,各有各的算盘。
- RGBW阵列: 在RGGB里插入一个白色(W)像素,提升低光灵敏度。代价是分辨率下降,需要复杂的去马赛克算法。
- RYYCy阵列: 用黄色(Y)和青色(Cy)替代部分绿色,常见于一些老式摄像机。色彩还原难度大。
- Quad Bayer阵列: 4个同色像素组成一组,用于高像素手机传感器。通过像素合并(Binning)提升暗光表现。
- X-Trans阵列: 富士的专利,6x6的伪随机排列,减少摩尔纹。我用过几次,去马赛克算法确实复杂。
从RAW到RGB:去马赛克(Demosaic)
传感器输出的RAW数据,每个像素只有一个颜色通道的值。要得到完整的RGB图像,必须通过插值算法把缺失的两个通道补回来。这个过程就叫去马赛克。
最简单的办法是双线性插值——取周围同色像素的平均值。但效果嘛……边缘全是锯齿和伪色。我早期做算法时试过,被领导骂了一顿。
稍微好一点的是边缘导向插值:先判断当前像素是水平边缘还是垂直边缘,然后沿着边缘方向插值,不跨边缘。这个算法在手机上用了很多年。
现在主流的是基于深度学习的去马赛克,效果确实好,但计算量也大。做嵌入式ISP时,还得靠传统算法。
// 一个简化的边缘导向插值(C++风格伪代码)
uint16_t demosaic_green_at_red(uint16_t R, uint16_t G_left, uint16_t G_right,
uint16_t G_up, uint16_t G_down) {
int16_t horz_diff = abs(G_left - G_right);
int16_t vert_diff = abs(G_up - G_down);
if (horz_diff < vert_diff) {
// 水平方向更平滑,用左右平均
return (G_left + G_right) / 2;
} else if (vert_diff < horz_diff) {
// 垂直方向更平滑,用上下平均
return (G_up + G_down) / 2;
} else {
// 四个方向平均
return (G_left + G_right + G_up + G_down) / 4;
}
}
传感器与ISP的接口:RAW图格式
传感器输出的RAW数据,通常不是8bit的。常见的位深有10bit、12bit、14bit,甚至16bit。位深越大,动态范围越宽,但数据量也越大。
我见过不少新手工程师,拿到RAW图直接用8bit显示,结果一片漆黑或者一片惨白。其实RAW数据需要做黑电平校正(Black Level Correction)和线性化后才能正常显示。
| 位深 | 动态范围 | 常见应用 |
|---|---|---|
| 8bit | 0~255 | 老式摄像头、预览模式 |
| 10bit | 0~1023 | 手机主摄、安防 |
| 12bit | 0~4095 | 专业相机、工业相机 |
| 14bit | 0~16383 | 高端单反、科研级 |
本章知识体系
下面这张图把传感器、Bayer阵列、CFA、去马赛克和RAW格式的关系串起来了。建议你保存下来,做ISP调试时对照着看。
嗯,这张图基本把传感器到RGB的完整链路画清楚了。你仔细看,从光子到最终图像,中间每一步都有坑。后面讲镜头阴影校正时,你会发现这些基础知识一个都跑不掉——阴影校正就是在RAW域做的,Bayer顺序搞错,校正方向都是反的。