YUV色彩格式总结(转)

YUV色彩格式总结(转)

YUV起源

常见的颜色模型中,RGB主要用于电子系统里表达和显示颜色,CMYK印刷四色模式用于彩色印刷,而YUV是被欧洲电视系统所采用的一种颜色编码方法。

Y'UV的发明是由于彩色电视与黑白电视的过渡时期。黑白视讯只有Y(Luma,Luminance)视讯,也就是灰阶值。到了彩色电视规格的制定,是以YUV/YIQ的格式来处理彩色电视图像,把UV视作表示彩度的C(Chrominance或Chroma),如果忽略C信号,那么剩下的Y(Luma)信号就跟之前的黑白电视信号相同,这样一来便解决彩色电视机与黑白电视机的相容问题。Y'UV最大的优点在于只需占用极少的带宽。

因为UV分别代表不同颜色信号,所以直接使用R与B信号表示色度的UV。 也就是说UV信号告诉了电视要偏移某象素的的颜色,而不改变其亮度。 或者UV信号告诉了显示器使得某个颜色亮度依某个基准偏移。 UV的值越高,代表该像素会有更饱和的颜色。

彩色图像记录的格式,常见的有RGB、YUV、CMYK等。 彩色电视最早的构想是使用RGB三原色来同时传输。这种设计方式是原来黑白带宽的3倍,在当时并不是很好的设计。RGB诉求于人眼对色彩的感应,YUV则着重于视觉对于亮度的敏感程度,Y代表的是亮度,UV代表的是彩度(因此黑白电影可省略UV,相近于RGB),分别用Cr和Cb来表示,因此YUV的记录通常以Y:UV的格式呈现。

使用YUV的优点有两个:

一.YUV主要用于优化彩色视频信号的传输,使其向后兼容老式黑白电视,这一特性用在于电视信号上。

二.YUV是数据总尺寸小于RGB格式(但用YUV444的话,和RGB888一样都是24bits)

彩色,Y分量,V分量,U分量

YUV色彩格式总结

本文主要介绍YUV的3种采样,YUV444,YUV422, YUV420,以及后两种格式转BGR的方法,和BGR转YUV系列的方法。本系列介绍的公式都是结合OpenCV根据OpenCV的计算方法提供的。

YUV格式的采样方式

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

4:4:4表示完全取样。

4:2:2表示2:1的水平取样,垂直完全采样。

4:2:0表示2:1的水平取样,垂直2:1采样。

4:1:1表示4:1的水平取样,垂直完全采样。

YUV格式有3中采样方式,分别是YUV444、YUV422、YUV420;其中YUV444也就是我们通常意义上的YUV,YUV420就是平时使用的NV21和NV12,其中NV12和NV21仅仅是存储顺序的差异。YUV422平时使用的相对较少。上篇文章中介绍过BGR转YUV,我们知道每一组BGR都会获得一组YUV,所以YUV444就是原始的YUV,是不经过采样的。

YUV444

上一篇文章介绍了BGR转YUV444,每一组BGR转换为一组YUV;转换公式如下:

Y = (4899 * R + 9617 * G + 1868 * B) >> 14;

V = ((R - Y) * 14369 + delta) >> 14;

U = ((B - Y) * 8061 + delta) >> 14;

delta = (255 / 2 + 1) * (1 << 14);

YUV444是BGR直接转换,不进行采样的结果;而YUV422以及YUV420是在YUV444的基础上进行采样得到的。如下图所示,展示了YUV444的一种演示方式:

其中实心黑圈作为整体表示UV分量,空心圈表示Y分量;所以每一个Y拥有一组UV分量。需要注意的是,这仅仅是示意图,表示采样方式,不表示数据的真是存储方式。444可以理解为第一行Y和UV的比是4(第一个4):4(第二个4);第二行Y和UV的比是4(第一个4):4(第三个4);因此使用YUV444表示这种采样方式。这也表示水平采样是4:4;垂直采样是4:4.

YUV422

BGR转YUV422的的公式是一样的,只是对YUV444进行采样,便可以得到YUV422. YUV422的表示如下图所示:

可以看到,第一行Y与UV的比例是4:2(第一个2);第二行也是4:2(第二个2);也可以理解为,水平方向上的采样比例为4:2;垂直方向是也为4:2.以上是YUV422的采样方式。所以在计算的时候,就可以少计算一半的UV分量;数据量也少一半的UV分量,也就是说,YUV422的数据量只有YUV444的2/3.

YUV420

YUV420的表示方式如下图所示:

可以看到,每4个Y拥有一组UV;第一行的采样是4:2;第二行的采样是4:0;所以取名YUV420.但是YUV420又有NV21和NV12两种格式,这两种格式的区别,仅仅是UV分量存储方式上的区别。同时,在数据量上,YUV420仅仅是YUV444的1/2.以上是关于YUV的3种格式的采样方式的介绍,下面会介绍这YUV格式数据的存储方式。

YUV格式的存储方式

YUV与YCrCb

