YUYV码流中提取单帧并转为RGB图片

来源:互联网 发布:ubuntu配置网络 编辑:程序博客网 时间:2024/05/16 13:06

YUV和RGB都是一种颜色空间编码,要在这两种颜色空间之间进行转换,当然得知道它们的内存布局情况。下面先简单说下这两种颜色空间的编码情况。


RGB颜色空间应该是我们比较熟悉的了,分别是Red,Green,Blue三基色,每个分量占用一个字节,取值为0-255,三个0为黑,三个255为白,每三个字节为一个像素,当然在我们很多时候的处理中,可能会加入alpha通道,表示透明度,组合起来就是rgba四个通道。


YUV有两大类,平面格式和打包格式,具体这两种格式有什么区别呢,只有知道了具体的存储格式,我们才能对各个像素进行处理。平面格式就是把YUV的三个分量Y,U,V分开存放,例如放在三个数组中;而打包格式是把YUV的三个分量都放在一起,如按照一定的编码顺序存放在一个数组中。就这里我们说的YUYV格式来说,它就是一种打包格式,在内存布局中的具体方式是这样,YUYV YUYV……,每个分量占用一个字节,每两个字节也就是16位为一个像素,每两个像素一组就是一个巨像素,我们看到每两个像素也就是一个巨像素中有两个Y分量一个UV分量,这是因为YUYV是以4:2:2的格式打包的。其他yuv格式如下,注意:YUY2到Y211都是打包格式,而IF09到YVU9都是平面格式。关于yuv的详细介绍请看这里。


MEDIASUBTYPE_YUY2 YUY2格式,以4:2:2方式打包 
MEDIASUBTYPE_YUYV YUYV格式(实际格式与YUY2相同) 
MEDIASUBTYPE_YVYU YVYU格式,以4:2:2方式打包 
MEDIASUBTYPE_UYVY UYVY格式,以4:2:2方式打包 
MEDIASUBTYPE_AYUV 带Alpha通道的4:4:4 YUV格式 
MEDIASUBTYPE_Y41P Y41P格式,以4:1:1方式打包 
MEDIASUBTYPE_Y411 Y411格式(实际格式与Y41P相同)
MEDIASUBTYPE_Y211 Y211格式 
MEDIASUBTYPE_IF09 IF09格式 
MEDIASUBTYPE_IYUV IYUV格式 
MEDIASUBTYPE_YV12 YV12格式
MEDIASUBTYPE_YVU9 YVU9格式

上述的这些格式的处理方式都和YUYV的处理方式类似,只是它们的打包格式不同,从而YUV分量在内存中的具体编码方式也就不同,如果在看YUV的详细介绍时对所列举的各个采集方式不是很明白,可以自己动手试着画些草图或者是YUV的布局方式来帮助自己理解。它们不同的采集方式将会对应不同的处理方式,下面主要是对YUYV格式为例来做介绍:


我们先设想一张图片,它具有长和高两个变量,首先明确一点,在我们对图片进行处理转换时,这两个变量width和height是不会变得,也就是说一张RGB图像和一张YUV图像它们的长和高都是一样的,这一点对我们后续的处理很重要。同时它的长和高都是以像素点为基本单位,所以总的像素就是这张图的面积width*height,既然width和height都不变,那么一张图片的总像素点肯定就不会变了,所以图片的一行所占用的内存就为: width*(一个像素点占用的字节数), 对这些有个明确的概念后,我们处理起来就容易多了。一张RGB24图像不考虑alpha通道,那么它一个像素占用三个字节,考虑alpha通道RGB32,那么一个像素就占用四个字节。


对于YUV格式的图像来说,降低的只是它的色度采样率,也就是它的UV分量,而亮度分量Y是没有降低的,这里就不再做具体的说明。例如,4:4:4的采样方式是一个Y分量对应一个U和一个V分量,并没有降低;4:2:2是两个Y共用一个UV分量;4:2:0是四个Y共用一个UV分量。所以它的像素点数仍然没有变得。如,YUYV格式16位一个像素,一张图片占用的总内存为: width*height*2,其他格式内存占用情况可以看这里。


下面对YUYV码流进行提取单帧处理,现在我们应该很清楚的知道了,YUYV格式单帧图片所占用的内存应该就为width*height*2,yuv格式和rgb之间的转换都是有相应的公式的,网上一找很多的讲解,这里也不再介绍了。


