3、RAW预处理:黑电平校正、坏点校正、镜头阴影校正(LSC)
好,咱们进入RAW域处理的第一道关卡。说白了,传感器拿到的原始数据是不能直接用的。为什么?因为硬件有物理偏差,有制造缺陷,还有光学系统的天然瑕疵。这三步预处理,就是把这些“脏东西”先清理干净。
我个人习惯把这三大校正称为“RAW的三件套”。顺序也很讲究:先黑电平,再坏点,最后LSC。调换顺序?嗯,我试过,效果会打折扣,后面细说。
3.1 黑电平校正(Black Level Correction)
先问个问题:传感器在完全无光的情况下,输出应该是0吗?
答案是否定的。因为传感器电路本身有暗电流,像素在无光照时也会积累少量电荷。这个底噪值,就是黑电平。如果不减掉,整张画面会偏灰,暗部细节全被埋没。
3.1.1 原理与实现
黑电平校正的公式很简单:
I_corrected = I_raw - black_level
但black_level怎么取?不同传感器策略不同。常见两种:
| 方式 | 说明 | 我遇到的坑 |
|---|---|---|
| 固定值 | 传感器厂商给一个固定偏移量,比如64 | 温度一变,固定值就不准了 |
| OB像素 | 传感器边缘有被遮光的像素,实时读取均值 | OB行偶尔有坏点,得先剔除再平均 |
3.1.2 代码示例
def black_level_correction(raw_img, ob_pixels=None, fixed_bl=None):
if ob_pixels is not None:
# 剔除OB行中的异常值(比如上下5%分位)
ob_clean = np.percentile(ob_pixels, [5, 95])
bl = np.mean(ob_clean)
else:
bl = fixed_bl
corrected = np.maximum(raw_img.astype(np.int16) - bl, 0)
return corrected.astype(np.uint16)
3.2 坏点校正(Defect Pixel Correction)
传感器制造不可能完美。总有一些像素对光不敏感(死点),或者一直亮着(亮点),或者响应异常。这些坏点如果不处理,会在最终图像上形成刺眼的“星星”或“黑洞”。
3.2.1 坏点检测策略
我把它分成两类:
- 静态坏点:出厂就有的,通过标定生成坏点map
- 动态坏点:使用中出现的,靠算法实时检测
实际项目中,我建议两者结合。静态map保底,动态算法兜底。
3.2.2 动态坏点检测算法
最常用的方法是“邻域比较法”。看当前像素和周围8个邻居的差异:
def detect_defect_pixel(img, x, y, threshold=40):
center = img[x, y]
neighbors = get_8_neighbors(img, x, y)
median_val = np.median(neighbors)
if abs(int(center) - int(median_val)) > threshold:
return True
return False
为什么用中位数而不是均值?因为均值容易被其他坏点带偏。中位数更鲁棒。
3.2.3 坏点校正方法
检测到坏点后,怎么修?
| 方法 | 原理 | 适用场景 |
|---|---|---|
| 中值替换 | 用邻域中位数替代 | 孤立坏点,效果最好 |
| 均值替换 | 用邻域均值替代 | 坏点簇,但会模糊细节 |
| 插值法 | 沿边缘方向插值 | 坏点在纹理区域 |
3.3 镜头阴影校正(LSC)
这是光学系统的“原罪”。镜头中心光线直射传感器,边缘光线斜射,导致边缘亮度下降。再加上镜头本身的透光率不均匀,就形成了“暗角”。
3.3.1 校正原理
说白了就是给每个像素乘一个增益系数。中心区域增益接近1,边缘增益大于1。
I_corrected(x, y) = I_raw(x, y) * gain(x, y)
gain(x, y)怎么来?两种主流方式:
- 多项式拟合:用二维多项式拟合阴影曲面
- 查表法:预先标定每个像素的增益,存成LUT
3.3.2 标定流程
我自己在项目中用的标定步骤:
- 拍摄一张均匀白板(DNP或X-Rite白卡)
- 取图像中心区域均值作为参考亮度
- 计算每个像素的增益 = 参考亮度 / 该像素亮度
- 对增益图做平滑(防止噪声放大)
- 存储为LUT或拟合系数
3.3.3 代码实现
def apply_lsc(raw_img, gain_lut):
# gain_lut 与 raw_img 同尺寸
corrected = raw_img * gain_lut
# 注意:增益后可能溢出,需要做饱和处理
corrected = np.clip(corrected, 0, 2**12 - 1) # 假设12bit RAW
return corrected.astype(np.uint16)
3.3.4 分通道处理
不同波长的光折射率不同,所以R、G、B通道的阴影曲线不一样。必须分通道标定和校正。
| 通道 | 阴影特点 | 校正策略 |
|---|---|---|
| R | 边缘衰减最严重 | 增益系数最大 |
| G | 居中 | 标准增益 |
| B | 衰减相对较小 | 增益系数最小 |
3.4 三大校正的流程关系
最后,我用一张图把这三步串起来。顺序不能乱:黑电平校正必须在最前面,因为它影响所有像素的基准。坏点校正紧随其后,因为坏点如果不修,LSC会把坏点增益放大。LSC放在最后,因为它依赖前两步的干净数据。
嗯,这三步做完,RAW数据才算“干净”了。后面才能放心地做去马赛克、白平衡、色调映射。每一步都有坑,但踩过一次就记住了。希望你们能一次跳过这些坑。