YUV格式总结
格式说明
YUV,是一种颜色编码方法。常使用在各个视频处理组件中。 YUV在对照片或视频编码时,考虑到人类的感知能力,允许降低色度的带宽。
YUV是编译true-color颜色空间(color space)的种类,Y’UV, YUV, YCbCr,YPbPr等专有名词都可以称为YUV,彼此有重叠。
YUV 分别由一个称为 Y(相当于灰度)的“亮度”分量(Luminance or Luma)和两个称为 U(蓝色投影 Cb)和 V(红色投影 Cr)的“色度”分量(Chrominance or Chroma)表示,由此得名。交换UV后会发现图片中的红色和蓝色被交换了。
仅有 Y 分量而没有 UV 分量信息,一样可以显示完整的黑白(灰度)图像,解决了模拟信号电视黑白与彩色的兼容问题。
常用分类方法
根据三个分量Y、U、V采样率不同,常常可以分成:yuv444、yuv422、yuv420、yuv411。
- yuv444 表示不降低色度(UV)通道的采样率。每个 Y 分量对应一组 UV 分量。
- yuv422 表示 2:1 水平下采样,没有垂直下采样。每两个 Y 分量共享一组 UV 分量。
- yuv420 表示 2:1 水平下采样,同时 2:1 垂直下采样。每四个 Y 分量共享一组 UV 分量。
- yuv411 表示 4:1 水平下采样,没有垂直下采样。每四个 Y 分量共享一组 UV 分量。
存储格式
YUV格式的存储格式分三种:
- Packed打包格式:yuv分量交错存储
- Plannar平面格式:yuv分量分三个平面存储
- Semi-Planar半平面格式:y分量uv分量分两个平面,uv平面交错存储
存储分布图
看完分布很好理解三种存储格式,常用的YUV存储分布图如下:
YUV420plannar
图片大小与宽高关系:size = widthxheighx1.5
I420也叫YU12或IYUV
/*I420内存分布
w
+--------------------+
|Y0Y1Y2Y3... |
|... | h
|... |
| |
+--------------------+
|U0U1 |
|... | h/2
+----------+
|V0V1 |
|... | h/2
+----------+
w/2
*/
YV12, UV的排布和I420相反
/*
YV12内存分布 w
+--------------------+
|Y0Y1Y2Y3... |
|... | h
|... |
| |
+--------------------+
|V0V1 |
|... | h/2
+----------+
|U0U1 |
|... | h/2
+----------+
w/2
*/
YUV420sp
图片大小与宽高关系:size = widthxheighx1.5
NV12和NV21是半平面存储格式,二者区别只是U、V所在平面相反
/*NV12内存分布
w
+--------------------+
|Y0Y1Y2Y3... |
|... | h
|... |
| |
+--------------------+
|U0V0U1V1 |
|... | h/2
+--------------------+
*/
/*
NV21内存分布 w
+--------------------+
|Y0Y1Y2Y3... |
|... | h
|... |
| |
+--------------------+
|V0U0V1U1 |
|... | h/2
+--------------------+
*/
格式转换
YUYV转I420
void YUYVConvertI420(int width,int hight,char *pYUYV,char *pI420)
{
char *u = pI420+hight*width;
char *v = u+hight*width/4;
int i=0,j=0;
for (i = 0; i <hight/2;i++) {
char *src_l1=(pYUYV+width*2*2*i);
char *src_l2=(src_l1+width*2);
char *y_l1=pI420+width*2*i;
char *y_l2=y_l1+width;
for(j=0;j<width/2;j++)
{
*y_l1++=src_l1[0];
*u++=src_l1[1];
*y_l1++=src_l1[2];
*v++=src_l1[3];
*y_l2++=src_l2[0];
*y_l2++=src_l2[2];
src_l1+=4;
src_l2+=4;
}
}
}
I420转NV12
void I420ConvertNV12(unsigned char* pI420, unsigned char* pNV12, int width, int height)
{
int i, j;
int y_size = width * height;
unsigned char* y = pI420;
unsigned char* u = pI420 + y_size;
unsigned char* v = pI420 + y_size * 5 / 4;
unsigned char* y_tmp = pNV12;
unsigned char* uv_tmp = pNV12 + y_size;
// y
memcpy(y_tmp, y, y_size);
// u
for (j = 0, i = 0; j < y_size/2; j+=2, i++)
{
// 此处可以调整UV位置,根据需要调整为NV12或NV21
uv_tmp[j] = u[i];
uv_tmp[j+1] = v[i];
// 下面是转NV21的
// uv_tmp[j] = v[i];
// uv_tmp[j+1] = u[i];
}
}
NV21转NV12
void NV21ConvertNV12(int width,int height,char *pNV21,char *pNV12)
{
memcpy(pNV12, pNV21, width*height);//y分量
int i;
for(i=0; i<width*height/2; i++){
memcpy(pNV12+width*height+i+1, pNV21+width*height+i, 1);//u分量
memcpy(pNV12+width*height+i, pNV21+width*height+i+1, 1);//v分量
}
}
图像裁剪
NV12的裁剪处理
// >>替代/运算,提高转换效率
void NV12CropHandle(unsigned char *tarYuv, unsigned char *srcYuv, int startW, int startH, int cutW, int cutH, int srcW, int srcH)
{
int i;
int j = 0;
int k = 0;
unsigned char *tmpY = tarYuv;
unsigned char *tmpUV = tarYuv + cutW*cutH;
unsigned int limitY = cutH+startH;
unsigned int limitUV = (cutH+startH)>>1;
for(i=startH; i<limitY; i++) {
// 逐行拷贝Y分量,共拷贝cutW*cutH
memcpy(tmpY+j*cutW, srcYuv+startW+i*srcW, cutW);
j++;
}
for(i=(startH>>1); i<limitUV; i++) {
//逐行拷贝UV分量,共拷贝cutW*cutH/2
memcpy(tmpUV+k*cutW, srcYuv+startW+srcW*srcH+i*srcW, cutW);
k++;
}
}