下面是yuyv转rgb24的函数:

int convert_yuv_to_rgb_pixel(int y, int u, int v){ unsigned int pixel32 = 0; unsigned char *pixel = (unsigned char *)&pixel32; int r, g, b; r = y + (1.370705 * (v-128)); g = y - (0.698001 * (v-128)) - (0.337633 * (u-128)); b = y + (1.732446 * (u-128)); if(r > 255) r = 255; if(g > 255) g = 255; if(b > 255) b = 255; if(r < 0) r = 0; if(g < 0) g = 0; if(b < 0) b = 0; pixel[0] = r * 220 / 256; pixel[1] = g * 220 / 256; pixel[2] = b * 220 / 256; return pixel32;}int convert_yuv_to_rgb_buffer(unsigned char *yuv, unsigned char *rgb, unsigned int width, unsigned int height){ unsigned int in, out = 0; unsigned int pixel_16; unsigned char pixel_24[3]; unsigned int pixel32; int y0, u, y1, v; for(in = 0; in < width * height * 2; in += 4) {  pixel_16 =   yuv[in + 3] << 24 |   yuv[in + 2] << 16 |   yuv[in + 1] <<  8 |   yuv[in + 0];  y0 = (pixel_16 & 0x000000ff);  u  = (pixel_16 & 0x0000ff00) >>  8;  y1 = (pixel_16 & 0x00ff0000) >> 16;  v  = (pixel_16 & 0xff000000) >> 24;  pixel32 = convert_yuv_to_rgb_pixel(y0, u, v);  pixel_24[0] = (pixel32 & 0x000000ff);  pixel_24[1] = (pixel32 & 0x0000ff00) >> 8;  pixel_24[2] = (pixel32 & 0x00ff0000) >> 16;  rgb[out++] = pixel_24[0];  rgb[out++] = pixel_24[1];  rgb[out++] = pixel_24[2];  pixel32 = convert_yuv_to_rgb_pixel(y1, u, v);  pixel_24[0] = (pixel32 & 0x000000ff);  pixel_24[1] = (pixel32 & 0x0000ff00) >> 8;  pixel_24[2] = (pixel32 & 0x00ff0000) >> 16;  rgb[out++] = pixel_24[0];  rgb[out++] = pixel_24[1];  rgb[out++] = pixel_24[2]; } return 0;}

下面是测试代码:

    //因为只是简单的测试用,所以代码写的有点随意    QFile file("G:/Documents/QtProject/yuyvSplit/flower_yuyv422_352X288.yuv");    if(!file.open(QIODevice::ReadOnly))    {        qDebug() << "open file failed.";        return;    }    int width = 352;    int height = 288;    int frames  = 250;  //yuyv码流总的帧数    int singleCounts = width * height * 2;    int yuyvCounts = singleCounts * 250;int yuyvPitch = width * 2;    //yuyv 缓存一行的大小    int rgb24Size = width * height * 3;int rgb32Size = width * height * 4;int rgb32Pitch = width * 4;   //rgb32 缓存一行的大小    int rgb24Pitch = width * 3;    qDebug() << "yuyvCounts: " << yuyvCounts << "\nfileSize: " << file.size();    for(int i = 0; i < 1; ++i )    {        char *singleFrame = new char[singleCounts]();        int ret = file.read(singleFrame, singleCounts);        qDebug() << "read return size: " << ret << " signleCounts: " << singleCounts;        char *rgb24 = new char[rgb24Size]();        convert_yuv_to_rgb_buffer((unsigned char*)singleFrame, (unsigned char*)rgb24, width, height);        //不用QT的话,这里可以直接把图片rgb24buffer写入文件image.bmp中,        //注意保存格式,bmp是位图,而类似jpg,png之类的是经压缩过的。        QImage *img = new QImage((unsigned char*)rgb24, width, height, rgb24Pitch, QImage::Format_RGB888);        //这里把图像呈现出来观察        ui->image->setPixmap(QPixmap::fromImage(*img));delete[] singleFrame;delete[] rgb32;    }    file.close();

YUV码流文件可以用这个工具打开,可以直接从中看到yuv码流的帧数,用到的YUYV文件见这里,感谢提供者!


1 0