YUV444的存储比较单一,Y单独存储,UV交叉存储,这里主要区分一下YUV444和YCrCb;YCrCb和YUV的区别在两方面:

计算系数

存储顺序

下面是RGB转YUV的代码

{

typedef _Tp channel_type;

RGB2YCrCb_i(int _srccn, int _blueIdx, bool _isCrCb)

: srccn(_srccn), blueIdx(_blueIdx), isCrCb(_isCrCb)

{

//设置系数

static const int coeffs_crb[] = { R2Y, G2Y, B2Y, YCRI, YCBI };

static const int coeffs_yuv[] = { R2Y, G2Y, B2Y, R2VI, B2UI };

//yuv和YCrCb的系数不同

memcpy(coeffs, isCrCb ? coeffs_crb : coeffs_yuv, 5*sizeof(coeffs[0]));

//RGB和BGR的区别,需要交换B分量和R分量的位置

if(blueIdx==0) std::swap(coeffs[0], coeffs[2]);

}

void operator()(const _Tp* src, _Tp* dst, int n) const

{

int scn = srccn, bidx = blueIdx;

//区分是YUV还是YCrCb

int yuvOrder = !isCrCb; //1 if YUV, 0 if YCrCb

int C0 = coeffs[0], C1 = coeffs[1], C2 = coeffs[2], C3 = coeffs[3], C4 = coeffs[4];

//color.hpp +26 : yuv_shift = 14

int delta = ColorChannel<_Tp>::half()*(1 << yuv_shift);

n *= 3;

for(int i = 0; i < n; i += 3, src += scn)

{

int Y = CV_DESCALE(src[0]*C0 + src[1]*C1 + src[2]*C2, yuv_shift);

int Cr = CV_DESCALE((src[bidx^2] - Y)*C3 + delta, yuv_shift);

int Cb = CV_DESCALE((src[bidx] - Y)*C4 + delta, yuv_shift);

dst[i] = saturate_cast<_Tp>(Y);

//YUV和YCrCb计算系数不同

dst[i+1+yuvOrder] = saturate_cast<_Tp>(Cr);

dst[i+2-yuvOrder] = saturate_cast<_Tp>(Cb);

}

}

int srccn, blueIdx;

bool isCrCb;

int coeffs[5];

};

具体区别在代码中注释了,首先看计算公式:

Y = (4899 * R + 9617 * G + 1868 * B) >> 14;

Cr = ((R - Y) * 11682 + delta) >> 14;

Cb = ((B - Y) * 9241 + delta) >> 14;

delta = (255 / 2 + 1) * (1 << 14);

存储顺序:

dst[i+1+yuvOrder] = saturate_cast<_Tp>(Cr);

dst[i+2-yuvOrder] = saturate_cast<_Tp>(Cb);

可以看到,YCrCb刚好对应YVU,所以仅仅是UV分量的存储顺序有区别;

YUV420

YUV格式的存储方式有很多,YUV格式的数据存储分为two-plane和three-plane两种方式;所谓的two-plane是指Y单独存储一个plane,UV交叉存储,占用一个plane;three-plane是Y U V分别占用一个plane,一共三个plane.three-plane一般叫做YUV420p,two-plane叫做YUV420sp,我们熟知的NV21和NV12便是YUV420sp。下面是OpenCV种RGB转YUV420的代码,其中有两个标志位interleaved和swapUV,分别用于区分YUV420p和YUV420sp以及NV21和NV12;NV21的存储是VU,而NV12是UV顺序。

struct RGB888toYUV420pInvoker: public ParallelLoopBody

