本文是isp算法学习的第十三篇章,主要是讲述CSC(color space convert)。

0CSC简介

CSC-coloer space convert,也有的地方叫CSM(color space matrix),通过一些线性变化,将原本图像的颜色空间转换到其他的颜色空间, 常见的有RGB2YUV,RGB2SV等等,MATLAB文档中对CSC的定义,可以点击这里

matlab定义

Color Space Conversion(在颜色空间之间转换颜色信息)

颜色空间转换块在颜色空间之间转换颜色信息。使用“转换”参数指定要在两者之间进行转换的颜色空间。

  • R'G'B' to Y'CbCr
  • Y'CbCr to R'G'B'
  • R'G'B' to intensity
  • R'G'B' to HSV
  • HSV to R'G'B'
  • sR'G'B' to XYZ
  • XYZ to sR'G'B'
  • sR'G'B' to L*a*b*
  • L*a*b* to sR'G'B'

1YUV简介

YUV中Y表示亮度信号,UV表示色度信号也就是色差信息,通常查资料还会出现YCrCb这种信号,其实YCrCb是数字信号时代定义的一种色差信号,是通过YUV加上一定程度的offset得到的,使得色差数据都大于0,大多数情况下已经不对二者进行区分了,现在提到的YUV其实都是指的YCrCb格式,只是习惯原因通常还是会直接说是YUV。

YUV格式有很多种类,如下图是微软WindowsAPI文档中对YUV的一些宏定义

图中的那些数据格式都是YUV格式,而它们各自的区别主要就是采样比和信号的排列循序。

1.1存储格式

  • planar:先存储 Y,然后 U,然后 V
  • packed:yuv 交叉存储。

常见格式

yuv444: packet 采样(yuv yuv yuv)和 planar 采样(yyyy uuuu vvvv

yuv422:packet 采样

  • yuvy:YUYV YUYV
  • uyvy:UYVY UYVY

yuv422p:planar采样:YYYY UU VV

yuv420:packet采样: YUV Y YUV Y

yuv420p:planar采样

1.2采样格式

最常见的采样格式如下所示

  • YUV444:完全采样,即每一个Y信号对应的UV信号都采样,没有损失任何信号;
  • YUV422:两个Y信号公用一个UV分量,及4个有分量对应2个U和2个V分量,色度信号损失一部分从而减小数据量;
  • YUV420:四个Y分量公用一个UV分量,在422的基础上进一步降低数据量。

为节省带宽起见,大多数 YUV 格式平均使用的每像素位数都少于 24 位。主要的采样(subsample)格式有 YCbCr4:2:0、YCbCr4:2:2、YCbCr4:1:1 和 YCbCr4:4:4。YUV 的表示法称为 A:B:C 表示法。

  • A:一块A*2个像素的概念区域,一般都是4
  • B:第1行的色度采样数目
  • C:第2行的色度采样数目。C的值一般要么等于B,要么等于0
色度二次采样 色度二次采样

1.2.1原图

下面我们以表格来说明,表格中,每一格代表一个像素

行列 1 2 3 4
1 Y0 U0 V0 Y1 U1 V1 Y2 U2 V2 Y3 U3 V3
2 Y4 U4 V4 Y5 U5 V5 Y6 U6 V6 Y7 U7 V7
3 Y8 U8 V8 Y9 U9 V9 Y10 U10 V10 Y11 U11 V11
4 Y12 U12 V12 Y13 U13 V13 Y14 U14 V14 Y15 U15 V15

1.2.2 4:4:4采样(YUV444(I444、YV24))

  • 每1个像素都有自己独立的1组CbCr分量
  • 1个像素占用24bit(3字节),跟RGB24的体积一样
  • 这种格式是没有进行色度二次采样的

采样后上面的采样是无损采样,和原图一模一样。全是YUV

行列 1 2 3 4
1 Y0 U0 V0 Y1 U1 V1 Y2 U2 V2 Y3 U3 V3
2 Y4 U4 V4 Y5 U5 V5 Y6 U6 V6 Y7 U7 V7
3 Y8 U8 V8 Y9 U9 V9 Y10 U10 V10 Y11 U11 V11
4 Y12 U12 V12 Y13 U13 V13 Y14 U14 V14 Y15 U15 V15

1.2.2.1按存储格式分类

4:4:4 Planar I444(yuv444p)
4:4:4 Planar YV24
4:4:4 Semi-Planar NV24
4:4:4 Semi-Planar NV42

1.2.2.2 I444、YV24

I444、YV24三个分量均为平面格式,共三个平面,即:先连续存储 Y,然后连续存储 U,最后连续存储 V

不同点在于 U V 的排列顺序,P 为 Planar 的缩写

  • I444:先是 w * h 长度的 Y,后面跟 w * h 长度的 U, 最后是 w * h 长度的 V,总长度为 w * h * 3

    I444: YYYYYYYY UUUUUUUU VVVVVVVV => YUV420P

  • YV24:先是 w * h 长度的 Y,后面跟 w * h 长度的 V, 最后是 w * h 长度的 U,总长度为 w * h * 3

    YV24: YYYYYYYY VVVVVVVV UUUUUUUU => YUV420P

1.2.3 4:2:2采样(YUV422(NV16、NV61、I422、YV16、YUVY、VYUY、UYVY))

  • 每采样过一个像素点,都会采样其 Y 分量,而 U、V 分量就会间隔一个采集一个。

  • 1个像素平均占用16bit(2字节)

  • 因为2个像素共占用32bit(4字节 = 2个Y分量 + 1个Cb分量 + 1个Cr分量)

  • 还原时,水平方向相邻的2个像素(1行2列)共用1组CbCr分量

采样后评:上面的4:2:2采样**,水平方向Y:U:V 是4:2:2,**垂直方向4:4:0**。所以我选择水平比例是科学的!**)

