YUV420数据转为IplImage格式

47 篇文章 35 订阅 ¥9.90 ¥99.00

1 YUV420理解

    首先我们需要对YUV420的数据有一个了解,很多人对YUV数据格式不清楚,以至于在做视频的时候出现了一些不可预知的错误(比如说图像带有点、颜色不对等)。图文讲解,
在YUV420中,一个像素点对应一个Y,一个2X2的小方块对应一个U和V。对于所有YUV420图像,它们的Y值排列是完全相同的,因为只有Y的图像就是灰度图像。YUV420sp与YUV420p的数据格式它们的UV排列在原理上是完全不同的。
420p它是先把U存放完后,再存放V,也就是说UV它们分别是连续的。而420sp它是UV、UV这样交替存放的。(见下图)
有了上面的理论,我就可以准确的计算出一个YUV420在内存中存放的大小。
width * hight =Y(总和)
U = Y / 4
V = Y / 4
所以YUV420 数据在内存中的长度是 width * hight * 3 / 2,

假设一个分辨率为8X4的YUV图像,它们的格式如下图:

                                  YUV420sp格式如下图                                                                             YUV420p数据格式如下图

                                    
                                       

2 函数使用

(1)cvSetData()
函数原型:CVAPI(void)  cvSetData( CvArr* arr, void* data, int step );
arr        数组头
data    用户数据data
step    一行所占的字节数
该函数将用户数据data指派给数组头arr,step为一行所占的字节数。
(2)cvResize()
函数原型:CVAPI(void)  cvResize( const CvArr* src, CvArr* dst,int interpolation CV_DEFAULT( CV_INTER_LINEAR ));
src        源图像
dst        目标图像
interpolation 修改、插补的方法,取值如下:
·CV_INTER_NN - 最近-邻居插补
·CV_INTER_LINEAR - 双线性插值(默认方法)
·CV_INTER_AREA - 像素面积相关重采样。当缩小图像时,该方法可以避免波纹的出现。当放大图像时,类似于方法CV_INTER_NN。
·CV_INTER_CUBIC - 双三次插值。
函数cvResize 重新调整图像src(或它的ROI),使它精确匹配目标dst(或其ROI)。这里需要说明的是,cvResize可以用来调整3通道图像(如RGB图像)和单通道图像的大小。
(3)cvMerge()
函数原型:CVAPI(void)  cvMerge( const CvArr* src0, const CvArr* src1,const CvArr* src2, const CvArr* src3,CvArr* dst );
cvMerge()是cvSplit()的逆运算。数组src0,src1,src2和src3将被合并到数组dst中。当然,dst应该与源数组具有相同的数据类型和尺寸,但它可以有
两个,三个,四个通道,未使用的源图像参数可设置为NULL。
(4)cvCvtColor()
CVAPI(void)  cvCvtColor( const CvArr* src, CvArr* dst, int code );
src    原始图像
dst    目标图像
code    色彩空间转换的模式
当数据类型一致时,它将图像从一个颜色空间(通道的数值)转换到另一个[Wharton71].具体转换类型由参数code来指定。

3 源代码

(1)

// YUV420数据转为IplImage格式.cpp : 定义控制台应用程序的入口点。
//
#define CRT_SECURE_NO_WARNINGS
#include <windows.h>
#include "stdafx.h"
#include <iostream>
#include "opencv2/core/core.hpp"
#include "opencv2/highgui/highgui.hpp"
#include "opencv2/imgproc/imgproc_c.h"
#include "opencv2/imgproc/types_c.h"
#include "CvvImage.h"

using namespace std;
using namespace cv;

