前一阵买了一个USB的摄像头接在cubieboard2上打算进行图像采集并存储图片,在解决了驱动以及V4L2摄像头编程等问题之后发现采集到的图像数据是YUYV格式的,因为我有一个现成的写BMP文件(RGB565)的函数,所以就将YUYV转换成RGB565格式写入BMP文件来看看cubieboard2的图像采集效果(其实可以利用libjpeg直接将YUYV转JPEG[5],这样写文件传文件还能快点,不过我确实也应该了解下YUYV)。
1.YUYV和RGB的基本概念 1.1 什么是YUV和RGB色彩空间 在不同的应用领域中为了更好更准确的满足各自的需求,出现了各种各样的色彩空间模型来量化的描述颜色。我们比较常接触到的就包括 RGB / CMYK / YIQ / YUV / HIS等等。 对于数字电子多媒体领域来说,我们经常接触到的色彩空间的概念,主要是RGB , YUV这两种(实际上,这两种体系包含了许多种具体的颜色表达方式和模型,如sRGB, Adobe RGB, YUV422, YUV420 …)。 RGB是按三基色加光系统的原理来描述颜色,而YUV则是按照亮度、色差的原理来描述颜色,Y表示亮度,UV并非任何单词的缩写,UV信号实际上就是蓝色差信号和红色差信号,一定程度上间接的代表了蓝色和红色的强度(偏向冷色和暖色的程度)。 YUV主要用于电视系统以及模拟视频领域,它将亮度信息(Y)与色彩信息(UV)分离,没有UV信息一样可以显示完整的图像,只不过是黑白的,这样的设计很好地解决了彩色电视机与黑白电视的兼容问题。并且,YUV不像RGB那样要求三个独立的视频信号同时传输,所以用YUV方式传送占用极少的频宽。 1.2 YUV、YIQ和YCbCr区别 对于YUV模型,很多时候我们是把它和YIQ / YCrCb模型混为一谈的。实际上,YUV模型用于PAL制式的电视系统;YIQ模型与YUV模型类似,用于NTSC制式的电视系统。YIQ颜色空间中的I和Q分量相当于将YUV空间中的UV分量做了一个33度的旋转;这两者常应用于模拟领域。YCbCr颜色空间是由YUV颜色空间派生的一种颜色空间,主要用于数字电视系统中(ITU-R BT.601建议)[1]。 我们在数字电子多媒体领域所谈到的YUV格式(比如我们现在用的USB摄像头),实际上准确的说,是以YcrCb色彩空间模型为基础的具有多种存储格式的一类颜色模型的家族(包括 YUV444 / YUV422 / YUV420 / YUV420P等等,这些YUV模型区别主要在于UV数据的采样方式和存储方式)。并不是传统意义上用于PAL制模拟电视的YUV模型。
1.3 RGB和YUV数学模型转换 RGB -> YIQ: RGB ->YCrCb: 据此可以计算出(我自己没计算,有空的话用MATLAB对矩阵求逆验证下): R = Y +1.4075 *(V-128) G = Y –0.3455 *(U –128) – 0.7169 *(V –128) B = Y +1.779 *(U – 128) 这里的Y,U,V就是USB摄像头采集到的对应YUV数据,而这YUV实际上指的是YCrCb。 2. YUV的存储格式 2.1 采样方式 YUV码流的存储格式其实与其采样的方式密切相关,主流的采样方式有三种,YUV4:4:4,YUV4:2:2,YUV4:2:0,如何根据其采样格式来从码流中还原每个像素点的YUV值,因为只有正确地还原了每个像素点的YUV值,才能通过YUV与RGB的转换公式提取出每个像素点的RGB值,然后显示出来。用三个图来直观地表示采集的方式,以黑点表示采样该像素点的Y分量,以空心圆圈表示采用该像素点的UV分量。[2]所以, YUV 4:4:4采样,每一个Y对应一组UV分量。 YUV 4:2:2采样,每两个Y共用一组UV分量。 YUV 4:2:0采样,每四个Y共用一组UV分量。 2.2 存储格式 YUV的存储格式通常有两大类:打包(packed)格式和平面(planar)格式。前者将YUV分量存放在同一个数组中,通常是几个相邻的像素组成一个宏像素(macro-pixel);而后者使用三个数组分开存放YUV三个分量,就像是一个三维平面一样。我们常说得YUV420属于planar格式的YUV(这句没去验证)。 Camera Sensor中,最常用的YUV模型是 YUV422格式,因为它采用4个字节描述两个像素,能和RGB565模型比较好的兼容,有利于Camera Sensor和Camera controller的软硬件接口设计。而YUV422还有细分的格式,比如,YUYV,YYUV,UYVY… 我的摄像头是YUYV格式,后面就单讲YUYV。 我的摄像头采用的是打包的YUYV格式,Y0和Y1共用一对U0和U1,即两个像素用4个字节来存储,数据在unsignedchar型的数组中的存储如下: Addr: 0 1 2 3 | 4 5 6 7 YUV: Y0 U0 Y1 V0 | Y2 U1 Y3 U1 Pixsel: { 1 } { 2 } | { 3 } { 4 }
3 RGB565的存储格式 RGB565,两个字节存放5bit红色,6bit 绿色,5bit蓝色信息。我原本比较熟悉的RGB24,R、G、B各用了一个字节,这8bit的颜色信息如何压缩成5、6、5bit而显示出整个范围的颜色(稍有色差),存储格式又是怎样。我没有找专门讲RGB565的文章,下面的这个存储格式是我从网上的一个程序中倒推出来的。该程序段经过验证,可以正确显示图片。程序段如下: *(RGBdata++) =( ((g & 0x1C) << 3) | ( r >> 3) ); *(RGBdata++) =( (b & 0xF8) | ( g >> 5) ); RGBdata是存储RGB565的数组,r、g、b分别是存储三种颜色的unsigned char 型的变量,值的取值范围在0-255之间。r和b都经过了右移运算,舍弃了低三位的信息,g也是舍弃了低二位的信息,所以显示出的颜色能大致符合,稍有色差(实际上是对应了调色板的索引值)。最后的存储格式如下: {``````blue`````} {```````green`````````} {```````red```````} b7 b6 b5 b4 b3 g7 g6 g5 | g4 g3 g2 r7 r6 r5 r4 r3 A17 A16 A15 A14 A13 A12 A11 A10|A07 A06 A05 A04 A03 A02 A01 A00 [------Addr1------------------][------------Addr0-------------] 4. YUV转RGB加快运算速度 R = Y +1.4075 *(V-128) G = Y –0.3455 *(U –128) – 0.7169 *(V –128) B = Y +1.779 *(U – 128) 转换公式中有浮点运算,将浮点运算转换成右移运算。 v=V-128; u=U-128; y=Y; r=y+ v+ (v*103>>8); g=y- (u*88>>8) - (v*183>>8); b=y+ u + (u*198>>8); 5. 补充说明 5.1 YUYV与RGB的其他转换矩阵 YUYU转RGB除了1.3节中提到的转换矩阵,另外还有以下转换矩阵[6]:
经过计算可以得到转换关系a: v=V-128; u=U-128; y=Y-16; r = 1.164*y + 1.596*v; g = 1.164*y - 0.380*u- 0.813*v; b = 1.164*y + 2.018*u; 还有另外一个转换关系b,但是我没有去找原始的转换矩阵, v=V-128; u=U-128; y=Y; r = y+1.14*v; g = y-0.394*u-0.581*v; b = y+2.203*u; 以上两种转换关系我都测试过,图片可以正常显示。 5.2 YUYV的planar格式 我最开始参考的程序[3],YUYV是planar格式的,测试的时候发现存储下来的图片如下左图,看起来好像每一小块区域保留了一定的图像,但是每一小块区域都有一部分信息错误,通篇都是如此。想像了一下,这样的现象正好符合packet格式错当成planar格式来处理的结果。据此修改程序后图像显示正常了。 5.3 RGB565的存储格式 我最开始参考的程序[3],rgb565的格式处理是这样: *(RGBdata++) =( ((g & 0x1C) << 3) | ( b >> 3) ); *(RGBdata++) =( (r & 0xF8) | ( g >> 5) ); 也就是从地位到高位排列是b、g、r。但是按照这种方式处理之后,我发现我采集到的图像中,红色和蓝色正好相反了,如下左图,所以怀疑r和b的位置颠倒了。调换位置之后图像显示就正确了。 本来以为是不是作者不小心写错了,不过后来看到很多篇博文rgb的顺序都是如此,差点怀疑是我摄像头的特殊性造成的,可是再一想,不可能的,摄像头有关的只有YUYV格式,而此后的处理(如YUV转RGB,提取出来rgb的颜色分量)已经和摄像头本身的格式无关了,就是说r、g、b或者b、g、r的顺序只和内存中的图片格式有关,我用的是16bit的BMP,理论上顺序是唯一的。于是又查了一下RGB565,发现从低位到高位的确应该是r、g、b[4]。 6. YUYU转RGB565的源程序 我的摄像头是packed(打包)YUYV格式,GRB565的存储中由低到高是RGB。 int convert_yuyv_to_rgb(unsignedchar *inBuf, unsigned char *outBuf,int imgWidth, int imgHeight, int cvtMethod) { introws,cols; inty,u,v,r,g,b; unsignedchar *YUVdata,*RGBdata; intYpos,Upos,Vpos;//in the inBuf's postion YUVdata= inBuf; RGBdata = outBuf; Ypos=0; Upos=Ypos+1; Vpos=Upos+2; for(rows=0;rows
[0] YUV / RGB 格式及快速转换算法 http://www.cnblogs.com/huaping-audio/archive/2009/12/27/1633624.html
[1] 关于YUV和RGB之间的转换公式 http://blog.sina.com.cn/s/blog_5713096b0100059i.html
[2] 谈谈RGB、YUV2、YUYV、YVYU、UYVY、AYUV http://blog.csdn.net/cuiy0001/article/details/8591037
[3] YUV420P和YUV422转RGB565 http://blog.sina.com.cn/s/blog_475e9bf20100siir.html
[4] RGB565格式互转,及彩条程序 http://blog.csdn.net/zhandoushi1982/article/details/5178645
[5]YUYV转JPEG http://blog.csdn.net/zd394071264/article/details/8288111
[6]http://wenku.baidu.com/link?url=OBu9Ukl682IHSB1efGPNE6TdrEIM3YE3TGT266VJk69s5f8gMzYbE06fkKYDeRum8SDOilKkOergGyslpkh5iSkBicnbTb9oOf7YRTQxcJy