YUV 格式

YUV 格式

Y为0.5时,UV的色彩对应RGB色域

原图与Y、U、V的效果

上图从上到下分别是:原图,Y向量图片,U向量图片,V向量图片。

YUV 格式有两大类:Planar(平面格式) 和 Packed(打包格式)。

  • Planar:先连续存储所有像素点的 Y,紧接着存储所有像素点的 U,随后是所有像素点的 V。相当于将 YUV 拆分成三个平面 (plane) 存储。
  • Packed:每个像素点的 Y, U, V 是连续交替存储的。

YUV 分为三个分量,Y 表示亮度(Luminance),即灰度值;UV 表示色度(Chrominance),描述色彩和饱和度。

与我们熟知的 RGB 类似,YUV 也是一种颜色编码方法,主要用于电视系统以及模拟视频领域,它将亮度信息(Y)与色彩信息(UV)分离,没有 UV 信息一样可以显示完整的图像,只不过是黑白的,这样的设计很好地解决了彩色电视机与黑白电视的兼容问题。并且,YUV 不像 RGB 那样要求三个独立的视频信号同时传输,所以用 YUV 方式传送占用极少的频宽。

YUV 的存储格式与其采样方式密切相关。

主流的采样方式有 4 种:YUV(4:4:4), YUV(4:2:2), YUV(4:2:0), YUV(4:1:1)

  • YUV 4:4:4 采样:每一个 Y 对应一组 UV 分量(4 个 Y 采样就对应 4 个 Cb 和 4 个 Cr 采样)。平均一个像素占用 8+8+8 = 24 位。
  • YUV 4:2:2 采样:每两个 Y 共用一组 UV 分量(4 个 Y 采样就对应 2 个 Cb 和 2 个 Cr 采样)。平均一个像素占用 8+4+4 = 16 位。
  • YUV 4:2:0 采样:每四个 Y 共用一组 UV 分量(4 个 Y 采样就对应 2 个 Cb 或 2 个 Cr 采样)。平均一个像素占用 8+4+0 = 12 位。
  • YUV 4:1:1 采样:每四个 Y 共用一组 UV 分量(4 个 Y 采样就对应 1 个 Cb 和 1 个 Cr 采样)。平均一个像素占用 8+2+2 = 12 位。

YUV411 这个格式很少使用,这里就不介绍了。

YUV444 格式

YUV444P

Y1 Y2 Y3 Y4 Y5 Y6 Y7 Y8
Y9 Y10 Y11 Y12 Y13 Y14 Y15 Y16
Y17 Y18 Y19 Y20 Y21 Y22 Y23 Y24
Y25 Y26 Y27 Y28 Y29 Y30 Y31 Y32
U1 U2 U3 U4 U5 U6 U7 U8
U9 U10 U11 U12 U13 U14 U15 U16
U17 U18 U19 U20 U21 U22 U23 U24
U25 U26 U27 U28 U29 U30 U31 U32
V1 V2 V3 V4 V5 V6 V7 V8
V9 V10 V11 V12 V13 V14 V15 V16
V17 V18 V19 V20 V21 V22 V23 V24
V25 V26 V27 V28 V29 V30 V31 V32

YUV422 格式 ( YCbYCr / YUYV ,CbYCrY / UYVY / I422 ,422P , 422SP )

打包格式 (Packed) :YCbYCr / YUYV ,CbYCrY / UYVY / I422

平面格式 (Planar) :422P , 422SP

YCbYCr / YUYV

Y U Y V Y U Y V
Y1 U1 Y2 V1 Y3 U2 Y4 V2
Y5 U3 Y6 V3 Y7 U4 Y8 V4
Y9 U5 Y10 V5 Y11 U6 Y12 V6
Y13 U7 Y14 V7 Y15 U8 Y16 V8
Y17 U9 Y18 V9 Y19 U10 Y20 V10
Y21 U11 Y22 V11 Y23 U12 Y24 V12
Y25 U13 Y26 V13 Y27 U14 Y28 V14
Y29 U15 Y30 V15 Y31 U16 Y32 V16

CbYCrY / UYVY / I422

U Y V Y U Y V Y
U1 Y1 V1 Y2 U2 Y3 V2 Y4
U3 Y5 V3 Y6 U4 Y7 V4 Y8
U5 Y9 V5 Y10 U6 Y11 V6 Y12
U7 Y13 V7 Y14 U8 Y15 V8 Y16
U9 Y17 V9 Y18 U10 Y19 V10 Y20
U11 Y21 V11 Y22 U12 Y23 V12 Y24
U13 Y25 V13 Y26 U14 Y27 V14 Y28
U15 Y29 V15 Y30 U16 Y31 V16 Y32