行列 1 2 3 4
1 Y0 U0 - Y1 - V1 Y2 U2 - Y3 - V3
2 Y4 U4 - Y5 - V5 Y6 U6 - Y7 - V7
3 Y8 U8 - Y9 - V9 Y10 U10 - Y11 - V11
4 Y12 U12 - Y13 - V13 Y14 U14 - Y15 - V15

1.2.3.1按存储格式分类

4:2:2 Planar I422(yuv422sp)
4:2:2 Planar YV16
4:2:2 Semi-Planar NV16
4:2:2 Semi-Planar NV61
4:2:2 Packed UYVY(Y422,UYNV)
4:2:2 Packed YUYV(YUY2,V422,YUNV)
4:2:2 Packed YVYU

1.2.3.2 NV16、NV61

NV16、NV61 的存储格式为 Y 平面,UV 打包一个平面,共两个平面,即:先连续存储 Y,然后连续交叉存储 UV

不同点在于 UV 的排列顺序,SP 为 Semi-Planar 的缩写

  • NV16:先是 w * h 长度的 Y,后面跟 w * h 长度的 UV(交叉存储),总长度为 w * h * 2

    NV16: YYYYYYYY UVUVUVUV => YUV422SP

  • NV61:先是 w * h 长度的 Y,后面跟 w * h 长度的 VU(交叉存储),总长度为 w * h * 2

    NV61: YYYYYYYY VUVUVUVU => YUV422SP

1.2.3.3 I422、YV16

I422、YV16 三个分量均为平面格式,共三个平面,即:先连续存储 Y,然后连续存储 U,最后连续存储 V

不同点在于 U V 的排列顺序,P 为 Planar 的缩写

  • I420:先是 w * h 长度的 Y,后面跟 w * h * 0.5 长度的 U, 最后是 w * h * 0.5 长度的 V,总长度为 w * h * 2

    I422: YYYYYYYY UUUU VVVV => YUV422P

  • YV12:先是 w * h 长度的 Y,后面跟 w * h * 0.5 长度的 V, 最后是 w * h * 0.5 长度的 U,总长度为 w * h * 2

    YV16: YYYYYYYY VVVV UUUU => YUV422P

1.2.3.4 YUVY、VYUY、UYVY

YUVY、VYUY、UYVY 为打包格式:每个像素点的 Y,U,V 是连续交叉存储

  • YUVY:在 Packed 内部,YUV 的排列顺序是 YUVY,两个 Y 共用一组 UV

    YUVY: YUVY YUVY YUVY YUVY => YUV422

  • VYUY:在 Packed 内部,YUV 的排列顺序是 VYUY,两个 Y 共用一组 UV

    VYUY: VYUY VYUY VYUY VYUY => YUV422

  • UYVY:在 Packed 内部,YUV 的排列顺序是 UYVY,两个 Y 共用一组 UV

    UYVY: UYVY UYVY UYVY UYVY => YUV422

