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

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文件见 这里,感谢提供者!


你可能感兴趣的:(图像处理)