YUV422P

Y1 Y2 Y3 Y4 Y5 Y6 Y7 Y8
Y9 Y10 Y11 Y12 Y13 Y14 Y15 Y16
Y17 Y18 Y19 Y20 Y21 Y22 Y23 Y24
Y25 Y26 Y27 Y28 Y29 Y30 Y31 Y32
U1 U2 U3 U4 U5 U6 U7 U8
U9 U10 U11 U12 U13 U14 U15 U16
V1 V2 V3 V4 V5 V6 V7 V8
V9 V10 V11 V12 V13 V14 V15 V16

YUV422SP

Y1 Y2 Y3 Y4 Y5 Y6 Y7 Y8
Y9 Y10 Y11 Y12 Y13 Y14 Y15 Y16
Y17 Y18 Y19 Y20 Y21 Y22 Y23 Y24
Y25 Y26 Y27 Y28 Y29 Y30 Y31 Y32
U1 V1 U2 V2 U3 V3 U4 V4
U5 V5 U6 V6 U7 V7 U8 V8
U9 V9 U10 V10 U11 V11 U12 V12
U13 V13 U14 V14 U15 V15 U16 V16

YUV420 格式( I420 / YU12 , YV12 , NV12 , NV21 )

分为:YUV420P ( I420 / YU12 , YV12 ),YUV420SP ( NV12 , NV21 )

YU12 是 I420 的别称,YV12 与之是 UV 顺序不同。

NV12 和 NV21 则是 UV 交织顺序不同。

I420 / YU12 (YUV420P)

又称 YU12 ( Y 后面紧跟着 V,位深为 12 )

Y1 Y2 Y3 Y4 Y5 Y6 Y7 Y8
Y9 Y10 Y11 Y12 Y13 Y14 Y15 Y16
Y17 Y18 Y19 Y20 Y21 Y22 Y23 Y24
Y25 Y26 Y27 Y28 Y29 Y30 Y31 Y32
U1 U2 U3 U4 U5 U6 U7 U8
V1 V2 V3 V4 V5 V6 V7 V8

YV12 (YUV420P)

与 I420(YUV420P) 的 UV 存储顺序相反

Y1 Y2 Y3 Y4 Y5 Y6 Y7 Y8
Y9 Y10 Y11 Y12 Y13 Y14 Y15 Y16
Y17 Y18 Y19 Y20 Y21 Y22 Y23 Y24
Y25 Y26 Y27 Y28 Y29 Y30 Y31 Y32
V1 V2 V3 V4 V5 V6 V7 V8
U1 U2 U3 U4 U5 U6 U7 U8

NV12 (YUV420SP)

UV 分量交织存储

Y1 Y2 Y3 Y4 Y5 Y6 Y7 Y8
Y9 Y10 Y11 Y12 Y13 Y14 Y15 Y16
Y17 Y18 Y19 Y20 Y21 Y22 Y23 Y24
Y25 Y26 Y27 Y28 Y29 Y30 Y31 Y32
U1 V1 U2 V2 U3 V3 U4 V4
U4 V4 U5 V5 U7 V7 U8 V8

NV21 (YUV420SP)

VU 分量交织存储。与 YUV420P(NV12) 的 UV 交织存储顺序相反

Y1 Y2 Y3 Y4 Y5 Y6 Y7 Y8
Y9 Y10 Y11 Y12 Y13 Y14 Y15 Y16
Y17 Y18 Y19 Y20 Y21 Y22 Y23 Y24
Y25 Y26 Y27 Y28 Y29 Y30 Y31 Y32
v1 U1 V2 U2 V3 U3 V4 U4
v4 U4 V5 U5 V7 U7 V8 U8

YUV 转 RGB

不管 YUV 的格式如何,最终得到的就是 Y、U、V 三个向量的值。

那么 Y、U、V 的数据如何转为 RGB 图片呢?毕竟 RGB 图片上有很多强大的算法。

YUV 与 RGB 的转换公式:

U 和 V 组件可以被表示成原始的 R、G,和 B:

从 RGB 到 YUV

如一般顺序,转移组件的范围可得到:

YUV范围

在逆转关系上,从 YUV 到 RGB,可得:

从 YUV 到 RGB

取而代之,以矩阵表示法(matrix representation),可得到公式:

RGB 转 YUV 的转换矩阵

YUV 转 RGB 的转换矩阵

发布了177 篇原创文章 · 获赞 45 · 访问量 21万+
展开阅读全文

我在网上找了个bmp转YUV的程序进行修改,但读不出数据,不知什么问题,有谁懂的请帮忙看下,谢谢了

04-28