一个1920*1080的yuv422文件用YUVViewer打开之后,局部放大图:

1.2.4 4:2:0采样(YUV420(NV12、NV21、I420、YV12))

  • 每采样过一个像素点,都会采样其 Y 分量,而 U、V 分量就会间隔一行按照 2 : 1 进行采样。

  • 1个像素平均占用12bit(1.5字节)

  • 因为4个像素共占用48bit(6字节 = 4个Y分量 + 1个Cb分量 + 1个Cr分量)

  • 还原时,相邻的4个像素(2行2列)共用1组CbCr分量

采样后上面的4:2:0采样**,水平方向第二行里面四个像素的Y:U:V比例 是4:2:0,**垂直方向Y:U:V是4:2:2**。所以我选择水平比例是科学的!**)

行列 1 2 3 4
1 Y0 U0 - Y1 - - Y2 U2 - Y3 - -
2 Y4 - V4 Y5 - - Y6 - V6 Y7 - -
3 Y8 U8 - Y9 - - Y10 U10 - Y11 - -
4 Y12 - V12 Y13 - - Y14 - V14 Y15 - -

1.2.4.1按存储格式分类

4:2:0 Planar I420(yuv420p)
4:2:0 Planar YU12
4:2:0 Semi-Planar NV12
4:2:0 Semi-Planar NV21

1.2.4.2 NV12、NV21

NV12、NV21 的存储格式为 Y 平面,UV 打包一个平面,共两个平面,即:先连续存储 Y,然后连续交叉存储 UV

不同点在于 UV 的排列顺序,SP 为 Semi-Planar 的缩写

  • NV12:先是 w * h 长度的 Y,后面跟 w * h * 0.5 长度的 UV(交叉存储),总长度为 w * h * 1.5

    NV12: YYYYYYYY UVUV => YUV420SP # iOS 平台常用 NV12

  • NV21:先是 w * h 长度的 Y,后面跟 w * h * 0.5 长度的 VU(交叉存储),总长度为 w * h * 1.5

    NV21: YYYYYYYY VUVU => YUV420SP # Android 平台常用 NV21

1.2.4.3 I420、YV12

I420、YV12 三个分量均为平面格式,共三个平面,即:先连续存储 Y,然后连续存储 U,最后连续存储 V

不同点在于 U V 的排列顺序,P 为 Planar 的缩写

  • I420:先是 w * h 长度的 Y,后面跟 w * h * 0.25 长度的 U, 最后是 w * h * 0.25 长度的 V,总长度为 w * h * 1.5

    I420: YYYYYYYY UU VV => YUV420P # Android 平台常用 I420

  • YV12:先是 w * h 长度的 Y,后面跟 w * h * 0.25 长度的 V, 最后是 w * h * 0.25 长度的 U,总长度为 w * h * 1.5

    YV12: YYYYYYYY VV UU => YUV420P

1.2信号排列分类

从微软文档中可以看到有一个YUY2的格式,其实这种格式又叫YUYV格式是一种422的采样格式,然后还有一种YVYU的格式也是422采样,两者又有什么不同呢,其实就是信号排列不同,比如在内存中YUYV格式存储为Y1U1Y2V1Y3U2Y4V2而YVYU则存储为Y1V1Y2U1Y3V2Y3U2这种方式都是Y和UV交替存储,还有一种常见的存储方式就是先将所有的Y分量存储好,然后再去存储UV分量,当时后续的UV分量的排列又会有不同的变化。这种排列组合方式很多也就带来的不同的格式的出现,这里就不在进一步做介绍了,有兴趣的同学可以自行上网了解。

2转换过程

2.0 计算公式

2.1.1常规转换标准

$$ \begin{cases} Y=0.29882×R+0.58681×G+0.114363×B\\
U=C_b=-0.172485×R-0.338718×G+0.511207×B\\
V=C_r=0.51155×R-0.42811×G-0.08343×B\
\end{cases} $$

2.1.2BT601标准(标清数字电视SDTV)

