Skip to content

LV025-从Bayer到YUV420的流程 ​

一、 链路总览 ​

相机从“看到光”到“存成一个文件”,中间要经过传感器采样、ISP 处理、色彩空间转换、色度下采样,最后才落盘。以 1920x1080、10bit Bayer 为例,整条链路是这样的:

01-pipeline

阶段数据形态位宽每像素占用1920x1080 一帧
传感器输出Bayer RAW,每像素 1 个分量10bit(RAW10 打包)1.25 字节2.47 MB
ISP 内部仍是 Bayer,每像素 1 个分量10 ~ 12bit2 字节(解包后按 16bit 放)3.96 MB
去马赛克之后每像素 R、G、B8bit x 33 字节5.93 MB
RGB → YUVYUV4448bit x 33 字节5.93 MB
色度下采样 4:2:0YUV4208bit(Y) + 8bit(U/V)/41.5 字节2.97 MB
存储到文件I420 / NV12 裸数据8bit1.5 字节2.97 MB

(表里的 MB 按 1024 进制换算,下同。)

这里有个反直觉的结论,先放出来:处理完的 YUV420(2.97MB)比传感器原始输出的 Bayer RAW(2.47MB)还大。为什么越大越处理、越处理越大,看完下面几步就明白了。

二、 从 sensor 到 YUV420 的处理流程 ​

1. 采集:怎么从 sensor 拿到 Bayer 数据 ​

前面那张表是从“传感器输出”开始算的。但数据不会凭空出现在 SoC 里——从配置 sensor 到 ISP 拿到 Bayer,中间还有一段采集通路:

txt
sensor:光电转换 → 模拟增益 → ADC → 数字 RAW(Bayer)
   ↓ DVP 并口 或 MIPI CSI-2(差分,经 D-PHY)
SoC 侧:D-PHY → CSI2RX 控制器(或 VICAP)
   ↓
ISP 流水线(或先落到 DDR)

1.1 先把 sensor 配起来 ​

sensor 上电不会自己出图,主控要先通过 I2C 配置它的寄存器:分辨率、像素格式(RAW8/RAW10/RAW12)、帧率、曝光、增益,并读 OTP 里的模组标定数据。在 MIPI 体系里这条控制总线叫 CCI(Camera Control Interface),本质仍是 I2C,见《LV010-CCI》。

除总线外还得给足:外部时钟(MCLK,常见 24MHz,sensor 内部 PLL 再倍频出像素时钟)、电源时序(AVDD/DOVDD/DVDD)、reset/powerdown 控制。

1.2 sensor 内部怎么“产生” Bayer ​

光打进感光区转成电荷,经模拟增益、逐行 ADC 变成数字值。每个像素前盖着 R/Gr/Gb/B 之一,所以读出来每像素只有 1 个分量;ADC 的位深决定 RAW 位宽(10bit 最常用)。输出的行、帧节奏由同步信号控制。

1.3 数据怎么传出来:DVP 还是 MIPI ​

  • MIPI CSI-2 + D-PHY(主流):数据走差分对,一般是 1 组时钟 + 1/2/4 组数据 lane。CSI-2 是协议层(打包、帧/行起始、虚拟通道、多路复用),D-PHY 是物理层(差分电气)。RAW10 之类的打包就发生在这一层,接口细节见《LV005-MIPI简介》和《LV005-MIPI-Sensor接口原理图》。
  • DVP 并口(老式、低分辨率):PCLK + HSYNC + VSYNC + 8/10/12 位并行数据线,协议为 ITU-R BT.601/656、BT.1120。

1.4 SoC 侧怎么接住(以 RK3568 为例) ​

进入 SoC 后通常分两级:D-PHY 先把差分信号还原成字节流(PPI),再由 CSI2RX 控制器解析 CSI-2 包、还原成像素交给 ISP;CSI2RX 还支持 HDR 多帧、PDAF,并内置 FPN(固定模式噪声)去除。

到这里数据有两条去向:

  • 处理通路:进 ISP 流水线(ISP 自带 CSI2RX,可直接对接 PHY 器件)。
  • 直存通路:进 VICAP(Video Capture),由它把 DVP / MIPI(CSI2) 数据经 DMA、AXI 直接写进 DDR,不经过 ISP 处理。