#include <stdio.h> #include <stdlib.h> #include "bmp2rgb.h" u_int8_t BMP2RGB(BITMAPFILEHEADER file_header,BITMAPINFOHEADER info_header, FILE* bmpFile, u_int8_t* rgbBuf);//24bit RGB u_int8_t RGB24ToYUV420(int Width,int Height,u_int8_t* rgbBuf,u_int8_t*YuvBuffer); #define max(a,b) (((a)>(b))?(a):(b)) #define min(a,b) (((a) < (b)) ? (a) : (b)) int main(int argc, char** argv) { //设置命令行参数 argv[1]= "boot_logo.bmp"; argv[2]="boot_logo.yuv"; //相当于设置文件名 char* bmpFileName = argv[1]; char* yuvFileName = argv[2]; //打开文件 FILE* bmpFile = fopen(bmpFileName, "rb"); if (bmpFile == NULL) { printf(" Open the BMP file.\n"); exit(1); } else { printf("The BMP file is %s\n", bmpFileName); } FILE* yuvFile = fopen(yuvFileName, "wb"); if (yuvFile == NULL) { printf("Cannot open the YUV file.\n"); exit(1); } else { printf("The YUV file is %s\n", yuvFileName); } //读取BMP文件头,信息头,读取错误时的处理代码 BITMAPFILEHEADER file_header; BITMAPINFOHEADER info_header; if (fread(&file_header, sizeof(BITMAPFILEHEADER), 1, bmpFile) != 1) if (file_header.bfType != 0x4D42) { printf("Not BMP file.\n"); exit(1); } if (fread(&info_header, sizeof(BITMAPINFOHEADER), 1, bmpFile) != 1) { printf("read info header error!"); exit(1); }//结束读取BMP文件头 //读取图像尺寸 int width = info_header.biWidth; int height = info_header.biHeight; //开辟缓冲区 buf u_int8_t* yBuf = (u_int8_t*)malloc(height*width); u_int8_t* uBuf = (u_int8_t*)malloc(height*width / 4); u_int8_t* vBuf = (u_int8_t*)malloc(height*width / 4); u_int8_t* rgbBuf = (u_int8_t*)malloc(height*width * 3); u_int8_t*YuvBuffer =(u_int8_t*)malloc(height*width * 5); if (yBuf == NULL || uBuf == NULL || vBuf == NULL || rgbBuf == NULL || YuvBuffer==NULL) { printf("Not enough memory\n"); exit(1); } //BMP与RGB的转换,得到RGB数据 if (BMP2RGB(file_header, info_header, bmpFile, rgbBuf)) { printf("BMP2RGB error\n"); exit(1); } //RGB与YUV的转换,得到YUV数据 // int flip = 0; /*读取到的图像数据是倒序存放的,flip=0保证了RGB2YUV可以正确地对其转换*/ /* if (RGB2YUV(width, height, rgbBuf, yBuf, uBuf, vBuf, flip)) { printf("RGB2YUV error\n"); exit(1); } //将yuv按顺序写入yuvfile文件 fwrite(yBuf, 1, width * height, yuvFile); fwrite(uBuf, 1, (width * height) / 4, yuvFile); fwrite(vBuf, 1, (width * height) / 4, yuvFile);*/ if( RGB24ToYUV420( width, height, rgbBuf,YuvBuffer)) { printf("RGB24ToYUV420 error\n"); exit(1); } int len=0; len= fwrite(YuvBuffer, 1,sizeof(YuvBuffer), yuvFile); printf("len ==%d byte\n",len); //打印宽高,方便yuv观看程序打开 printf("width is %d", width); printf("\n"); printf("height is %d", height); printf("\n"); //清理内存 free(rgbBuf); free(YuvBuffer); free(yBuf); free(uBuf); free(vBuf); fclose(bmpFile); fclose(yuvFile); return 0; } u_int8_t BMP2RGB(BITMAPFILEHEADER file_header,BITMAPINFOHEADER info_header, FILE* bmpFile, u_int8_t* rgbBuf) { BITMAPFILEHEADER file_h=file_header; BITMAPINFOHEADER info_h=info_header; FILE* pFile =bmpFile; int w=0,h=0; //确定像素的实际点阵数 w = (info_h.biWidth*info_h.biBitCount + 31) / 32 * 4;//w为实际一行的字节数 h = info_h.biHeight;//h为列数 // printf("w==%d,h==%d\n",w,h); //开辟实际字节数量的缓冲区,读数据,一次读取一个字节 u_int8_t* dataBuf = (u_int8_t*)malloc(w*h); /*使用文件头的字节偏移属性bfOffBits 直接把文件指针定位到像素值数据的起始 */ fseek(pFile, file_h.bfOffBits, 0); fread(dataBuf, 1, w*h, pFile); unsigned char* data = dataBuf; u_int8_t* rgb = rgbBuf; //开始写入rgb int i, j; for (j = 0; j < h; j++)//j控制行循环 { for (i = 0; i < w; i += 3)//i控制列循环 { *rgb = data[i + w*j];//B *(rgb + 1) = data[i + w*j + 1];//G *(rgb + 2) = data[i + w*j + 2];//R rgb += 3; } } //释放内存 free(dataBuf); return 0; } /***************************************************************************************************************/ u_int8_t RGB24ToYUV420(int Width,int Height,u_int8_t* rgbBuf,u_int8_t*YuvBuffer) { u_int8_t* yuvBuf=YuvBuffer;//YUV空间 int nWidth=Width; int nHeight=Height; /////////////////////下面转换算法是网上查到的 int i, j; u_int8_t*bufY = yuvBuf; u_int8_t*bufU = yuvBuf + nWidth * nHeight; u_int8_t*bufV = bufU + (nWidth* nHeight* 1/4); u_int8_t*Y=bufY; u_int8_t*U=bufU; u_int8_t*V=bufV; u_int8_t*bufRGB; unsigned char y, u, v, r, g, b; if (NULL==rgbBuf) { printf("NULL==rgbBuf\n"); return 1 ; } for (j = 0; j<nHeight;j++) { bufRGB = rgbBuf + nWidth * (nHeight - 1-j) * 3 ; for (i = 0;i<nWidth;i++) { int pos = nWidth * i + j; r= *(bufRGB++); g = *(bufRGB++); b = *(bufRGB++); y =(unsigned char)(( 66 * r + 129 * g + 25 * b + 128) >>8) + 16;//16 v = (unsigned char)((-38 * r - 74 * g + 112 * b + 128) >>8) +128 ; //128 u = (unsigned char)((112 * r - 94 * g - 18 * b + 128) >> 8) + 128 ; *(bufY++)=max(0,min(y, 255 )); if (j%2==0&&i%2 ==0) { if (u>255) { u=255; } if (u<0) { u = 0; } *(bufU++) =u; //存u分量 } else { //存v分量 if (i%2==0) { if (v>255) { v = 255; } if (v<0) { v = 0; } *(bufV++) =v; } } } } return 0; } ``` #include <stdio.h> #include "sys/types.h" #include <stdlib.h> typedef unsigned long DWORD;//32bit typedef unsigned short WORD;//16bit typedef unsigned long LONG; //32bit typedef struct tagBITMAPFILEHEADER { //0x00~0x01,说明文件的类型 WORD bfType; //0x02~0x05,说明文件的大小,用字节B为单位 DWORD bfSize; //0x06~0x07,保留,设置为0 WORD bfReserved1; //0x08~0x09,保留,设置为0 WORD bfReserved2; //0x0a~0x0d,说明从BITMAP_FILE_HEADER结构开始到实际的图像数据之间的字节偏移量 DWORD bfOffBits; } BITMAPFILEHEADER; typedef struct tagBITMAPINFOHEADER { //0x0e~0x11,说明当前结构体所需字节数 DWORD biSize; //0x12~0x15,以像素为单位说明图像的宽度 LONG biWidth; //0x16~0x19,以像素为单位说明图像的高度 LONG biHeight; //0x1a~0x1b,说明位面数,必须为1 WORD biPlanes; //0x1c~0x1d,说明图像的位深度 WORD biBitCount; //0x1e~0x21,说明图像是否压缩及压缩类型 DWORD biCompression; //0x22~0x25,以字节为单位说明图像大小,必须是4的整数倍 DWORD biSizeImage; //0x26~0x29,目标设备的水平分辨率,像素/米 LONG biXPelsPerMeter; //0x2a~0x2d,目标设备的垂直分辨率,像素/米 LONG biYPelsPerMeter; //0x2e~0x31,说明图像实际用到的颜色数,如果为0,则颜色数为2的biBitCount次方 DWORD biClrUsed; //0x32~0x35,说明对图像显示有重要影响的颜色索引的数目,如果是0,表示都重要。 DWORD biClrImportant; } BITMAPINFOHEADER; ``` 运行环境linux,bmp图片24位 运行结果: The BMP file is boot_logo.bmp The YUV file is boot_logo.yuv len ==8 byte width is 185729024 height is 0 问答

没有更多推荐了,返回首页

©️2019 CSDN 皮肤主题: 精致技术 设计师: CSDN官方博客

分享到微信朋友圈

×

扫一扫,手机浏览