现在的位置: 首页 > 综合 > 正文

YUV格式转换为RGB(基于opencv)

2017年12月12日 ⁄ 综合 ⁄ 共 4466字 ⁄ 字号 评论关闭

之前写代码过程中需要将YUV格式视频中每帧提取出来,然后保存为图片。网上普遍有两种方法,第一种是通过opencv自带cvCvtColor,但是这种方法有bug,得到的图片会泛白。第二种方法是公式法。

法一:opencv自带cvCvtColor

说明:这种方法会出现图片“泛白”,具体原因网上是说cvCvtColor这个函数左右协议不同,不太懂。

代码:

 

void FileWriteFrames(){
	char *filename = "E:\\openCV\\zhang\\yuvSource\\football_cif.yuv";
	ifstream readMe(filename, ios::in | ios::binary);  // 打开并读yuv数据
	IplImage *image, *rgbimg, *yimg, *uimg, *vimg, *uuimg, *vvimg;
	cvNamedWindow("yuv",CV_WINDOW_AUTOSIZE);
	rgbimg = cvCreateImage(cvSize(ISizeX, ISizeY), IPL_DEPTH_8U, 3);
	image = cvCreateImage(cvSize(ISizeX, ISizeY), IPL_DEPTH_8U, 3);
    
	yimg = cvCreateImageHeader(cvSize(ISizeX, ISizeY), IPL_DEPTH_8U, 1);    // 亮度分量
	uimg = cvCreateImageHeader(cvSize(ISizeX/2, ISizeY/2), IPL_DEPTH_8U, 1);  // 这两个都是色度分量
	vimg = cvCreateImageHeader(cvSize(ISizeX/2, ISizeY/2), IPL_DEPTH_8U, 1);
    
	uuimg = cvCreateImage(cvSize(ISizeX, ISizeY), IPL_DEPTH_8U, 1);
	vvimg = cvCreateImage(cvSize(ISizeX, ISizeY), IPL_DEPTH_8U, 1);
	int nframes;
	for(nframes = 0; nframes < FCount; nframes ++){
		char nframesstr[20];
		readMe.read((char*)Y[nframes],ISizeX*ISizeY);
		//readMe.seekg(-ISizeX*ISizeY, ios::cur);
		//readMe.read((char*)buf[nframes],ISizeX*ISizeY+ISizeX/2*ISizeY/2+ISizeX/2*ISizeY/2);
		readMe.read((char*)buf[nframes],ISizeX/2*ISizeY/2);
		readMe.read((char*)buf2[nframes],ISizeX/2*ISizeY/2);
		cvSetData(yimg,Y[nframes],ISizeX);
		//cvSetData(uimg,buf[nframes] + ISizeX*ISizeY, ISizeX/2);
		cvSetData(uimg,buf[nframes], ISizeX/2);
		cvSetData(vimg,buf2[nframes], ISizeX/2);

		
		cvResize(uimg,uuimg, CV_INTER_LINEAR);
		cvResize(vimg,vvimg, CV_INTER_LINEAR);
		cvMerge(yimg,uuimg,vvimg,NULL,image);   // 合并单通道为三通道
		cvCvtColor(image,rgbimg,CV_YUV2BGR);    
		
		stringstream ss;  // 类型转换统一转换为char* 类型
		ss << nframes;
		ss << ".jpg" ;
		ss >> nframesstr;
		cvShowImage("yuv", rgbimg);
		cvSaveImage(nframesstr,rgbimg);
		int c = cvWaitKey(30);
		if((char)c == 27)
		{
			break;
		}
	}
	readMe.close();
	cvReleaseImage(&uuimg);
    cvReleaseImage(&vvimg);
	cvReleaseImageHeader(&yimg);
	cvReleaseImageHeader(&uimg);
	cvReleaseImageHeader(&vimg);
	cvReleaseImage(&image);
	cvDestroyWindow("yuv");	
}

法二:公式法

代码:

