YUV转RGB
- 格式:doc
- 大小:38.00 KB
- 文档页数:4
YUV422_to_RGB24转换算法最近做摄像头的测试,读取出来的数据为YUV422(YUYV)格式,若要在LCD上面显示,需要先做格式转换,但在网上搜了一遍,只有YUV420转RGB的算法,无奈自己做了一个函数,实现到RGB24的格式转换,转成RGB24(即RGB888)之后,就可以根据自己的LCD格式再做其他转换,例如我的LCD显示格式为RGB565,可以再做一个RGB24到RGB565的转换即可,类似转换比较简单,相关例程网上也比较多,就不多做描述了,下面给出YUV422到RGB24的转换函数:typedef unsigned char uint8_t;static void yuv422_to_rgb24(unsigned char *yuv422,unsigned char *rgb24, int width, int height){int x,y;uint8_t *yuv444;yuv444 = (uint8_t *) malloc(sizeof(uint8_t) * WIDTH * HEIGHT * 3);for(x = 0,y = 0;x < width*height*2,y < width*height*3;x+=4,y+=6){yuv444[y] = yuv422[x];yuv444[y+1] = yuv422[x+1];yuv444[y+2] = yuv422[x+3];yuv444[y+3] = yuv422[x+2];yuv444[y+4] = yuv422[x+1];yuv444[y+5] = yuv422[x+3];}for(x = 0;x < width*height*3;x+=3){rgb24[x+2] = yuv444[x] + 1.402*(yuv444[x+2] - 128);rgb24[x+1] = yuv444[x]-0.34414*(yuv444[x+1]-128)-0.71414*(yuv444[x+2]-128);rgb24[x] = yuv444[x] + 1.772*(yuv444[x+1] - 128);if (rgb24[x]>255)rgb24[x]=255;if (rgb24[x]<0)rgb24[x]=0;if (rgb24[x+1]>255)rgb24[x+1]=255;if (rgb24[x+1]<0)rgb24[x+1]=0;if (rgb24[x+2]>255)rgb24[x+2]=255;if (rgb24[x+2]<0)rgb24[x+2]=0;}free(yuv444);}该函数的转换思想是先进行YUV422到YUV444的转化,再进行YUV444到RGB24的转化,利用一个YUV422像素点得到两个YUV444像素点,其中YUV444[0]由Y[0]、U[0]、V[0]得到,YUV444[1]由Y[1] 、U[0]、V[0]得到,可见UV分量被重复利用。
关于YUV和RGB之间的转换公式总结了一下网上关于YUV的一些东西先区分一下YUV和YCbCrYUV色彩模型来源于RGB模型,该模型的特点是将亮度和色度分离开,从而适合于图像处理领域。
应用:模拟领域Y'= 0.299*R' + 0.587*G' + 0.114*B'U'= -0.147*R' - 0.289*G' + 0.436*B' = 0.492*(B'- Y')V'= 0.615*R' - 0.515*G' - 0.100*B' = 0.877*(R'- Y')R' = Y' + 1.140*V'G' = Y' - 0.394*U' - 0.581*V'B' = Y' + 2.032*U'YCbCr模型来源于YUV模型。
YCbCr是YUV 颜色空间的偏移版本.应用:数字视频,ITU-R BT.601建议Y’ = 0.257*R' + 0.504*G' + 0.098*B' + 16Cb' = -0.148*R' - 0.291*G' + 0.439*B' + 128Cr' = 0.439*R' - 0.368*G' - 0.071*B' + 128R' = 1.164*(Y’-16) + 1.596*(Cr'-128)G' = 1.164*(Y’-16) - 0.813*(Cr'-128) - 0.392*(Cb'-128)B' = 1.164*(Y’-16) + 2.017*(Cb'-128)PS: 上面各个符号都带了一撇,表示该符号在原值基础上进行了伽马校正,伽马校正有助于弥补在抗锯齿的过程中,线性分配伽马值所带来的细节损失,使图像细节更加丰富。
opencv yuv 公式在计算机视觉领域,OpenCV是一个广泛使用的开源计算机视觉库,提供了丰富的图像处理和计算机视觉算法。
其中,YUV色彩空间是一种常用的色彩编码方式,它将颜色信息和亮度信息分离,广泛应用于数字视频传输和图像处理领域。
本文将介绍OpenCV中YUV色彩空间的相关公式和用法。
一、YUV色彩空间简介YUV色彩空间由亮度(Y)和色度(UV)两个分量组成,亮度分量表示像素的明暗程度,而色度分量则表示像素的颜色信息。
这种分离的方式在图像和视频压缩中非常有用,可以降低数据量并提高压缩效率。
在YUV色彩空间中,亮度(Y)分量占据整个色彩空间的明度信息。
色度(UV)分量则描述了颜色信息,其中U表示与蓝色的差异,V表示与红色的差异。
由于人眼对亮度更加敏感,而对颜色信息的敏感度较低,因此YUV色彩空间能够满足人眼对图像感知的需求。
二、YUV与RGB的转换公式在OpenCV中,我们可以使用以下公式将YUV和RGB色彩空间相互转换:RGB to YUV:Y = 0.299 * R + 0.587 * G + 0.114 * BU = 0.492 * (B - Y)V = 0.877 * (R - Y)YUV to RGB:R = Y + 1.13983 * VB = Y + 2.032 * UG = Y - 0.39465 * U - 0.5806 * V其中,R、G、B分别表示RGB色彩空间中的红、绿、蓝分量,而Y、U、V则表示YUV色彩空间中的亮度和色度分量。
三、OpenCV中的YUV图像处理在OpenCV中,我们可以通过以下代码来实现YUV图像的处理:```cpp// 加载YUV图像cv::Mat yuvImage = cv::imread("image.yuv",cv::IMREAD_GRAYSCALE);// YUV to RGB转换cv::Mat bgrImage;cv::cvtColor(yuvImage, bgrImage, cv::COLOR_YUV2BGR);// 进行图像处理// ...// RGB to YUV转换cv::Mat processedYUVImage;cv::cvtColor(bgrImage, processedYUVImage, cv::COLOR_BGR2YUV);// 保存处理后的YUV图像cv::imwrite("processed_image.yuv", processedYUVImage);```通过这段代码,我们可以加载YUV图像并将其转换为RGB色彩空间,然后进行图像处理,最后再将结果转换回YUV色彩空间并保存。
【总结】关于YUV-RGB格式转换的⼀些个⼈理解 这段时间⼀直在研究YUV的格式问题例如YUV422、YUV420,在⽹上搜索了很多这⽅⾯的资料,发现很多资料讲的东西是重复的,没有⽐较深⼊的讲解,所以看了之后印象不是很深,过了⼀段时间之后⼜对它们有了困惑。
所以就有了⼀个想法,打算⾃⼰写⼀个c语⾔⼩程序,通过对BMP⽂件的RGB数据读取,然后将得到的RGB数据转换成为YUV格式的⽂件,然后⽤YUV的播放器打开,查看是否解析正确。
(在这⾥,BMP⽂件我选择了24bpp的格式,为了⽅便。
)通过这样的⽅式,正向和逆向学习来加深YUV⽂件的认识。
经过了2天的努⼒,从想法到实践,终于完成了YUV422和YUV420的⽣成,基本上对YUV的⼏种常见格式有了⼀些深⼊的看法,由于本⼈的⽔平有限,如果理解有出⼊,还请各位⽹友能够指出。
⼀、基本思路先附上主要程序的流程图:在正式讲解代码前,我们先来了解YUV的分类:packed: 打包模式,即Y、U、V数据是连续排布的planar: 平⾯模式,即Y、U、V是各⾃分开存放的semi-planar: 半平⾯模式,即Y单独存放, UV⼀起存放上⾯的3种是主要内存排布的分类,⾄于详细的排布如下(主要是YUV422、YUV420):(我们使⽤⼀张分辨率为 height*width的图⽚为例⼦)YUV422 planar:整个YUV⽂件中,Y、U、V是⼀个⼤数组,⽽其中我们可以看成是三个⼩数组,分别是Y数组(长度为Height*Width)、U数组(长度为Height*Width/2)、V数组(长度为Height*Width/2)。
YUV422 Semi planar:YUV420 planar :整个YUV⽂件中,Y、U、V是⼀个⼤数组,⽽其中我们可以看成是三个⼩数组,分别是Y数组(长度为Height*Width)、U数组(长度为Height*Width/4)、V数组(长度为Height*Width/4)。
1 前言自然界的颜色千变万化,为了给颜色一个量化的衡量标准,就需要建立色彩空间模型来描述各种各样的颜色,由于人对色彩的感知是一个复杂的生理和心理联合作用的过程,所以在不同的应用领域中为了更好更准确的满足各自的需求,就出现了各种各样的色彩空间模型来量化的描述颜色。
我们比较常接触到的就包括RGB / CMYK / YIQ / YUV / HSI等等。
对于数字电子多媒体领域来说,我们经常接触到的色彩空间的概念,主要是RGB , YUV 这两种(实际上,这两种体系包含了许多种具体的颜色表达方式和模型,如sRGB, Adobe RGB, YUV422, YUV420 …), RGB是按三基色加光系统的原理来描述颜色,而YUV则是按照亮度,色差的原理来描述颜色。
即使只是RGB YUV这两大类色彩空间,所涉及到的知识也是十分丰富复杂的,自知不具备足够的相关专业知识,所以本文主要针对工程领域的应用及算法进行讨论。
2 YUV相关色彩空间模型对于YUV模型,实际上很多时候,我们是把它和YIQ / YCrCb模型混为一谈的。
实际上,YUV模型用于PAL制式的电视系统,Y表示亮度,UV并非任何单词的缩写。
YIQ模型与YUV模型类似,用于NTSC制式的电视系统。
YIQ颜色空间中的I和Q分量相当于将YUV空间中的UV分量做了一个33度的旋转。
YCbCr颜色空间是由YUV颜色空间派生的一种颜色空间,主要用于数字电视系统中。
从RGB到YCbCr的转换中,输入、输出都是8位二进制格式。
三者与RGB的转换方程如下:RGB -> YUV:实际上也就是:Y=0.30R+0.59G+0.11B ,U=0.493(B-Y) ,V=0.877(R-Y)RGB -> YIQ:RGB -> YCrCb:从公式中,我们关键要理解的一点是,UV / CbCr信号实际上就是蓝色差信号和红色差信号,进而言之,实际上一定程度上间接的代表了蓝色和红色的强度,理解这一点对于我们理解各种颜色变换处理的过程会有很大的帮助。
1.//rgb转yuv4202.//////////////////////////////////////////////////////////////////////////3.bool RGB2YUV(LPBYTE RgbBuf,UINT nWidth,UINT nHeight,LPBYTE yuvBuf,unsigned long*len)4.{5.int i,j;6.unsigned char*bufY,*bufU,*bufV,*bufRGB,*bufYuv;7.memset(yuvBuf,0,(unsigned int)*len);8.bufY=yuvBuf;9.bufV=yuvBuf+nWidth*nHeight;10.bufU=bufV+(nWidth*nHeight*1/4);11.*len=0;12.unsigned char y,u,v,r,g,b,testu,testv;13.unsigned int ylen=nWidth*nHeight;14.unsigned int ulen=(nWidth*nHeight)/4;15.unsigned int vlen=(nWidth*nHeight)/4;16.for(j=0;j<nHeight;j++)17.{18.bufRGB=RgbBuf+nWidth*(nHeight-1-j)*3;19.for(i=0;i<nWidth;i++)20.{21.int pos=nWidth*i+j;22.r=*(bufRGB++);23.g=*(bufRGB++);24.b=*(bufRGB++);25.y=(unsigned char)((66*r+129*g+25*b+128)>>8)+16;26.u=(unsigned char)((-38*r-74*g+112*b+128)>>8)+128;27.v=(unsigned char)((112*r-94*g-18*b+128)>>8)+128;28.*(bufY++)=max(0,min(y,255));29.if(j%2==0&&i%2==0)30.{31.if(u>255)32.{33.u=255;34.}35.if(u<0)36.{37.u=0;38.}39.*(bufU++)=u;40.//存u分量41.}42.else43.{44.//存v分量45.if(i%2==0)46.{47.if(v>255)48.{49.v=255;50.}51.if(v<0)52.{53.v=0;54.}55.*(bufV++)=v;56.}57.}58.}59.}60.*len=nWidth*nHeight+(nWidth*nHeight)/2;61.return true;62.}63.//////////////////////////////////////////////////////////////////////////64.//yuv420转rgb65.//////////////////////////////////////////////////////////////////////////66.bool YUV2RGB(LPBYTE yuvBuf,UINT nWidth,UINT nHeight,LPBYTE pRgbBuf,unsigned long*len)67.{68.#define PIXELSIZE nWidth*nHeight69.const int Table_fv1[256]={-180,-179,-177,-176,-174,-173,-172,-170,-169,-167,-166,-165,-163,-162,-160,-159,-158,-156,-155,-153,-152,-151,-149,-148,-146,-145,-144,-142,-141,-139,-138,-137,-135,-134,-132,-131,-130,-128,-127,-125,-124,-123,-121,-120,-118,-117,-115,-114,-113,-111,-110,-108,-107,-106,-104,-103,-101,-100,-99,-97,-96,-94,-93,-92,-90,-89,-87,-86,-85,-83,-82,-80,-79,-78,-76,-75,-73,-72,-71,-69,-68,-66,-65,-64,-62,-61,-59,-58,-57,-55,-54,-52,-51,-50,-48,-47,-45,-44,-43,-41,-40,-38,-37,-36,-34,-33,-31,-30,-29,-27,-26,-24,-23,-22,-20,-19,-17,-16,-15,-13,-12,-10,-9,-8,-6,-5,-3,-2,0,1,2,4,5,7,8,9,11,12,14,15,16,18,19,21,22,23,25,26,28,29,30,32,33,35, 36,37,39,40,42,43,44,46,47,49,50,51,53,54,56,57,58,60,61,63,64,65,67,68, 70,71,72,74,75,77,78,79,81,82,84,85,86,88,89,91,92,93,95,96,98,99,100,10 2,103,105,106,107,109,110,112,113,114,116,117,119,120,122,123,124,126,12 7,129,130,131,133,134,136,137,138,140,141,143,144,145,147,148,150,151,15 2,154,155,157,158,159,161,162,164,165,166,168,169,171,172,173,175,176,17 8};70.const int Table_fv2[256]={-92,-91,-91,-90,-89,-88,-88,-87,-86,-86,-85,-84,-83,-83,-82,-81,-81,-80,-79,-78,-78,-77,-76,-76,-75,-74,-73,-73,-72,-71,-71,-70,-69,-68,-68,-67,-66,-66,-65,-64,-63,-63,-62,-61,-61,-60,-59,-58,-58,-57,-56,-56,-55,-54,-53,-53,-52,-51,-51,-50,-49,-48,-48,-47,-46,-46,-45,-44,-43,-43,-42,-41,-41,-40,-39,-38,-38,-37,-36,-36,-35,-34,-33,-33,-32,-31,-31,-30,-29,-28,-28,-27,-26,-26,-25,-24,-23,-23,-22,-21,-21,-20,-19,-18,-18,-17,-16,-16,-15,-14,-13,-13,-12,-11,-11,-10,-9,-8,-8,-7,-6,-6,-5,-4,-3,-3,-2,-1,0,0,1,2,2,3,4,5,5,6,7,7,8,9,10,10,11,12,12,13,14,15,15,16,17,17,18,19,2 0,20,21,22,22,23,24,25,25,26,27,27,28,29,30,30,31,32,32,33,34,35,35,36,3 7,37,38,39,40,40,41,42,42,43,44,45,45,46,47,47,48,49,50,50,51,52,52,53,5 4,55,55,56,57,57,58,59,60,60,61,62,62,63,64,65,65,66,67,67,68,69,70,70,7 1,72,72,73,74,75,75,76,77,77,78,79,80,80,81,82,82,83,84,85,85,86,87,87,8 8,89,90,90};71.const int Table_fu1[256]={-44,-44,-44,-43,-43,-43,-42,-42,-42,-41,-41,-41,-40,-40,-40,-39,-39,-39,-38,-38,-38,-37,-37,-37,-36,-36,-36,-35,-35,-35,-34,-34,-33,-33,-33,-32,-32,-32,-31,-31,-31,-30,-30,-30,-29,-29,-29,-28,-28,-28,-27,-27,-27,-26,-26,-26,-25,-25,-25,-24,-24,-24,-23,-23,-22,-22,-22,-21,-21,-21,-20,-20,-20,-19,-19,-19,-18,-18,-18,-17,-17,-17,-16,-16,-16,-15,-15,-15,-14,-14,-14,-13,-13,-13,-12,-12,-11,-11,-11,-10,-10,-10,-9,-9,-9,-8,-8,-8,-7,-7,-7,-6,-6,-6,-5,-5,-5,-4,-4,-4,-3,-3,-3,-2,-2,-2,-1,-1,0,0,0,1,1,1,2,2,2,3,3,3,4,4,4,5,5,5,6,6,6,7,7,7,8,8,8,9,9,9,10,10,11,11 ,11,12,12,12,13,13,13,14,14,14,15,15,15,16,16,16,17,17,17,18,18,18,19,19, 19,20,20,20,21,21,22,22,22,23,23,23,24,24,24,25,25,25,26,26,26,27,27,27, 28,28,28,29,29,29,30,30,30,31,31,31,32,32,33,33,33,34,34,34,35,35,35,36, 36,36,37,37,37,38,38,38,39,39,39,40,40,40,41,41,41,42,42,42,43,43};72.const int Table_fu2[256]={-227,-226,-224,-222,-220,-219,-217,-215,-213,-212,-210,-208,-206,-204,-203,-201,-199,-197,-196,-194,-192,-190,-188,-187,-185,-183,-181,-180,-178,-176,-174,-173,-171,-169,-167,-165,-164,-162,-160,-158,-157,-155,-153,-151,-149,-148,-146,-144,-142,-141,-139,-137,-135,-134,-132,-130,-128,-126,-125,-123,-121,-119,-118,-116,-114,-112,-110,-109,-107,-105,-103,-102,-100,-98,-96,-94,-93,-91,-89,-87,-86,-84,-82,-80,-79,-77,-75,-73,-71,-70,-68,-66,-64,-63,-61,-59,-57,-55,-54,-52,-50,-48,-47,-45,-43,-41,-40,-38,-36,-34,-32,-31,-29,-27,-25,-24,-22,-20,-18,-16,-15,-13,-11,-9,-8,-6,-4,-2,0,1,3,5,7,8,10,12,14,15,17,19,21,23,24,26,28,30,31,33,35,37,39,40,42,4 4,46,47,49,51,53,54,56,58,60,62,63,65,67,69,70,72,74,76,78,79,81,83,85,8 6,88,90,92,93,95,97,99,101,102,104,106,108,109,111,113,115,117,118,120,1 22,124,125,127,129,131,133,134,136,138,140,141,143,145,147,148,150,152,1 54,156,157,159,161,163,164,166,168,170,172,173,175,177,179,180,182,184,1 86,187,189,191,193,195,196,198,200,202,203,205,207,209,211,212,214,216,2 18,219,221,223,225};73.*len=3*nWidth*nHeight;74.if(!yuvBuf||!pRgbBuf)75.return false;76.const long nYLen=long(PIXELSIZE);77.const int nHfWidth=(nWidth>>1);78.if(nYLen<1||nHfWidth<1)79.return false;80.//Y data81.unsigned char*yData=yuvBuf;82.//v data83.unsigned char*vData=&yData[nYLen];84.//u data85.unsigned char*uData=&vData[nYLen>>2];86.if(!uData||!vData)87.return false;88.int rgb[3];89.int i,j,m,n,x,y,pu,pv,py,rdif,invgdif,bdif;90.m=-nWidth;91.n=-nHfWidth;92.bool addhalf=true;93.for(y=0;y<nHeight;y++){94.m+=nWidth;95.if(addhalf){96.n+=nHfWidth;97.addhalf=false;98.}else{99.addhalf=true;100.}101.for(x=0;x<nWidth;x++){102.i=m+x;103.j=n+(x>>1);104.py=yData[i];105.//search tables to get rdif invgdif and bidif106.rdif=Table_fv1[vData[j]];//fv1107.invgdif=Table_fu1[uData[j]]+Table_fv2[vData[j]];//fu1+fv2 108.bdif=Table_fu2[uData[j]];//fu2109.rgb[0]=py+rdif;//R110.rgb[1]=py-invgdif;//G111.rgb[2]=py+bdif;//B112.j=nYLen-nWidth-m+x;113.i=(j<<1)+j;114.//copy this pixel to rgb data115.for(j=0;j<3;j++)116.{117.if(rgb[j]>=0&&rgb[j]<=255){118.pRgbBuf[i+j]=rgb[j];119.}120.else{121.pRgbBuf[i+j]=(rgb[j]<0)?0:255;122.}123.}124.} 125.}126.return true; 127.}。
android中YUV转RGB的方法在一个外国网站上看到一段YUV转RGB的程序很不错,根据维基上的知识,方法应该是没问题的,自己也用过了,效果没问题。
首先说一下android上preview中每一帧的信息都是YUV420的,或者叫NV21,又或者叫YCbCr_420_SP (NV21),反正这么个东西呢,Y,U,V三个分量的数量比是4:1:1.也就是说每四个像素共用一对UV。
举个例子,如果是一个30*40的帧,那么有1200个Y分量,分别有300个U和300个V分量。
总共有1200*1.5这么多个值。
如果调用android中的onPreviewFrame(byte[] data, Camera camera)这个方法,data就是这一帧的数据。
数据是这么排列的:首先是Y分量(亮度) Y.... Y....接着是v,u分量成对出现 (v,u),(v,u)...... (v,u),(v,u)......然后讲一下共用的v,u问题比如图像是4*4的: Y11,Y12,Y13,Y14 Y21,Y22,Y23,Y24 Y31,Y32,Y33,Y34 Y41,Y42,Y43,Y44 V,U分量是这样的: (V1,U1),(V2,U2) (V3,U3),(V4,U4) 其中, Y11,Y12, Y21,Y22, 共用 (V1,U1) Y13,Y14 Y23,Y24 共用 (V2,U2) 其他的同理。
根据以上的介绍,我们就可以写程序了,程序如下:int yy,v,u; //三个分量 int frame_size=width*height;int p_y=x*width+y; //当前像素点y分量的位置int p_v=frame_size+(x >> 1) * width + (y& ~1) + 0; //当前像素点v分量的位置 int p_u=frame_size+(x >> 1) * width + (y& ~1) + 1; //当前像素点u分量的位置 yy=pSrc[p_y]; v=pSrc[p_v]; u=pSrc[p_u];yy = yy-16>0 ? yy-16 : 0; v= v-128>0?v-128:0;u=u-128>0?u-128:0; //开始转化成rgbunsigned char r = (unsigned char) ((116 *(int)yy + 160 *(int)v)/100);unsigned char g = (unsigned char) ((116 * (int)yy - 81 * (int)v - 39 * (int)u)/100); unsigned cha r b = (unsigned char) ((116 * (int)yy + 202 * (int)u)/100);。
(转)RGB、YUY2、YUYV、YVYU、UYVY、AYUV格式详解YUY2经常⽤于电视制式以及许多摄像头的输出格式.⽽我们在处理时经常需要将其转化为RGB进⾏处理,这⾥简单介绍下YUY2(YUV)与RGB 之间相互转化的关系:YUY2(YUV) To RGB:C = Y - 16D = U - 128E = V - 128R = clip(( 298 * C + 409 * E + 128) >> 8) G = clip(( 298 * C - 100 * D - 208 * E + 128) >> 8) B = clip(( 298 * C + 516 * D + 128) >> 8)其中 clip()为限制函数,将其取值限制在0-255之间.RGB To YUY2(YUV):Y = ( ( 66 * R + 129 * G + 25 * B + 128) >> 8) + 16U = ( ( -38 * R - 74 * G + 112 * B + 128) >> 8) + 128V = ( ( 112 * R - 94 * G - 18 * B + 128) >> 8) + 128上述两个公式在代码中的int YUV2RGB(void* pYUV, void* pRGB, int width, int height, bool alphaYUV, bool alphaRGB);int RGB2YUV(void* pRGB, void* pYUVX, int width, int height, bool alphaYUV, bool alphaRGB);函数中转换。
在诸如摄像头的数据获取中,我们往往需要直接在YUY2(YUV)空间上进⾏⼀些图象处理,我们希望能够在YUY2 (YUV)进⾏⼀些RGB上可以做到的处理。
这⾥已blending为例,将两张带有透明度的YUY2(YUV)图⽚进⾏叠加,以达到在RGB空间进⾏图像合成的效果。
YUV转RGB(转)
1 前言
自然界的颜色千变万化,为了给颜色一个量化的衡量标准,就需要建立色彩空间模型来描述各种各样的颜色,由于人对色彩的感知是一个复杂的生理和心理联合作用的过程,所以在不同的应用领域中为了更好更准确的满足各自的需求,就出现了各种各样的色彩空间模型来量化的描述颜色。
我们比较常接触到的就包括RGB / CMYK / YIQ / YUV / HSI等等。
对于数字电子多媒体领域来说,我们经常接触到的色彩空间的概念,主要是RGB , YUV这两种(实际上,这两种体系包含了许多种具体的颜色表达方式和模型,如sRGB, Adobe RGB, YUV422, YUV420 …), RGB是按三基色加光系统的原理来描述颜色,而YUV则是按照亮度,色差的原理来描述颜色。
即使只是RGB YUV这两大类色彩空间,所涉及到的知识也是十分丰富复杂的,自知不具备足够的相关专业知识,所以本文主要针对工程领域的应用及算法进行讨论。
2 YUV相关色彩空间模型
2.1 YUV 与YIQ YcrCb
对于YUV模型,实际上很多时候,我们是把它和YIQ / YCrCb模型混为一谈的。
实际上,YUV模型用于PAL制式的电视系统,Y表示亮度,UV并非任何单词的缩写。
YIQ模型与YUV模型类似,用于NTSC制式的电视系统。
YIQ颜色空间中的I和Q分量相当于将YUV空间中的UV分量做了一个33度的旋转。
YCbCr颜色空间是由YUV颜色空间派生的一种颜色空间,主要用于数字电视系统中。
从RGB到YCbCr的转换中,输入、输出都是8位二进制格式。
三者与RGB的转换方程如下:
RGB -> YUV:
实际上也就是:
Y=0.30R+0.59G+0.11B ,U=0.493(B-Y) ,V=0.877(R-Y)
RGB -> YIQ:
RGB -> YCrCb:
从公式中,我们关键要理解的一点是,UV / CbCr信号实际上就是蓝色差信号和红色差信号,进而言之,实际上一定程度上间接的代表了蓝色和红色的强度,理解这一点对于我们理解各种颜色变换处理的过程会有很大的帮助。
我们在数字电子多媒体领域所谈到的YUV格式,实际上准确的说,是以YcrCb
色彩空间模型为基础的具有多种存储格式的一类颜色模型的家族(包括YUV444 / YUV422 / YUV420 / YUV420P等等)。
并不是传统意义上用于PAL制模拟电视的YUV 模型。
这些YUV模型的区别主要在于UV数据的采样方式和存储方式,这里就不详述。
而在Camera Sensor中,最常用的YUV模型是YUV422格式,因为它采用4个字节描述两个像素,能和RGB565模型比较好的兼容。
有利于Camera Sensor和Camera controller的软硬件接口设计。
3 YUV2RGB快速算法分析
这里指的YUV实际是YcrCb了8 ) YUV2RGB的转换公式本身是很简单的,但是牵涉到浮点运算,所以,如果要实现快速算法,算法结构本身没什么好研究的了,主要是采用整型运算或者查表来加快计算速度。
首先可以推导得到转换公式为:
R = Y + 1.4075 *(V-128)
G = Y – 0.3455 *(U –128)– 0.7169 *(V –128)
B = Y + 1.779 *(U – 128)
3.1 整型算法
要用整型运算代替浮点运算,当然是要用移位的办法了,我们可以很容易得到下列算法:
u = YUVdata[UPOS] - 128;
v = YUVdata[VPOS] - 128;
rdif = v + ((v * 103) >> 8);
invgdif = ((u * 88) >> 8) +((v * 183) >> 8);
bdif = u +( (u*198) >> 8);
r = YUVdata[YPOS] + rdif;
g = YUVdata[YPOS] - invgdif;
b = YUVdata[YPOS] + bdif;
为了防止出现溢出,还需要判错计算的结果是否在0-255范围内,做类似下面的判断。
if (r>255)
r=255;
if (r<0)
r=0;
要从RGB24转换成RGB565数据还要做移位和或运算:
RGBdata[1] =( (r & 0xF8) | ( g >> 5) );
RGBdata[0] =( ((g & 0x1C) << 3) | ( b >> 3) );
3.2 部分查表法
查表法首先可以想到的就是用查表替代上述整型算法中的乘法运算。
rdif = fac_1_4075[u];
invgdif = fac_m_0_3455[u] + fac_m_0_7169[v];
bdif = fac_1_779[u];
这里一共需要4个1维数组,下标从0开始到255,表格共占用约1K的内存空间。
uv可以不需要做减128的操作了。
在事先计算对应的数组元素的值的时候计算在内就好了。
对于每个像素,部分查表法用查表替代了2次减法运算和4次乘法运算,4次移位运算。
但是,依然需要多次加法运算和6次比较运算和可能存在的赋值操作,相对第一种方法运算速度提高并不明显。
3.3 完全查表法
那么是否可以由YUV直接查表得到对应的RGB值呢?乍一看似乎不太可能,以最复杂的G的运算为例,因为G与YUV三者都相关,所以类似G=YUV2G[Y][U][V]这样的算法,一个三维下标尺寸都为256的数组就需要占用2的24次方约16兆空间,绝对是没法接受的。
所以目前多数都是采用部分查表法。
但是,如果我们仔细分析就可以发现,对于G我们实际上完全没有必要采用三维数组,因为Y只与UV运算的结果相关,与UV的个体无关,所以我们可以采用二次查表的方法将G的运算简化为对两个二维数组的查表操作,如下:
G = yig2g_table[ y ][ uv2ig_table[ u ][ v ] ];
而RB本身就只和YU或YV相关,所以这样我们一共需要4个8*8的二维表格,需要占用4乘2的16次方共256K内存。
基本可以接受。
但是对于手机这样的嵌入式运用来说,还是略有些大了。
进一步分析,我们可以看到,因为在手机等嵌入式运用上我们最终是要把数据转换成RGB565格式送到LCD屏上显示的,所以,对于RGB三分量来说,我们根本不需要8bit这么高的精度,为了简单和运算的统一起见,对每个分量我们其实只需要高6bit的数据就足够了,所以我们可以进一步把表格改为4个6*6的二维表格,这样一共只需要占用16K内存!在计算表格元素值的时候还可以把最终的溢出判断也事先做完。
最后的算法如下:
y = (YUVdata[Y1POS] >> 2);
u = (YUVdata[UPOS] >> 2);
v = (YUVdata[VPOS] >> 2);
r = yv2r_table[ y ][ v ];
g = yig2g_table[ y ][ uv2ig_table[ u ][ v ] ];
b = yu2b_table[ y ][ u ];
RGBdata[1] =( (r & 0xF8) | ( g >> 5) );
RGBdata[0] =( ((g & 0x1C) << 3) | ( b >> 3) );
这样相对部分查表法,我们增加了3次移位运算,而进一步减少了4次加法运算和6次比较赋值操作。
在计算表格元素数值的时候,要考虑舍入和偏移等因数使得计算的中间结果满足数组下标非负的要求,需要一定的技巧。
采用完全查表法,相对于第一种算法,最终运算速度可以有比较明显的提高,具体性能能提高多少,要看所在平台的CPU运算速度和内存存取速度的相对比例。
内存存取速度越快,用查表法带来的性能改善越明显。
在我的PC上测试的结果性能大约能
提高35%。
而在某ARM平台上测试只提高了约15%。
3.4 进一步的思考
实际上,上述算法:
RGBdata[1] =( (r & 0xF8) | ( g >> 5) );
RGBdata[0] =( ((g & 0x1C) << 3) | ( b >> 3) );
中的(r & 0xF8) 和( b >> 3) 等运算也完全可以在表格中事先计算出来。
另外,YU / YV的取值实际上不可能覆盖满6*6的范围,中间有些点是永远取不到的无输入,RB的运算也可以考虑用5*5的表格。
这些都可能进一步提高运算的速度,减小表格的尺寸。
另外,在嵌入式运用中,如果可能尽量将表格放在高速内存如SRAM中应该比放在SDRAM中更加能发挥查表法的优势。
4 RGB2YUV
目前觉得这个是没法将3维表格的查表运算化简为2维表格的查表运算了。
只能用部分查表法替代其中的乘法运算。
另外,多数情况下,我们需要的还是YUV2RGB的转换,因为从Sensor得到的数据通常我们会用YUV数据,此外JPG和MPEG实际上也是基于YUV格式编码的,所以要显示解码后的数据需要的也是YUV2RGB的运算8 )运气运气。