IplImage* YUV420_To_IplImage_Opencv(unsigned char* pYUV420, int width, int height){

	if (!pYUV420)
	{
		return NULL;
	}

	IplImage *yuvImg, *rgbImg, *yImg, *uImg, *vImg, *uuImg, *vvImg;

	int nWidth = width;
	int nHeight = height;

	rgbImg = cvCreateImage(cvSize(nWidth, nHeight), IPL_DEPTH_8U, 3);
	yuvImg = cvCreateImage(cvSize(nWidth, nHeight), IPL_DEPTH_8U, 3);

	//只有Y的图像就是灰度图像
	yImg = cvCreateImageHeader(cvSize(nWidth, nHeight), IPL_DEPTH_8U, 1);
	uImg = cvCreateImageHeader(cvSize(nWidth / 2, nHeight / 2), IPL_DEPTH_8U, 1);
	vImg = cvCreateImageHeader(cvSize(nWidth / 2, nHeight / 2), IPL_DEPTH_8U, 1);

	uuImg = cvCreateImage(cvSize(nWidth, nHeight), IPL_DEPTH_8U, 1);
	vvImg = cvCreateImage(cvSize(nWidth, nHeight), IPL_DEPTH_8U, 1);

	cvSetData(yImg, pYUV420, nWidth);
	cvSetData(uImg, pYUV420 + nWidth*nHeight, nWidth / 2);
	cvSetData(vImg, pYUV420 + long(nWidth*nHeight*1.25), nWidth / 2);

	cvResize(uImg, uuImg, CV_INTER_LINEAR);
	cvResize(vImg, vvImg, CV_INTER_LINEAR);

	cvMerge(yImg, uuImg, vvImg, NULL, yuvImg);

	cvCvtColor(yuvImg, rgbImg, CV_YCrCb2RGB);

	cvReleaseImage(&uuImg);
	cvReleaseImage(&vvImg);
	cvReleaseImageHeader(&yImg);
	cvReleaseImageHeader(&uImg);
	cvReleaseImageHeader(&vImg);
	cvReleaseImage(&yuvImg);

	if (!rgbImg)
	{
		return NULL;
	}

	return rgbImg;
}

int _tmain(int argc, _TCHAR* argv[])
{

	BYTE * yuvBuf = new BYTE[(640 * 360 * 3) / 2];

	FILE* f = fopen("sintel_640_360.yuv", "r+b");

	if (!f)
	{
		return -1;
	}
	fread(yuvBuf, 1, (640 * 360 * 3) / 2, f);
	fclose(f);

	IplImage* pImg = YUV420_To_IplImage_Opencv(yuvBuf, 640, 360);
	cvNamedWindow("test");
	cvShowImage("test", pImg);

	printf("hello...\n");
	system("pause");

	return 0;
}

(2)采用数学转换的方式,代码如下:

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 yIndex = 0, uvIndex = 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;

}

//yuv转rgb的函数
void YUV420_To_RGB(char* pYUV, unsigned char* pRGB, int width, int height)
{
	char* pY = pYUV;
	char* pU = pYUV + height*width;
	char* pV = pU + (height*width / 4);


	unsigned char* pBGR = NULL;
	unsigned char R = 0;
	unsigned char G = 0;
	unsigned char B = 0;
	char Y = 0;
	char U = 0;
	char V = 0;
	double tmp = 0;
	for (int i = 0; i < height; ++i)
	{
		for (int j = 0; j < width; ++j)
		{
			pBGR = pRGB + i*width * 3 + j * 3;

			Y = *(pY + i*width + j);
			U = *pU;
			V = *pV;

			//B  
			tmp = MB(Y, U, V);
			//B = (tmp > 255) ? 255 : (char)tmp;  
			//B = (B<0) ? 0 : B;  
			B = (unsigned char)tmp;
			//G  
			tmp = MG(Y, U, V);
			//G = (tmp > 255) ? 255 : (char)tmp;  
			// G = (G<0) ? 0 : G;  
			G = (unsigned char)tmp;
			//R  
			tmp = MR(Y, U, V);
			//R = (tmp > 255) ? 255 : (char)tmp;  
			//R = (R<0) ? 0 : R;  
			R = (unsigned char)tmp;


			*pBGR = R;
			*(pBGR + 1) = G;
			*(pBGR + 2) = B;


			if (i % 2 == 0 && j % 2 == 0)
			{
				*pU++;
				//*pV++;  
			}
			else
			{
				if (j % 2 == 0)
				{
					*pV++;
				}
			}
		}

	}
}



  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值