ISP 的输入能力:DVP 支持 BT.601/656(RAW8/10/12、YUV422)与 BT.1120;MIPI 支持 1/2/4 lane、RAW8/10/12、YUV422;最大输入 4096x2304。

—— 《Rockchip_RK3568_TRM_Part2_V1.1-20210301.pdf》12.1 Overview(P.981)、12.3.1 CSI2RX(P.982–983)、Chapter 11 VICAP(P.948)

2. 传感器吐出来的 Bayer RAW(10bit) ​

2.1 Bayer 阵列:每个像素只有一个分量 ​

02-bayer-raw10

传感器上每个感光点前面都盖着一片滤色片,只让红、绿、蓝中的一种光通过,所以一个像素只有一个分量。最常见的排布是 RGGB(第一行 R、Gr,第二行 Gb、B),此外还有 BGGR、GRBG、GBRG 三种,统称 Bayer 阵列(CFA)。

图中绿色写成了 Gr、Gb,而不是笼统的一个 G,这里说明一下:与 R 同一行的绿叫 Gr,与 B 同一行的绿叫 Gb。分开标是有原因的:

  • 一是相位:只有区分开才能准确描述四种排布,RGGB 是 R Gr / Gb B,BGGR 是 B Gb / Gr R,GRBG 是 Gr R / B Gb,GBRG 是 Gb B / R Gr;否则单说一个“绿”,根本讲不清它挨着的是 R 还是 B。
  • 二是 Gr、Gb 在 ISP 里就是两条通道:相邻滤色片的串扰、微透镜的角度响应,会让两者对同一束光的响应略有差异,所以白平衡常常按 R/Gr/Gb/B 四通道各给一个增益,去马赛克时两者的插值路径也不同。

只写一个 G 不算错(不少资料为了简洁就是这么画的),但会丢掉这两层信息。

绿色占了一半的感光点,是因为人眼对绿色最敏感,而绿色也携带了最多的亮度信息。

Bayer 长什么样、插值算法怎么写,在《LV010-bayer格式》里已经讲过,这里不重复,只关注数据怎么流动。

2.2 10bit 是怎么装进字节里的 ​

10bit 不是整数个字节,所以要么按 16bit(2 字节)对齐存放,要么打包。相机和 SoC 之间走 MIPI CSI-2(见《LV005-MIPI简介》),用的就是打包格式:

txt
RAW10:4 个像素 -> 5 个字节
       Byte0 ~ Byte3 = P0 ~ P3 的高 8 位
       Byte4         = P0、P1、P2、P3 各自剩下的低 2 位(4 x 2 = 8 位,刚好凑一个字节)

也就是图上画的那样:4 x 10bit = 40bit = 5Byte,平均 10bit/像素 = 1.25 字节/像素。

RAW 格式每像素位宽打包方式每像素占用1920x1080 一帧
RAW88bit1 像素 1 字节1 字节1.98 MB
RAW1010bit4 像素 5 字节1.25 字节2.47 MB
RAW1212bit2 像素 3 字节1.5 字节2.97 MB
RAW1414bit4 像素 7 字节1.75 字节3.46 MB
RAW1616bit1 像素 2 字节2 字节3.96 MB
txt
RAW10,1920x1080:
2073600 x 10 / 8 = 2592000 Byte = 2531.25 KB ≈ 2.47 MB

注意“打包”只存在于传输和存储环节:ISP 拿到数据后一般会先解包成 16bit 对齐再来算,所以同一个 sensor,RAW10 传出来 2.47MB,到了 ISP 内部就变成 3.96MB 了。

3. ISP 把 Bayer 变成 RGB ​

3.1 处理链上每一步在做什么 ​

数据从 sensor 出来之后,进的是 ISP(Image Signal Processor)。不同厂商的流程不完全一样,但大体都包含这些步骤:

步骤干什么位宽变化
黑电平校正 BLC减掉暗电流造成的底噪10 ~ 12bit
镜头阴影校正 LSC把镜头边缘偏暗的部分提亮10 ~ 12bit
坏点校正 DPC干掉个别坏掉的感光点10 ~ 12bit
去噪 NR抑制暗光下的噪声10 ~ 12bit
白平衡 AWBR、G、B 各乘一个增益,让黑白灰不偏色10 ~ 12bit
去马赛克 Demosaic插值补齐每个像素缺失的两个分量每像素变成 3 个分量
色彩校正 CCM3x3 矩阵,把 sensor 的 RGB 拉到目标色域(如 sRGB)8 ~ 12bit
Gamma / 色调曲线把线性响应映射成符合人眼感知的曲线8 ~ 12bit
色彩空间转换 CSCRGB → YUV8bit x 3
色度下采样YUV444 → YUV4208bit x 1.5

至于为什么 ISP 内部要用比 sensor 更高的位深(比如 sensor 出 10bit、ISP 内部算 12bit),是为了在乘增益、做矩阵这些运算里少丢精度,最后再压到 8bit 输出。

这些步骤具体落在哪个硬件模块上,统一放在第三节讲。

3.2 去马赛克:1 个分量插成 3 个 ​

03-demosaic

前面说 Bayer 每个像素只有 1 个分量,但屏幕上的一个像素需要 R、G、B 三个分量。去马赛克就是根据周围像素的颜色值,把每 2x2 里缺失的分量插值补出来。

这是整条链路上唯一让数据量变大的一步:每像素从 1 个分量变成 3 个分量,字节数直接翻 3 倍。而且要清楚一点——插值出来的像素里,只有 1/3 是真实采到的值,剩下的都是算出来的,真实信息量始终只有原始 RAW 那么多。

4. RGB → YUV ​

4.1 为什么要转 ​

因为接下来要“对色度下采样”,而前提是把亮度(Y)和色度(U/V)拆开。人眼对亮度敏感、对色度不敏感,只有拆开之后,才能单独把色度砍掉一半甚至四分之三而肉眼看不出区别。关于人眼特性和采样模式,见《LV020-YUV的格式》。

4.2 转换公式 ​

以 8bit 全范围(JPEG / PC 常用)为例,BT.601:

txt
Y  =  0.299R + 0.587G + 0.114B
Cb = -0.169R - 0.331G + 0.500B + 128
Cr =  0.500R - 0.419G - 0.081B + 128

BT.709(HD 常用):

txt
Y  =  0.2126R + 0.7152G + 0.0722B
Cb = -0.1146R - 0.3854G + 0.5000B + 128
Cr =  0.5000R - 0.4542G - 0.0458B + 128

系数不同、还有 16~235 的“电视范围”和 0~255 的“全范围”之分,细节见《LV015-YUV与RGB转换》。

4.3 此时还是 YUV444 ​

转完还是每个像素一个 Y + 一个 U + 一个 V,也就是 YUV444,每像素 3 字节,和 RGB 一样大。到这一步只是换了坐标系,一点空间都没省。

5. 色度下采样成 4:2:0 ​

04-yuv420-downsample

4:2:0 的含义、以及为什么是“2x2 的 4 个像素共用一组 UV”,在《LV020-YUV的格式》里详细讲过,这里一句话带过:U、V 在水平和垂直方向上都按 2:1 采样,亮度保持全采样。

txt
单个像素占用 = 1Byte(Y) + 1Byte(U)/4 + 1Byte(V)/4 = 1.5Byte

1920x1080 一帧:
2073600 x 1.5 = 3110400 Byte = 3037.5 KB ≈ 2.97 MB

到这一步,“3 字节/像素”变成了“1.5 字节/像素”,相比 RGB888 正好省一半,这也是 YUV420 成为相机、视频、播放器默认格式的原因。

6. 写进文件是什么样 ​

6.1 YUV 文件里没有头信息 ​

和 BMP、JPEG 不一样,YUV 文件没有任何头和元数据:没有宽高、没有采样格式、没有位深,只有一大坨裸像素。所以拿到一个 .yuv 文件,必须外部约定好“1920x1080 + I420”这样的信息才能正确显示(比如用 7yuv 打开时要手选格式)。

