之前写代码过程中需要将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#