1. 传感器基础与RAW数据概述
各位同学,咱们这门课叫《传感器标定与RAW数据预处理》,第一讲得先把地基打牢。说白了,就是搞清楚你手里的RAW数据到底是怎么来的,它长什么样,里面藏了哪些信息。
我做了十几年嵌入式视觉,刚入行那会儿,拿到RAW文件就直接往算法里塞,结果效果一塌糊涂。后来才明白——不懂传感器原理,RAW数据预处理就是瞎搞。好,咱们开始。
1.1 CMOS与CCD传感器工作原理
目前主流的图像传感器就两种:CCD和CMOS。虽然现在手机、相机、工业相机里绝大多数都是CMOS,但CCD在某些高灵敏度、低噪声的场景下依然有它的位置。
| 特性 | CCD | CMOS |
|---|---|---|
| 读出方式 | 逐行转移,统一输出 | 每个像素独立寻址读出 |
| 噪声水平 | 低(统一放大) | 较高(像素级放大器差异) |
| 功耗 | 高(需要多路电压) | 低(单电压供电) |
| 读出速度 | 慢(串行输出) | 快(并行/列并行读出) |
| 成本 | 高 | 低 |
| 典型应用 | 天文、科研、高端工业 | 手机、安防、消费电子 |
我个人的习惯是,选型时先看帧率和功耗要求。如果项目需要30fps以上、电池供电,基本就告别CCD了。但如果你做的是弱光环境下的科学成像,CCD的低噪声优势还是很难替代的。
CMOS的工作原理,我简单概括一下:每个像素里都有一个光电二极管,光子打进来产生电子,电子被转换成电压,然后通过行列选通电路读出。嗯,这里有个关键点——每个像素的放大器增益不可能完全一致,这就导致了FPN(固定模式噪声)。后面讲标定的时候,我们会专门处理这个问题。
核心概念:传感器输出的原始电压值,和入射光强之间是线性关系吗?理论上是的,但实际上存在暗电流、非线性响应、像素串扰等因素。所以RAW数据预处理的第一步,就是把这些非理想因素校正回来。
1.2 Bayer阵列模式
单颗传感器怎么感知颜色?答案就是Bayer阵列。说白了,就是在每个像素上覆盖一个滤光片,只让特定波长的光通过。
最常见的Bayer模式是RGGB,也就是一个像素感红,两个像素感绿,一个像素感蓝。为什么绿色多一个?因为人眼对绿色最敏感,这样设计能更好地模拟人眼视觉。
我曾经在一个项目中遇到过客户投诉,说拍出来的绿色植物偏紫。查了半天,发现是Bayer阵列的排列方向搞反了——ISP里的去马赛克算法默认是RGGB,但传感器实际输出是GBRG。你看,这种低级错误,一旦批量生产就非常麻烦。
避坑指南:拿到一款新传感器,第一件事就是确认Bayer模式的排列顺序。不要相信数据手册上的文字描述,直接拍一张均匀白板,看RAW数据的R/G/B通道位置。我曾经被一份翻译错误的数据手册坑过,从那以后,我只相信实测数据。
常见的Bayer模式有四种:
- RGGB:最常用,索尼、三星大部分传感器采用
- BGGR:部分OmniVision传感器使用
- GRBG:某些安森美传感器使用
- GBRG:较少见,但确实存在
你想想看,如果去马赛克算法用错了Bayer模式,出来的图像会是什么样?颜色完全错乱,像打翻了调色盘。所以,Bayer模式是RAW数据处理的第一个关键参数。
1.3 RAW文件格式解析
RAW文件,说白了就是传感器输出的原始数字值,没有经过任何ISP处理。它保存的是每个像素的量化后的电压值,位深通常比JPEG的8位深得多。
1.3.1 RAW位深:8bit、10bit、12bit
| 位深 | 动态范围 | 典型应用 | 存储大小(1000万像素) |
|---|---|---|---|
| 8bit | 0-255 | 低端摄像头、早期手机 | 约10MB |
| 10bit | 0-1023 | 主流手机、工业相机 | 约12.5MB |
| 12bit | 0-4095 | 高端相机、科学成像 | 约15MB |
我个人建议,做算法开发时尽量用12bit RAW数据。为什么呢?因为8bit数据经过多次数学运算后,量化误差会被放大,最终结果可能惨不忍睹。12bit给你更多的灰度级,后期处理的空间大得多。
1.3.2 DNG格式
DNG是Adobe推出的开源RAW格式,说白了就是想把各家厂商的私有RAW格式统一起来。DNG文件本质上是一个TIFF文件,里面包含了RAW数据、元数据(传感器参数、标定数据等)以及一个预览图。
我在项目中经常用DNG作为中间格式。比如从工业相机采集到RAW数据后,我会先封装成DNG,然后用Adobe的DNG SDK做验证。这样能确保我的预处理流程和行业标准对齐。
注意:DNG虽然开源,但不同厂商的DNG文件在元数据填充上可能不一致。特别是黑电平、白平衡增益、标定矩阵这些关键参数,有的厂商填了,有的没填。所以解析DNG时,一定要检查元数据的完整性。
1.3.3 RAW12/10/8的存储格式
RAW数据在内存或文件里怎么存的?这里有个坑——位深和存储位宽不一定相等。
举个例子:12bit RAW数据,每个像素值范围0-4095,需要12个bit来存。但计算机内存是按字节(8bit)对齐的。所以常见的存储方式有:
- RAW8:每个像素占1字节,直接存
- RAW10:4个像素占5字节(4×10=40bit=5字节),打包存储
- RAW12:2个像素占3字节(2×12=24bit=3字节),打包存储
你想想看,如果解析代码没按正确的打包方式解包,出来的数据就是乱的。我曾经调试一个bug,图像上出现周期性的条纹,查了两天才发现是RAW10的打包顺序搞反了。
// RAW12解包示例:2个像素占3字节
// 假设字节流为:b0, b1, b2
// 像素0 = (b0 << 4) | (b1 >> 4)
// 像素1 = ((b1 & 0x0F) << 8) | b2
uint16_t unpack_raw12(uint8_t *buf, int index) {
int byte_offset = (index / 2) * 3;
if (index % 2 == 0) {
return (buf[byte_offset] << 4) | (buf[byte_offset + 1] >> 4);
} else {
return ((buf[byte_offset + 1] & 0x0F) << 8) | buf[byte_offset + 2];
}
}
1.4 RAW数据存储结构
搞清楚了格式,咱们再看看RAW数据在内存里到底怎么排列的。这里我画了一张图,帮你建立直观认识。
从这张图你能看到,RAW数据的存储其实分三层:
- 像素阵列层:传感器上每个像素的原始值,按Bayer模式排列
- 打包层:根据位深,把像素值按特定规则打包成字节流
- 文件层:在字节流基础上加上文件头、元数据,形成DNG或私有RAW文件
嗯,这里要注意的是,不同厂商的私有RAW格式,在文件头里藏的信息差别很大。有的把黑电平、增益、温度都写进去了,有的只写了个分辨率。所以解析私有RAW时,一定要拿到厂商的SDK或者详细的文档,否则你就是在猜。
总结一下本章的核心:
- CMOS和CCD各有优劣,选型看帧率、功耗、噪声要求
- Bayer模式是RAW数据的颜色基础,搞错排列顺序,后面全白搭
- RAW位深决定了动态范围和存储方式,12bit是算法开发的黄金标准
- DNG是行业标准格式,但元数据完整性需要验证
- RAW数据的存储结构:像素阵列 → 打包对齐 → 文件封装,每一层都有坑