芯片之间传递时不是这样:buffer 一般自带描述信息(宽、高、stride、格式),落成“纯裸数据文件”之后这些就丢了。

6.2 I420 / NV12 / NV21 的排布 ​

05-file-layout

以 8x8 的图为例(Y 平面 8x8,U、V 平面各 4x4):

格式排布8x8 图的字节数1920x1080 的字节数
I420(YUV420P)Y → U → V 三段分开64 + 16 + 16 = 963110400
YV12Y → V → U 三段分开64 + 16 + 16 = 963110400
NV12(YUV420SP)Y 一段,UV 交织一段(UVUV…)64 + 32 = 963110400
NV21Y 一段,VU 交织一段(VUVU…)64 + 32 = 963110400

1920x1080 的拆解:

txt
Y 平面:1920 x 1080 = 2073600 Byte
U 平面:960 x 540   = 518400  Byte
V 平面:960 x 540   = 518400  Byte
合计  :3110400 Byte = 3037.5 KB ≈ 2.97 MB

补充一点:硬件里为了内存对齐,Y 的行宽(stride)常会补齐到 16 / 32 / 64 的整数倍,此时 buffer(以及按 buffer 直接 dump 出来的文件)会比上面的理论值大一些。

6.3 一帧到底多大 ​

06-size-compare

格式每像素占用1920x1080 一帧
Bayer RAW10(打包)1.25 字节2.47 MB
Bayer RAW(解包后按 16bit)2 字节3.96 MB
YUV420(8bit)1.5 字节2.97 MB
YUV422(8bit)2 字节3.96 MB
RGB888 / YUV444(8bit)3 字节5.93 MB
YUV420(10bit,P010)2 字节5.93 MB

(P010 是把 10bit 的数放进 16bit 容器里存,所以又把体积拉回去了)

两个结论:

(1)YUV420(8bit)比 10bit 打包的 Bayer RAW 还大。色度下采样省下的是相对 RGB / YUV444 的体积,不是相对 RAW 的体积——RAW 才是这条链路上最“瘦”的形态,因为每个像素只有一个分量。

(2)到这一步为止都还是“一帧 2.97MB”的量级,真正把体积压下去的是后面的视频编码(H.264 / H.265 等),那属于视频编解码章节的内容。

三、 各步骤对应的 ISP 硬件模块 ​

前面几节讲的是“做了什么”,这一节补上“由谁来做”。以 RK3568 的 ISP 为例,按 TRM 的划分,ISP 内部依次分四段:RAW Processing → RGB Processing → YUV Processing → Memory Interface(MI)(《Rockchip_RK3568_TRM_Part2_V1.1-20210301.pdf》P.982)。

1. 链路步骤 → ISP 模块对照 ​

通用步骤RK3568 模块说明
黑电平校正 BLCBLS(Black-level-subtraction)从像素里减掉一个 DC 偏置;自动模式读传感器遮蔽像素求均值
镜头阴影校正 LSCLSC每个像素乘各自的校正系数,系数在 sector 角点之间做双线性插值
坏点校正 DPCDPCC静态 + 动态坏点/坏点簇校正,最大 3x3,静态表可存 2048 个位置
绿平衡GICGreen Imbalance Correction,修正绿通道不平衡
去噪 NRRAW 域 BAYNR(空域)/ BAY3D(时域);YUV 域 2DNR(YNR + CNR)RAW 域分空域、时域两套,YUV 域再分亮度、色度
白平衡 AWBRAWAWB(统计)+ AWB_GAIN(增益)统计在 RAW 域产生,增益按 R/Gr/Gb/B 四通道乘回
去马赛克 DemosaicDEBAYER插值 + 滤波输出 RGB;黑白模式下三通道输出同值
色彩校正 CCMCCM3x3 颜色校正矩阵
GammaRAW 域 GAMMA_IN、RGB 域 GAMMA_OUT输入侧贴合 sensor 特性,输出侧贴合人眼感知
色彩空间转换 RGB→YUVCSMColor Space Matrix
色度下采样到 4:2:0CONV422 + SELF_RESIZE / SCL先做 444→422,再由 Resize 完成 422→420