$$ \begin{cases} Y=0.299×R+0.587×G+0.114×B\\
U=C_b=0.564×(B-Y)\\
V=C_r=0.713×(R-Y)\
\end{cases} $$

2.1.3BT709标准(高清数字电视HDTV)

$$ \begin{cases} Y=0.2126×R+0.7152×G+0.0722×B\\
U=C_b=0.539×(B-Y)\\
V=C_r=0.635×(R-Y)\
\end{cases} $$

2.1.4BT2020(超高清数字电视UHDTV)

$$ \begin{cases} Y=0.2627×R+0.6780×G+0.2593×B\\
U=C_b=0.5315×(B-Y)\\
V=C_r=0.678×(R-Y)\
\end{cases} $$

但是通常在ISP的Pipeline中用到的CSC转换只有RGB2YUV,然后有一些主控(很多没有这个)设计的时候在CCM之后会有一个RGB2HSV的转换,以便进一步通过色度和饱和度两个层面对颜色做进一步的处理。

那么ISP要实现RGB2YUV转换就必定会有一个转换公式,通常在网上查这个公式,可能会查到不同的转换公式,这是因为各个转换公式的的标准不同,通常我们采用BT的标准,也就是国际电信联盟指定的标准,如下图分别罗列了BT601BT709BT2020三个标准,具体在选用的时候根据需要选择一个就行了。

2.1BT601(标清数字电视SDTV)

求出亮度信号 $$ E_Y=0.299E_R+0.587E_G+0.114E_B $$ 那么 $$ (E_R-E_Y)=E_R-0.299E_R-0.587E_G-0.114E_B=0.701E_R-0.587E_G-0.114E_B\\
(E_B-E_Y)=E_B-0.299E_R-0.587E_G-0.114E_B=-0.299E_R-0.587E_G+0.886E_B\
$$ 则 $$ E_{CR}=\frac{E_R-E_Y}{1.402}=\frac{0.701E_R-0.587E_G-0.114E_B}{1.402}\\
E_{CB}=\frac{E_B-E_Y}{1.772}=\frac{-0.299E_R-0.587E_G+0.886E_B}{1.772}\\
$$ 假设亮度信号只占用了220(8位)或877(10位)级,以提供工作范围,并假设黑色位于16.00级,那么量化的亮度信号的十进制Y为 $$ Y=int[(219E_Y+16)×D]/D $$ 其中,D在1或4中任选一个值,分别与8位和10位的量化相对应。操作符int()为0到0.4999范围中的小数部分返回值0…,并为0.5到0.999范围中的小数部分返回值+1…,即它近似大于0.5的小数

同样,假设色差信号只占用255(8位)或879(10位)级,并假设零级位128.00级,那么量化的色差信号的十进制CR和CB为 $$ C_R=int[(224E_{C_R}+128)×D]/D\\
C_B=int[(224E_{C_B}+128)×D]/D\\
$$

2.2BT709(高清数字电视HDTV)

2.2.1基本信号概念性非线性预纠错

$$ \gamma=0.45 $$

2.2.2亮度信号推导

$$ E_Y=0.2126E_R+0.7152E_G+0.0722E_B $$

2.2.3色差信号的推导(模拟编码)

$$ E_{CB}=\frac{E_B-E_Y}{1.8556}=\frac{-0.2126E_R-0.7152E_G+0.9278E_B}{1.8556}\\
E_{CR}=\frac{E_R-E_Y}{1.5748}=\frac{0.7874E_R-0.7152E_G-0.0722E_B}{1.5748}\
$$

2.2.4RGB、亮度和色差信号的量化

$$ D_R=INT[(219E_R+16)·2^{n-8}]\\
D_R=INT[(219E_G+16)·2^{n-8}]\\
D_R=INT[(219E_B+16)·2^{n-8}]\\

D_R=INT[(219E_Y+16)·2^{n-8}]\\
D_R=INT[(219E_{CB}+128)·2^{n-8}]\\
D_R=INT[(219E_{CR}+128)·2^{n-8}]\\
$$

2.2.5通过RGB信号的量化推导亮度和色差信号

