1. ISP基础与图像传感器

做嵌入式图像处理这些年,我最大的感触是:不懂传感器,就别谈ISP优化。很多工程师一上来就调参数,结果换了颗传感器,之前的经验全废了。这一章,咱们先把地基打牢。

1.1 ISP流水线概述

ISP,全称Image Signal Processor。说白了,就是把传感器输出的原始数据,变成人眼看着舒服的图像。你想想看,手机拍一张照片,背后其实跑了一整套流水线。

我个人习惯把ISP流水线分成三个阶段:

  • 前端处理:坏点校正、黑电平补偿、镜头阴影校正。这些操作都在RAW域完成。
  • 中间处理:去马赛克、白平衡、颜色校正、伽马校正。这是最核心的部分。
  • 后端处理:边缘增强、降噪、色调映射。输出最终图像。

核心观点:ISP不是一条固定的流水线。不同平台、不同传感器,流水线的顺序和模块都可能不一样。我在项目中遇到过客户非要先做降噪再做去马赛克,结果细节全糊了——顺序真的很重要。

下面这张图,是我自己画的一个典型ISP流水线框架。你可以把它当作本章的地图。

典型ISP流水线框架 CMOS传感器 前端处理 坏点/黑电平/阴影 中间处理 去马赛克/白平衡 颜色校正/伽马 后端处理 降噪/锐化/色调 YUV/JPEG 💡 说明: • 前端处理在RAW域,中间处理在RGB域,后端处理在YUV域 • 不同平台可能合并或拆分某些模块 • FPGA加速通常针对前端和中间处理中的计算密集型模块

1.2 CMOS传感器工作原理

CMOS传感器,现在几乎一统天下了。CCD?除了极少数工业场景,基本看不到了。

它的工作原理,我总结成三步:

  1. 光电转换:光子打到像素上,产生电子。光越强,电子越多。
  2. 电荷读出:通过行选和列选,把每个像素的电压读出来。
  3. 模数转换:ADC把模拟电压转成数字值,也就是RAW数据。

实战经验:我曾经调试一款安防摄像头,发现暗光下图像偏绿。查了半天,原来是传感器增益曲线没配好。CMOS的模拟增益和数字增益,对色彩影响很大——这个坑我踩过。

这里有个关键参数表,做ISP调试时经常用到:

参数 典型值 影响
像素尺寸 1.4µm - 2.0µm 越大,感光越好,但分辨率受限
满阱容量 10000e⁻ - 30000e⁻ 决定动态范围
读出噪声 2e⁻ - 10e⁻ 影响暗光信噪比
量子效率 50% - 80% 越高,灵敏度越好

1.3 Bayer阵列与色彩滤波

CMOS传感器本身是色盲的——它只能感知光的强度,分不清颜色。那彩色照片怎么来的?靠的是Bayer阵列。

Bayer阵列,说白了就是在每个像素上盖一层滤色片。最常见的格式是RGGB:

  • 50%的像素是绿色(G)
  • 25%的像素是红色(R)
  • 25%的像素是蓝色(B)

为什么绿色占一半?因为人眼对绿色最敏感。这个设计很巧妙,我每次讲到这里都觉得挺佩服当年发明Bayer的人。

⚠️ 注意:Bayer阵列不是唯一的色彩滤波方案。还有RCCC(用于车规)、RCCB(用于低照度)等变体。选型时一定要确认传感器的Bayer模式,否则ISP配置全错。

常见的Bayer排列有四种:

模式 第一行 第二行 常见场景
BGGR B G B G G R G R Sony传感器
RGGB R G R G G B G B OmniVision传感器
GBRG G B G B R G R G 部分安防传感器
GRBG G R G R B G B G 部分车载传感器

1.4 RAW图格式解析

RAW图,就是传感器直接输出的原始数据。没有经过任何ISP处理。我经常跟团队说:RAW图是ISP调试的起点,也是终点——调试好了,后面都是锦上添花;调试不好,后面再怎么调也救不回来。

RAW图有几个关键属性:

  • 位深:常见8bit、10bit、12bit、14bit。位深越大,动态范围越好。
  • Bayer模式:上面讲过的四种之一。
  • 黑电平:传感器在无光时的输出值,通常不是0。
  • 白电平:传感器饱和时的输出值。

举个例子,一个10bit RAW图,黑电平可能是64,白电平是1023。有效数据范围只有64~1023,而不是0~1023。这个偏移量,ISP前端必须补偿掉。

关键公式:校正后的像素值 = (RAW值 - 黑电平) × 增益

这个公式虽然简单,但我在项目中见过有人把黑电平设成0,结果暗部全是噪声——嗯,细节决定成败。

下面是一个简单的RAW图读取代码示例,用Python演示:

import numpy as np

def read_raw_10bit(file_path, width, height, bayer='RGGB'):
    """
    读取10bit RAW图,返回numpy数组
    """
    raw_data = np.fromfile(file_path, dtype=np.uint16)
    raw_data = raw_data.reshape((height, width))
    
    # 10bit数据,右移6位得到有效值
    raw_data = raw_data & 0x03FF
    
    # 分离Bayer通道
    if bayer == 'RGGB':
        R = raw_data[0::2, 0::2]
        G1 = raw_data[0::2, 1::2]
        G2 = raw_data[1::2, 0::2]
        B = raw_data[1::2, 1::2]
    
    return raw_data, {'R': R, 'G1': G1, 'G2': G2, 'B': B}

# 使用示例
raw, channels = read_raw_10bit('image.raw', 1920, 1080)
print(f"RAW数据范围: {raw.min()} - {raw.max()}")

调试技巧:拿到一颗新传感器,我第一件事就是拍一张全黑图和一张全白图。全黑图看黑电平是否稳定,全白图看有没有坏点或阴影。这个习惯帮我省了不少排查时间。

最后,我把本章的知识点整理成一张结构图,方便你回顾:

第一章 知识结构 ISP基础与图像传感器 ISP流水线 前端→中间→后端 CMOS传感器 光电→读出→ADC Bayer阵列 RGGB/BGGR等 RAW图格式 位深/黑电平/白电平

嗯,这一章的内容就到这里。记住一句话:ISP调试,从理解传感器开始。后面章节我们会深入每个模块的实战调优,到时候这些基础会反复用到。