表内模块的功能见《Rockchip_RK3568_TRM_Part2_V1.1-20210301.pdf》P.986–989,模块基地址见同文档 P.991 的 Table 12-2。

2. ISP 内部按域分组 ​

【RAW Processing(RAW / Bayer 域)】

  • FPN:固定模式噪声去除,实现在 CSI2RX(MIPI) 里
  • BLS、DPCC、GAMMA_IN、LSC、GIC
  • HDRMGE(多帧合成)、HDRTMO(DRC 宽动态压缩)
  • BAYNR(空域降噪)、BAY3D(时域降噪)
  • AWB_GAIN,以及 3A 统计模块 RAWAWB、RAWAE_BIG / RAWAE_LITE、RAWAF、RAWHIST_BIG / RAWHIST_LITE、PDAF

【RGB Processing】

  • DEBAYER、CCM、GAMMA_OUT、DEHAZE_ENHANCE、3DLUT、LDCH

【YUV Processing】

  • 2DNR(YNR + CNR)、SHARP、CSM、CONV422、SCL / SELF_RESIZE

【Memory Interface(MI)】

  • 负责主路图像、MIPI、DBR、LUT 各通道与 DDR 之间的读写

—— 《Rockchip_RK3568_TRM_Part2_V1.1-20210301.pdf》P.982–991

3. 三个容易忽略的点 ​

(1)3A 的统计在 RAW 域完成:RAWAWB、RAWAE_*、RAWHIST_* 吃的都是 raw 数据,硬件只负责“测量”,软件拿到统计量后算出增益、曝光、白平衡参数,再回填给 AWB_GAIN 等模块去“应用”。

(2)Gamma 有两个:RAW 域一个 GAMMA_IN,RGB 域一个 GAMMA_OUT,位置不同、用途不同;平时说的“Gamma 校正”通常指输出侧的 GAMMA_OUT。

(3)4:2:0 是最后一步才做的:CSM 先把 RGB 变成 YUV444,CONV422 再降到 YUV422;要输出 4:2:0 还得靠 SELF_RESIZE / SCL(TRM 明确写了 Resize 支持 4:2:2 ↔ 4:2:0 的相互转换)。所以 ISP 默认输出多是 YUV422,YUV420 由 Resize 或后级模块决定。

四、 总结 ​

把整条链路串起来:

txt
sensor:I2C/CCI 配置 + MCLK 时钟,逐行读出(光电转换 → 模拟增益 → ADC → Bayer RAW,每像素 1 个分量,10bit)
   ↓ DVP 并口,或 MIPI CSI-2(差分,经 D-PHY)
SoC:D-PHY → CSI2RX 还原成像素,交给 ISP(或经 VICAP 直存 DDR)
   ↓ 解包 + 校正(黑电平、镜头阴影、坏点、去噪、白平衡)
仍是 Bayer,1 个分量(按 16bit 存放,2 字节/像素)
   ↓ 去马赛克(插值补齐)
RGB,3 个分量(8bit x 3,3 字节/像素)   ← 数据量变大的一步
   ↓ 色彩空间转换
YUV444,3 个分量(3 字节/像素)
   ↓ 色度下采样(U、V 各 2:1)
YUV420,1.5 字节/像素                    ← 数据量变小的一步
   ↓ 按 I420 / NV12 排布
裸数据文件(无头信息,1920x1080 一帧 2.97MB)

最后留三个容易搞混的点:

  • 数据量变大发生在去马赛克(1 个分量 → 3 个分量),变小发生在色度下采样(3 字节 → 1.5 字节)。
  • YUV420 之所以看着不糊,是因为色度下采样只降低了“颜色”的精度,亮度 Y 一点没降,而人眼对亮度最敏感。
  • 相机不一定都给 RAW:带 ISP 的 sensor(很多手机 sensor、USB 摄像头)直接输出 YUV422 / YUV420,此时拿到手的可能就已经是 NV12 / NV21 了,本文前半段那条“RAW → ISP → RGB”的路径在 sensor 内部已经走完了。

本文档由 markdowncli 技能辅助生成