{

RGB888toYUV420pInvoker(const uchar * _src_data, size_t _src_step,

uchar * _y_data, uchar * _uv_data, size_t _dst_step,

int _src_width, int _src_height, int _scn, bool swapBlue_, bool swapUV_, bool interleaved_)

: src_data(_src_data), src_step(_src_step),

y_data(_y_data), uv_data(_uv_data), dst_step(_dst_step),

src_width(_src_width), src_height(_src_height),

scn(_scn), swapBlue(swapBlue_), swapUV(swapUV_), interleaved(interleaved_) { }

void operator()(const Range& rowRange) const CV_OVERRIDE

{

const int w = src_width;

const int h = src_height;

const int cn = scn;

for( int i = rowRange.start; i < rowRange.end; i++ )

{

const uchar* brow0 = src_data + src_step * (2 * i);

const uchar* grow0 = brow0 + 1;

const uchar* rrow0 = brow0 + 2;

const uchar* brow1 = src_data + src_step * (2 * i + 1);

const uchar* grow1 = brow1 + 1;

const uchar* rrow1 = brow1 + 2;

if (swapBlue)

{

std::swap(brow0, rrow0);

std::swap(brow1, rrow1);

}

uchar* y = y_data + dst_step * (2*i);

uchar* u;

uchar* v;

//区分two-plane or three-plane

if (interleaved)

{

u = uv_data + dst_step * i;

v = uv_data + dst_step * i + 1;

}

else

{

u = uv_data + dst_step * (i/2) + (i % 2) * (w/2);

v = uv_data + dst_step * ((i + h/2)/2) + ((i + h/2) % 2) * (w/2);

}

//区分NV21 or NV12

if (swapUV)

{

std::swap(u, v);

}

for( int j = 0, k = 0; j < w * cn; j += 2 * cn, k++ )

{

int r00 = rrow0[j]; int g00 = grow0[j]; int b00 = brow0[j];

int r01 = rrow0[cn + j]; int g01 = grow0[cn + j]; int b01 = brow0[cn + j];

int r10 = rrow1[j]; int g10 = grow1[j]; int b10 = brow1[j];

int r11 = rrow1[cn + j]; int g11 = grow1[cn + j]; int b11 = brow1[cn + j];

const int shifted16 = (16 << ITUR_BT_601_SHIFT);

const int halfShift = (1 << (ITUR_BT_601_SHIFT - 1));

int y00 = ITUR_BT_601_CRY * r00 + ITUR_BT_601_CGY * g00 + ITUR_BT_601_CBY * b00 + halfShift + shifted16;

int y01 = ITUR_BT_601_CRY * r01 + ITUR_BT_601_CGY * g01 + ITUR_BT_601_CBY * b01 + halfShift + shifted16;

int y10 = ITUR_BT_601_CRY * r10 + ITUR_BT_601_CGY * g10 + ITUR_BT_601_CBY * b10 + halfShift + shifted16;

int y11 = ITUR_BT_601_CRY * r11 + ITUR_BT_601_CGY * g11 + ITUR_BT_601_CBY * b11 + halfShift + shifted16;

y[2*k + 0] = saturate_cast(y00 >> ITUR_BT_601_SHIFT);

y[2*k + 1] = saturate_cast(y01 >> ITUR_BT_601_SHIFT);

y[2*k + dst_step + 0] = saturate_cast(y10 >> ITUR_BT_601_SHIFT);

y[2*k + dst_step + 1] = saturate_cast(y11 >> ITUR_BT_601_SHIFT);

const int shifted128 = (128 << ITUR_BT_601_SHIFT);

int u00 = ITUR_BT_601_CRU * r00 + ITUR_BT_601_CGU * g00 + ITUR_BT_601_CBU * b00 + halfShift + shifted128;

int v00 = ITUR_BT_601_CBU * r00 + ITUR_BT_601_CGV * g00 + ITUR_BT_601_CBV * b00 + halfShift + shifted128;

if (interleaved)

{

u[k*2] = saturate_cast(u00 >> ITUR_BT_601_SHIFT);

v[k*2] = saturate_cast(v00 >> ITUR_BT_601_SHIFT);

}

else

{

u[k] = saturate_cast(u00 >> ITUR_BT_601_SHIFT);

v[k] = saturate_cast(v00 >> ITUR_BT_601_SHIFT);

}

}

}

}

}

BGR转YUV420的转换公式为:

Y = (R * 269484 + G * 528482 + B * 102760 + (1 << 19) + (1 << 16)) >> 20;

U = (R * (-155188) + G * (-305135) + B * 460324 + (1 << 19) + (128 << 20)) >> 20;

V = (R * 460324 + G * (-385875) + B * (-74448) + (1 << 19) + (128 << 20)) >> 20;

另外需要注意的是,YUV420在计算过程中是需要采样的,每4个Y共同使用一组UV,而这组UV则是取的2x2左上角的点——(0,0);代码如下:

int u00 = ITUR_BT_601_CRU * r00 + ITUR_BT_601_CGU * g00 + ITUR_BT_601_CBU * b00 + halfShift + shifted128;

int v00 = ITUR_BT_601_CBU * r00 + ITUR_BT_601_CGV * g00 + ITUR_BT_601_CBV * b00 + halfShift + shifted128;

可以看到OpenCV在计算的时候取用的是(0,0)位置的点。在别的代码中也可能采取其他的采样方式,比如水平方向上对U采样,垂直方向上对V采样,等等;另外关于转换系数,根据精度不同,系数也会有出入。表现在移动位数不同,比如OpenCV中,目前移动的位数是20;上一篇文章中介绍BGR转YUV,移动的位数是14;所以在自定义的实现中,可以根据对精度的需求进行修改,当然如果移动位数变少,精度也会下降。

转自:

https://zhuanlan.zhihu.com/p/51394272

https://www.jianshu.com/p/3e44c2262775

https://www.cnblogs.com/linhaostudy/p/11276519.html

相关文章

耳朵进水了怎么办?这些科学处理方法请收好
王者荣耀全皮肤多少钱(全皮肤440款售价总和)
win11为什么下载不了劲舞团?下载教程在哪里?
著名演员曾舜晞的经纪公司
365安卓版

著名演员曾舜晞的经纪公司

08-03 阅读: 7903
逆水寒战场攻略干货分享
365安卓版

逆水寒战场攻略干货分享

07-01 阅读: 9680
德国对秘鲁 交手统计数据
365安卓版

德国对秘鲁 交手统计数据

08-10 阅读: 3194