bool YUV420_To_BGR24(unsigned char *puc_y, unsigned char *puc_u, unsigned char *puc_v, unsigned char *puc_rgb, int width_y, int height_y)
{
    if (!puc_y || !puc_u || !puc_v || !puc_rgb)
    {
        return false;
    }
    
    //初始化变量
    int baseSize = width_y * height_y;
    int rgbSize = baseSize * 3;

    BYTE* rgbData  = new BYTE[rgbSize];
    memset(rgbData, 0, rgbSize);

    /* 变量声明 */
    int temp = 0;

    BYTE* rData = rgbData;                  //r分量地址
    BYTE* gData = rgbData + baseSize;       //g分量地址
    BYTE* bData = gData   + baseSize;       //b分量地址

    int uvIndex =0, yIndex =0;

    //YUV->RGB 的转换矩阵
    //double  Yuv2Rgb[3][3] = {1, 0, 1.4022,
    //    1, -0.3456, -0.7145,
    //    1, 1.771,   0};

    for(int y=0; y < height_y; y++)
    {
        for(int x=0; x < width_y; x++)
        {
            uvIndex        = (y>>1) * (width_y>>1) + (x>>1);
            yIndex         = y * width_y + x;

            /* r分量 */
            temp          = (int)(puc_y[yIndex] + (puc_v[uvIndex] - 128) * 1.4022);
            rData[yIndex] = temp<0 ? 0 : (temp > 255 ? 255 : temp);

            /* g分量 */
            temp          = (int)(puc_y[yIndex] + (puc_u[uvIndex] - 128) * (-0.3456) +
                (puc_v[uvIndex] - 128) * (-0.7145));
            gData[yIndex] = temp < 0 ? 0 : (temp > 255 ? 255 : temp);

            /* b分量 */
            temp          = (int)(puc_y[yIndex] + (puc_u[uvIndex] - 128) * 1.771);
            bData[yIndex] = temp < 0 ? 0 : (temp > 255 ? 255 : temp);
        }
    }

    //将R,G,B三个分量赋给img_data
    int widthStep = width_y*3;
    for (int y = 0; y < height_y; y++)
    {
        for (int x = 0; x < width_y; x++)
        {
			puc_rgb[y * widthStep + x * 3 + 2] = rData[y * width_y + x];   //R
            puc_rgb[y * widthStep + x * 3 + 1] = gData[y * width_y + x];   //G
            puc_rgb[y * widthStep + x * 3 + 0] = bData[y * width_y + x];   //B
        }
    }

    if (!puc_rgb)
    {
        return false;
    }
    delete [] rgbData;
    return true;
}

IplImage* YUV420_To_IplImage(unsigned char* pYUV420, int width, int height)
{
    if (!pYUV420)
    {
		return NULL;
    }

    //初始化变量
    int baseSize = width*height;
    int imgSize = baseSize*3;
	BYTE* pRGB24  = new BYTE[imgSize];
	memset(pRGB24,  0, imgSize);

    /* 变量声明 */
    int temp = 0;

    BYTE* yData = pYUV420;                  //y分量地址
    BYTE* uData = pYUV420 + baseSize;       //u分量地址
    BYTE* vData = uData  + (baseSize>>2);   //v分量地址

    if(YUV420_To_BGR24(yData, uData, vData, pRGB24, width, height) == false || !pRGB24)
    {
		return NULL;
	}

    IplImage *image = cvCreateImage(cvSize(width, height), 8,3);
    memcpy(image->imageData, pRGB24, imgSize);

    if (!image)
    {
        return NULL;
    }

    delete [] pRGB24;
    return image;
}

void FileWriteFrames(){
	char *filename = "E:\\openCV\\zhang\\yuvSource\\FOOTBALL_352x288_30_orig_01.yuv";
	ifstream readMe(filename, ios::in | ios::binary);  // 打开并读yuv数据
	int nframes;
	for(nframes = 0; nframes < FCount; nframes ++){
		char nframesstr[20];
		readMe.read((char*)Y[nframes],ISizeX*ISizeY);
		readMe.seekg(-ISizeX*ISizeY, ios::cur);
		readMe.read((char*)buf[nframes],ISizeX*ISizeY+ISizeX/2*ISizeY/2+ISizeX/2*ISizeY/2);  
		IplImage *rgbimg = YUV420_To_IplImage(buf[nframes], ISizeX, ISizeY);
		stringstream ss;  // 类型转换统一转换为char* 类型
		ss << nframes;
		ss << ".jpg" ;
		ss >> nframesstr;
		cvShowImage("yuv", rgbimg);
		cvSaveImage(nframesstr,rgbimg);
		int c = cvWaitKey(30);
		if((char)c == 27)
		{
			break;
		}
	}
	readMe.close();
}

完整代码见:http://download.csdn.net/detail/lu597203933/7362687

参见blog:http://blog.csdn.net/dreamd1987/article/details/7259479#

抱歉!评论已关闭.