$$ D_Y=INT[0.2126D_R+0.7152D_G+0.0722D_B]\\
D_{CB}=INT[(-\frac{0.2126}{1.8556}D_R-\frac{0.7152}{1.8556}D_G+\frac{0.9278}{1.8556})·\frac{224}{219}+2^{n-1}]\\
D_{CR}=INT[(\frac{0.7874}{1.5748}D_R-\frac{0.7152}{1.5748}D_G-\frac{0.0722}{1.5748})·\frac{224}{219}+2^{n-1}]\\
$$

2.3BT2020(超高清数字电视UHDTV)

其中亮度信号为 $$ Y_C=(0.2627R+0.6780G+0.0593B) $$ 色差信号的衍生物 $$ C_B= \begin{cases} \frac{B-Y_C}{-2N_B},&&&N_B\le{B-Y_C}\le0\\
\frac{B-Y_C}{2P_B},&&&0\lt{B-Y_C}\lt{P_B}\
\end{cases} \
C_R= \begin{cases} \frac{R-Y_C}{-2N_R},&&&N_R\le{R-Y_C}\le0\\
\frac{R-Y_C}{2P_R},&&&0\lt{R-Y_C}\lt{P_R}\
\end{cases} $$ 其中 $$ P_B=\alpha(1-0.0593^{0.45})=0.7909854…\\
N_B=\alpha(1-0.9407^{0.45})-1=-0.9701716…\\
P_R=\alpha(1-0.2627^{0.45})=0.4969147…\\
N_R=\alpha(1-0.7373^{0.45})-1=-0.8591209…\
$$ 在实际中,可采取以下数值 $$ P_B=0.7910,N_B=-0.9702\\
P_R=0.4969,N_R=-0.8591 $$ 求得色差信号的衍生物 $$ C_B= \begin{cases} \frac{B-Y_C}{1.9404},&&&-0.9702\le{B-Y_C}\le0\\
\frac{B-Y_C}{1.5820},&&&0\lt{B-Y_C}\lt{0.7910}\
\end{cases} \
C_R= \begin{cases} \frac{R-Y_C}{1.7182},&&&-0.8591\le{R-Y_C}\le0\\
\frac{R-Y_C}{0.9938},&&&0\lt{R-Y_C}\lt{0.4969}\
\end{cases} $$

3转换作用

针对为什么pipeline中需要这么一个转换将RGB转为YUV,个人总结为三点:

  1. YUV是早期欧洲定义的一种信号格式,主要是为了解决黑白电视和彩色电视过渡时期的信号兼容问题,黑白电视只需要亮度值,不需要彩色信号,而彩色电视既需要亮度信号也需要色彩信号,所以如果直接使用RGB就会带来兼容问题,而采用YUV信号,黑白电视不处理彩色信号即可;
  2. 可以将Y和UV分开处理,即将亮度信号色度信号分开处理,这样更符合HVS,因为本专题前面的博文也提到过HVS中人眼对亮度信号更明高,对色度信号相对不明感,那么在去噪等一些处理的时候就可以针对不同层面的信号做不同强度的处理,从而最大程度的保护图像效果;
  3. 为后续的数据压缩做准备,因为通常现在用的多的MJPG和网络传输用的H264和H265等信号都是基于YUV信号的基础上做进一步的数据压缩得来的。

4算法

 1clc;close all;clear;
 2addpath('../publicFunction/')
 3width = 3840;
 4height = 2160;
 5
 6% read raw
 7rawFile = 'images/SSC_raw_long_3840x2160_16_GB_0815103405_[US=16666,AG=1193,DG=1024,R=2231,G=1024,B=2465].raw';
 8%16位的raw图,两个字节存储,会空出6位
 9raw = readRaw(rawFile, 16, width, height);
10figure();
11%缩小到8位
12imshow(uint8(raw/256));
13title('raw');
14
15% read YUV
16YUVFileName = 'images/SSC_ispout_long_3840x2160_16_GB_0815103416_[US=16666,AG=1193,DG=1024,R=2231,G=1024,B=2465].yuv';
17fin = fopen(YUVFileName, 'r');
18%YUV输出也是8位的
19YUV = fread(fin, width*height*2, 'uint8=>uint8');
20Y = YUV(1:2:end);
21U = YUV(2:4:end);
22V = YUV(4:4:end);
23z = reshape(Y, width, height);
24z = z';
25rawData = z;
26figure();
27imshow(z);
28title('Y of YUV');

对比图如下所示,一般YUV可以用YUVplayer打开,或者7YUV都可以