YUV420与YUV444互转,YUV420与YUV444读取和保存,YUV的显示和播放功能

YUV420与YUV444互转,YUV420与YUV444读取和保存,YUV的显示和播放功能


【尊重原创,转载请注明出处】:https://blog.csdn.net/guyuealian/article/details/82454945

    OpenCV提供了RGB与YUV420/YUV444互转的接口:cvtColor(),但根尴尬OpenCV就是没有提供YUV444与YUV420互转的接口,如果你想用OpenCV转换,那就只能使用间接的方法了,将YUV420转为BGR(CV_YUV2BGR_I420),然后再将BGR转为YUV444(CV_BGR2YUV),这很蛋疼!效率太低了。

    实质上,只要我们理解了YUV420与YUV444的数据格式,完全可以自己转换的,下面使用C++实现YUV420与YUV444读取和保存,以及UV420与YUV444互转,并实现YUV的显示和播放功能。

    YUV444和YUV420的测试文件,可以这里下载:https://download.csdn.net/download/guyuealian/10697442


目录

C++读写YUV444和YUV420,现实YUV444与YUV420互转,支持OpenCV显示

一、YUV数据格式

1、YUV分三种采样方式:

2、YUV的存储格式:

二、YUV文件的读取和保存

1、读取YUV文件

2、保存YUV文件

三、C++实现YUV420与YUV444互转

1、YUV420转YUV444

2、YUV444转YUV420

四、显示和播放YUV文件

1、显示YUV图像

2、播放YUV数据 

五、完整的代码

参考资料:


一、YUV数据格式

     YUV有三个分量:Y(Luminance/Luma:亮度)、U和V表示色差,体现的是图片的色彩信息。相对于RGB彩色空间,将亮度信息和色彩信息分离。这种编码模式也更加适应于人眼,据研究表明,人眼对亮度信息比色彩信息更加敏感。而YUV下采样就是根据人眼的特点,将人眼相对不敏感的色彩信息进行压缩采样,得到相对小的文件进行播放和传输。与YUV相像YCbCr其实与其有少许不同,体现在参数的大小上,本质上都是将亮度信息与色彩信息相分开。


1、YUV分三种采样方式:

YUV420与YUV444互转,YUV420与YUV444读取和保存,YUV的显示和播放功能_第1张图片

    YUV444:对于每一个像素都对应一个Y分量、一个U分量、一个V分量。

    YUV422:对于一个像素都对应一个Y分量,但是每两个像素(或者说Y分量)对应一个U分量和一个V分量。

    YUV420:对于一个像素都对应一个Y分量,但是每四个像素(或者说Y分量)对应一个U分量和一个V分量。

2、YUV的存储格式:

    YUYUV在存储时是以数组的形式存储的,可以看做连续的三个一维数组。三个数组分别单独存储Y、U、V分量。以一幅100×100的YUV444图像为例,下图表示的YUV444的存储格式YUV420的存储格式

YUV420与YUV444互转,YUV420与YUV444读取和保存,YUV的显示和播放功能_第2张图片   

  从总数据量来看,YUV444需要存储100×100×3个值,YUV422需要存储100×100×2个值,YUV420需要存储100×100×3/2个值。


二、YUV文件的读取和保存

1、读取YUV文件

    YUV文件实质上是保存了图像的YUV格式的原始数据,并没有像png、jpg等图像格式对图像的数据进行压缩。这也就是为什么,YUV格式的文件普遍大比较大了。既然 YUV文件是未经压缩的原始数据,那读取和保存YUV文件就十分简单了。基本方法是只需要调用C++的fopen()函数打开YUV文件获得指向该流的文件指针,然后使用fread()从文件流中直接读取YUV的数据即可。

    下面是实现读取YUV444和YUV420的完整代码:

	/*********************************************************	
	@brief         : 读取yuv图像文件,若输入的是多帧的图像文件,如yuv视频文件,则返回最后一帧的数据
	@param fileYUV : YUV文件
	@param w       : 宽width
	@param h       : 高height
	@param colorType: 颜色空间类型:CS_YUV444,CS_YUV420
	@return        : unsigned char* 返回YUV数据
	********************************************************/	
    unsigned char* read_yuvImage(string fileYUV, int w, int h, ColorSpace colorType) {
		FILE * pFileIn;      //输入CS_YUV444文件
		if (NULL == (pFileIn = fopen(fileYUV.c_str(), "rb"))) {
			printf("File input is can't open!\n");
			fclose(pFileIn);
			return nullptr;
		}
		int bufLen = 0;
		if (colorType == CS_YUV444) {//定义保存CS_YUV444的数组
			bufLen = w * h * 3;
		}
		else if (colorType == CS_YUV420) {//定义保存YUV420的数组
			bufLen = w*h * 3 / 2;
		}
		unsigned char* yuvBuf = new unsigned char[bufLen];
		//fread(yuvBuf, bufLen * sizeof(unsigned char), 1, pFileIn);//返回第一帧
		while (fread(yuvBuf, bufLen * sizeof(unsigned char), 1, pFileIn))//返回最后一帧
		{
		}
		fclose(pFileIn);
		return yuvBuf;
	}

    注意这里定义一个枚举类型ColorSpace

	enum ColorSpace {
		CS_YUV444,
		CS_YUV420
	};

说明:

    上面read_yuvImage函数若输入的是多帧的图像文件,如yuv视频文件,则返回最后一帧的YUV数据,当然了,你稍微改改就可以实现读取每一帧YUV数据了。下面章节会增加显示YUV数据部分,会借助OpenCV实现播放YUV的视频文件。


2、保存YUV文件

    保存YUV文件也十分简单,调用C++的fopen()函数打开YUV文件获得指向该流的文件指针,然后使用fwrite()把YUV的数据流保存到打开的文件即可。

    下面是实现保存YUV444和YUV420数据的完整代码:

	/*********************************************************
	@brief         : 保存YUV数据流
	@param fileYUV : 输出YUV文件的路径
	@param inbuf   : 需要保存的YUV数据流
	@param w       : 宽width
	@param h       : 高height
	@param colorType: 颜色空间类型:CS_YUV444,CS_YUV420
	@return        : void
	********************************************************/
	void write_yuvImage(string fileYUV, unsigned char* inframe, int w, int h, ColorSpace colorType) {
		FILE * pFileOut;     //输出YUV文件
		if (NULL == (pFileOut = fopen(fileYUV.c_str(), "wb"))) {
			printf("File output is can't open!\n");
			fclose(pFileOut);
			return;
		}
		int bufLen = 0;
		if (colorType == CS_YUV444) {//定义保存CS_YUV444的数组
			bufLen = w * h * 3;
		}
		else if (colorType == CS_YUV420) {//定义保存YUV420的数组
			bufLen = w*h * 3 / 2;
		}
		fwrite(inframe, bufLen * sizeof(unsigned char), 1, pFileOut);
		fclose(pFileOut);
	}

三、C++实现YUV420与YUV444互转

    OpenCV提供了RGB与YUV420/YUV444互转的接口:cvtColor(),但根尴尬OpenCV就是没有提供YUV444与YUV420互转的接口,如果你想用OpenCV转换,那就只能间接的方法了,将YUV420转为BGR(CV_YUV2BGR_I420),然后再将BGR转为YUV444(CV_BGR2YUV),这很蛋疼!效率太低了。

    实质上,只要我们理解了YUV420与YUV444的数据格式,完全可以自己转换的,下面使用C++实现YUV420与YUV444互转,这部分不依赖OpenCV,因此效率十分高的!!!


1、YUV420转YUV444

    YUV420转YUV444,实际就是对UV两个分量色度进行上采样,最为简单的实现思路是直接填充。

    以U分量为例,其序列如下

    对U进行插值,每个田字格四个位置使用一个U值。

YUV420与YUV444互转,YUV420与YUV444读取和保存,YUV的显示和播放功能_第3张图片

    在代码中,可以直接通过对数组循环赋值即可完成。对V分量的操作相同。 填充方式实现简单,但效果较差,一般可以通过插值来完成上采样。

   下面是C++实现YUV420转YUV444的代码:

	void YUV420TOYUV444(unsigned char *inbuf, unsigned char *outbuf, int w, int h) {
		unsigned char *srcY = NULL, *srcU = NULL, *srcV = NULL;
		unsigned char *desY = NULL, *desU = NULL, *desV = NULL;
		srcY = inbuf;//Y
		srcU = srcY + w * h;//U
		srcV = srcU + w * h / 4;;//V

		desY = outbuf;
		desU = desY + w * h;
		desV = desU + w * h;
		memcpy(desY, srcY, w * h * sizeof(unsigned char));//Y分量直接拷贝即可
	   //UV分量转换
		int i, j;
		for (i = 0; i < h; i += 2) {//行
			for (j = 0; j < w; j += 2) {//列
										//U
				desU[i*w + j] = srcU[i / 2 * w / 2 + j / 2];
				desU[i*w + j + 1] = srcU[i / 2 * w / 2 + j / 2];
				desU[(i + 1)*w + j] = srcU[i / 2 * w / 2 + j / 2];
				desU[(i + 1)*w + j + 1] = srcU[i / 2 * w / 2 + j / 2];
				//V
				desV[i*w + j] = srcV[i / 2 * w / 2 + j / 2];
				desV[i*w + j + 1] = srcV[i / 2 * w / 2 + j / 2];
				desV[(i + 1)*w + j] = srcV[i / 2 * w / 2 + j / 2];
				desV[(i + 1)*w + j + 1] = srcV[i / 2 * w / 2 + j / 2];
			}
		}
	}

2、YUV444转YUV420

    与YUV420与YUV444思路类似的,我们反过来计算,将YUV444下采样为YUV420即可,

    下面是C++实现YUV444转YUV420的代码:

	void YUV444TOYUV420(unsigned char *inbuf, unsigned char *outbuf, int w, int h) {
		unsigned char *srcY = NULL, *srcU = NULL, *srcV = NULL;
		unsigned char *desY = NULL, *desU = NULL, *desV = NULL;
		srcY = inbuf;//Y
		srcU = srcY + w * h;//U
		srcV = srcU + w * h;//V

		desY = outbuf;
		desU = desY + w * h;
		desV = desU + w * h / 4;

		int half_width = w / 2;
		int half_height = h / 2;
		memcpy(desY, srcY, w * h * sizeof(unsigned char));//Y分量直接拷贝即可
		 //UV
		for (int i = 0; i < half_height; i++) {
			for (int j = 0; j < half_width; j++) {
				*desU = *srcU;
				*desV = *srcV;
				desU++;
				desV++;
				srcU += 2;
				srcV += 2;
			}
			srcU = srcU + w;
			srcV = srcV + w;
		}
	}

四、显示和播放YUV文件

1、显示YUV图像

    YUV数据需要转到RGB才能显示正常,显示部分需要OpenCV的支持,OpenCV的配置,就不说啦,自己搞搞吧!!!这里提供有一个鄙人已经写好的cv_imageshow()函数,可以实现将输入的YUV数据流转为BGR格式,再用OpenCV的imshow()直接进行显示。注意,这里提供cv_imageshow()只能显示一帧数据,即你输入的inbuf数据流必须是一帧的YUV数据。

	/*********************************************************
	@brief         : 显示YUV图片,这里会将YUV转到RGB上进行显示
	@param winname : 窗口显示的名称
	@param inbuf   : YUV数据流,直接将read_yuvImage返回的YUV数据流传入即可显示
	@param w       : 宽width
	@param h       : 高height
	@param colorType: 颜色空间类型:CS_YUV444,CS_YUV420
	@return        : void
	********************************************************/
	void cv_imageshow(string winname, unsigned char* inframe, int w, int h, ColorSpace colorType) {
		if (colorType == CS_YUV444) {//定义保存CS_YUV444的数组
			cv::Mat yuvMat = cv::Mat::zeros(h, w, CV_8UC3);
			std::vector yuvChannels;
			cv::split(yuvMat, yuvChannels);
			yuvChannels.at(0).data = (unsigned char*)inframe;//Y
			yuvChannels.at(1).data = (unsigned char*)inframe + w * h;//U
			yuvChannels.at(2).data = (unsigned char*)inframe + w * h * 2;//V
			cv::merge(yuvChannels, yuvMat);
			cv::Mat bgrMat;
			cv::cvtColor(yuvMat, bgrMat, CV_YUV2BGR);
			cv::imshow(winname, bgrMat);
			cv::waitKey(30);
		}
		else if (colorType == CS_YUV420) {//定义保存YUV420的数组
			int bufLen = w*h * 3 / 2;
			cv::Mat yuvImg = cv::Mat::zeros(h * 3 / 2, w, CV_8UC1);
			memcpy(yuvImg.data, inframe, bufLen * sizeof(unsigned char));
			//cv::imshow(winname, yuvImg);
			//cv::waitKey(30);
			cv::Mat bgrImg= cv::Mat::zeros(h , w, CV_8UC3);
			cv::cvtColor(yuvImg, bgrImg, CV_YUV2BGR_I420);
			cv::imshow(winname, bgrImg);
			cv::waitKey(30);
		}
	}

2、播放YUV数据 

    上面提供的cv_imageshow()为了显示图片,所以只能显示一帧数据,即你输入的inbuf数据流必须是一帧的YUV数据。但很多YUV文件实质上是多帧的视频文件,要实现YUV文件的播放功能也很简单:循环读取YUV数据流,使用OpenCV的imshow()一帧一帧的显示,就实现了播放YUV的功能了:

    下面是C++实现的cvPlayYUV444和cvPlayYUV420的函数,可以实现播放YUV444和YUV420的文件:

	/*********************************************************
	@brief         : 播放YUV420/YUV444文件
	@param fileYUV : YUV文件
	@param w       : 宽width
	@param h       : 高height
	@return        : void
	********************************************************/
	void cvPlayYUV444(string fileYUV, int w, int h) {
		FILE* pFileIn = fopen(fileYUV.c_str(), "rb+");
		int bufLen = w * h * 3;
		unsigned char* pYuvBuf = new unsigned char[bufLen];
		cv::Mat yuvMat = cv::Mat::zeros(h, w, CV_8UC3);
		std::vector yuvChannels;
		cv::split(yuvMat, yuvChannels);
		while (fread(pYuvBuf, bufLen * sizeof(unsigned char), 1, pFileIn))
		{
			//cv::Mat Y(i, w, CV_8UC1, (unsigned char*)pSrc);
			//cv::Mat U(i, w, CV_8UC1, (unsigned char*)pSrc + w * i);
			//cv::Mat V(i, w, CV_8UC1, (unsigned char*)pSrc + w * i * 2);
			yuvChannels.at(0).data = (unsigned char*)pYuvBuf;//Y
			yuvChannels.at(1).data = (unsigned char*)pYuvBuf + w * h;//U
			yuvChannels.at(2).data = (unsigned char*)pYuvBuf + w * h * 2;//V
			cv::merge(yuvChannels, yuvMat);
			cv::Mat bgrMat;
			cv::cvtColor(yuvMat, bgrMat, CV_YUV2BGR);
			cv::imshow(" cvPlayYUV444", bgrMat);
			cv::waitKey(30);
		}
		delete[] pYuvBuf;
		fclose(pFileIn);
	}


	void cvPlayYUV420(string fileYUV, int w, int h)
	{
		FILE* pFileIn = fopen(fileYUV.c_str(), "rb+");
		int bufLen = w*h * 3 / 2;
		unsigned char* pYuvBuf = new unsigned char[bufLen];
		while (fread(pYuvBuf, bufLen * sizeof(unsigned char), 1, pFileIn))
		{
			cv::Mat yuvImg, rgbImg;
			yuvImg.create(h * 3 / 2, w, CV_8UC1);
			memcpy(yuvImg.data, pYuvBuf, bufLen * sizeof(unsigned char));
			cv::Mat gray = yuvImg(cv::Rect(0, 0, w, h));
			cv::cvtColor(yuvImg, rgbImg, CV_YUV2BGR_I420);
			//
			cv::imshow(" cvPlayYUV420", rgbImg);
			cv::waitKey(30);
		}
		delete[] pYuvBuf;
		fclose(pFileIn);
	}

五、完整的代码

     整理一下,这里提供了YUV.h和YUV.cpp源文件和测试main程序,注意定义了namespace名称空间为cs

     YUV444和YUV420的测试文件,可以这里下载:https://download.csdn.net/download/guyuealian/10697442


YUV.h头文件:

#ifndef YUV_H
#define YUV_H
#include
#include 
using namespace std;
namespace cs {
	enum ColorSpace {
		CS_YUV444,
		CS_YUV420
	};
	enum ConvertColorSpace {
		CS_YUV444TOYUV420,
		CS_YUV420TOYUV444
	};
	/*********************************************************	
	@brief         : 读取yuv图像文件,若输入的是多帧的图像文件,如yuv视频文件,则返回最后一帧的数据
	@param fileYUV : YUV文件
	@param w       : 宽width
	@param h       : 高height
	@param colorType: 颜色空间类型:CS_YUV444,CS_YUV420
	@return        : unsigned char* 返回YUV数据
	********************************************************/
	unsigned char* read_yuvImage(string fileYUV, int w, int h, ColorSpace colorType);

	/*********************************************************
	@brief         : 播放YUV420/YUV444文件
	@param fileYUV : YUV文件
	@param w       : 宽width
	@param h       : 高height
	@return        : void
	********************************************************/
	void cvPlayYUV420(string fileYUV, int w, int h);
	void cvPlayYUV444(string fileYUV, int w, int h);

	/*********************************************************
	@brief         : 显示YUV图片,这里会将YUV转到RGB上进行显示
	@param winname : 窗口显示的名称
	@param inbuf   : YUV数据流,直接将read_yuvImage返回的YUV数据流传入即可显示
	@param w       : 宽width
	@param h       : 高height
	@param colorType: 颜色空间类型:CS_YUV444,CS_YUV420
	@return        : void
	********************************************************/
	void cv_imageshow(string winname, unsigned char* inbuf, int w, int h, ColorSpace colorType);

	/*********************************************************
	@brief         : 保存YUV数据流
	@param fileYUV : 输出YUV文件的路径
	@param inbuf   : 需要保存的YUV数据流
	@param w       : 宽width
	@param h       : 高height
	@param colorType: 颜色空间类型:CS_YUV444,CS_YUV420
	@return        : void
	********************************************************/
	void write_yuvImage(string fileYUV, unsigned char* inbuf, int w, int h, ColorSpace colorType);

	/*********************************************************
	@brief         : 实现YUV420与YUV444数据之间的转换,实质上会分别调用YUV420TOYUV444和YUV444TOYUV420函数
	@param inbuf   : 输入YUV数据流
	@param w       : 宽width
	@param h       : 高height
	@param ConvertColorSpace: 转换颜色空间类型:
	                         CS_YUV444TOYUV420:实现YUV444转YUV420
							 CS_YUV420TOYUV444:实现YUV420转YUV444
	@return        : unsigned char* 返回转换后的YUV数据
	********************************************************/
	unsigned char* convertYUV(unsigned char* inbuf, int w, int h, ConvertColorSpace type);
	void YUV420TOYUV444(unsigned char *inbuf, unsigned char *outbuff, int w, int h);
	void YUV444TOYUV420(unsigned char *inbuf, unsigned char *outbuff, int w, int h);

	/*********************************************************
	@brief         : 将YUV数据流转为OpenCV的Mat类型,方便使用OpenCV显示
	@param inbuf   : 输入YUV数据流
	@param w       : 宽width
	@param h       : 高height
	@param colorType: 颜色空间类型:CS_YUV444,CS_YUV420
	@return        : Mat类型图像数据
	********************************************************/
	cv::Mat YUVBufTOYUVMat(unsigned char* inbuf, int w, int h, ColorSpace colorType);

	/*********************************************************
	@brief         : 将OpenCV的Mat类型图像转YUV数据流
	@param inbuf   : OpenCV的Mat类型图像
	@param colorType: Mat类型图像颜色空间类型:CS_YUV444,CS_YUV420
	@return        : unsigned char* 返回转换后的YUV数据流
	********************************************************/
	unsigned char* YUVMatTOYUVBUF(cv::Mat yuvMat, ColorSpace colorType);
}
#endif

 


YUV.cpp实现文件: 

#include "YUV.h"
#include 

namespace cs {
	//void read_yuvImage(string fileYUV, unsigned char* outYUV, int w, int i, ColorSpace colorType)
	/*读取yuv图像文件,注意:若输入的是多帧的图像文件,如yuv视频文件,则返回最后一帧的数据*/
	unsigned char* read_yuvImage(string fileYUV, int w, int h, ColorSpace colorType) {
		FILE * pFileIn;      //输入CS_YUV444文件
		if (NULL == (pFileIn = fopen(fileYUV.c_str(), "rb"))) {
			printf("File input is can't open!\n");
			fclose(pFileIn);
			return nullptr;
		}
		int bufLen = 0;
		if (colorType == CS_YUV444) {//定义保存CS_YUV444的数组
			bufLen = w * h * 3;
		}
		else if (colorType == CS_YUV420) {//定义保存YUV420的数组
			bufLen = w*h * 3 / 2;
		}
		unsigned char* yuvBuf = new unsigned char[bufLen];
		//fread(yuvBuf, bufLen * sizeof(unsigned char), 1, pFileIn);//返回第一帧
		while (fread(yuvBuf, bufLen * sizeof(unsigned char), 1, pFileIn))//返回最后一帧
		{
		}
		fclose(pFileIn);
		return yuvBuf;
	}
	void cvPlayYUV444(string fileYUV, int w, int h) {
		FILE* pFileIn = fopen(fileYUV.c_str(), "rb+");
		int bufLen = w * h * 3;
		unsigned char* pYuvBuf = new unsigned char[bufLen];
		cv::Mat yuvMat = cv::Mat::zeros(h, w, CV_8UC3);
		std::vector yuvChannels;
		cv::split(yuvMat, yuvChannels);
		while (fread(pYuvBuf, bufLen * sizeof(unsigned char), 1, pFileIn))
		{
			//cv::Mat Y(i, w, CV_8UC1, (unsigned char*)pSrc);
			//cv::Mat U(i, w, CV_8UC1, (unsigned char*)pSrc + w * i);
			//cv::Mat V(i, w, CV_8UC1, (unsigned char*)pSrc + w * i * 2);
			yuvChannels.at(0).data = (unsigned char*)pYuvBuf;//Y
			yuvChannels.at(1).data = (unsigned char*)pYuvBuf + w * h;//U
			yuvChannels.at(2).data = (unsigned char*)pYuvBuf + w * h * 2;//V
			cv::merge(yuvChannels, yuvMat);
			cv::Mat bgrMat;
			cv::cvtColor(yuvMat, bgrMat, CV_YUV2BGR);
			cv::imshow(" cvPlayYUV444", bgrMat);
			cv::waitKey(30);
		}
		delete[] pYuvBuf;
		fclose(pFileIn);
	}


	void cvPlayYUV420(string fileYUV, int w, int h)
	{
		FILE* pFileIn = fopen(fileYUV.c_str(), "rb+");
		int bufLen = w*h * 3 / 2;
		unsigned char* pYuvBuf = new unsigned char[bufLen];
		while (fread(pYuvBuf, bufLen * sizeof(unsigned char), 1, pFileIn))
		{
			cv::Mat yuvImg, rgbImg;
			yuvImg.create(h * 3 / 2, w, CV_8UC1);
			memcpy(yuvImg.data, pYuvBuf, bufLen * sizeof(unsigned char));
			cv::Mat gray = yuvImg(cv::Rect(0, 0, w, h));
			cv::cvtColor(yuvImg, rgbImg, CV_YUV2BGR_I420);
			//
			cv::imshow(" cvPlayYUV420", rgbImg);
			cv::waitKey(30);
		}
		delete[] pYuvBuf;
		fclose(pFileIn);
	}

	void write_yuvImage(string fileYUV, unsigned char* inframe, int w, int h, ColorSpace colorType) {
		FILE * pFileOut;     //输出YUV文件
		if (NULL == (pFileOut = fopen(fileYUV.c_str(), "wb"))) {
			printf("File output is can't open!\n");
			fclose(pFileOut);
			return;
		}
		int bufLen = 0;
		if (colorType == CS_YUV444) {//定义保存CS_YUV444的数组
			bufLen = w * h * 3;
		}
		else if (colorType == CS_YUV420) {//定义保存YUV420的数组
			bufLen = w*h * 3 / 2;
		}
		fwrite(inframe, bufLen * sizeof(unsigned char), 1, pFileOut);
		fclose(pFileOut);
	}

	void cv_imageshow(string winname, unsigned char* inframe, int w, int h, ColorSpace colorType) {
		if (colorType == CS_YUV444) {//定义保存CS_YUV444的数组
			cv::Mat yuvMat = cv::Mat::zeros(h, w, CV_8UC3);
			std::vector yuvChannels;
			cv::split(yuvMat, yuvChannels);
			yuvChannels.at(0).data = (unsigned char*)inframe;//Y
			yuvChannels.at(1).data = (unsigned char*)inframe + w * h;//U
			yuvChannels.at(2).data = (unsigned char*)inframe + w * h * 2;//V
			cv::merge(yuvChannels, yuvMat);
			cv::Mat bgrMat;
			cv::cvtColor(yuvMat, bgrMat, CV_YUV2BGR);
			cv::imshow(winname, bgrMat);
			cv::waitKey(30);
		}
		else if (colorType == CS_YUV420) {//定义保存YUV420的数组
			int bufLen = w*h * 3 / 2;
			cv::Mat yuvImg = cv::Mat::zeros(h * 3 / 2, w, CV_8UC1);
			memcpy(yuvImg.data, inframe, bufLen * sizeof(unsigned char));
			//cv::imshow(winname, yuvImg);
			//cv::waitKey(30);
			cv::Mat bgrImg= cv::Mat::zeros(h , w, CV_8UC3);
			cv::cvtColor(yuvImg, bgrImg, CV_YUV2BGR_I420);
			cv::imshow(winname, bgrImg);
			cv::waitKey(30);
		}
	}

	void YUV420TOYUV444(unsigned char *inbuf, unsigned char *outbuf, int w, int h) {
		unsigned char *srcY = NULL, *srcU = NULL, *srcV = NULL;
		unsigned char *desY = NULL, *desU = NULL, *desV = NULL;
		srcY = inbuf;//Y
		srcU = srcY + w * h;//U
		srcV = srcU + w * h / 4;;//V

		desY = outbuf;
		desU = desY + w * h;
		desV = desU + w * h;
		memcpy(desY, srcY, w * h * sizeof(unsigned char));//Y分量直接拷贝即可
	   //UV分量转换
		int i, j;
		for (i = 0; i < h; i += 2) {//行
			for (j = 0; j < w; j += 2) {//列
										//U
				desU[i*w + j] = srcU[i / 2 * w / 2 + j / 2];
				desU[i*w + j + 1] = srcU[i / 2 * w / 2 + j / 2];
				desU[(i + 1)*w + j] = srcU[i / 2 * w / 2 + j / 2];
				desU[(i + 1)*w + j + 1] = srcU[i / 2 * w / 2 + j / 2];
				//V
				desV[i*w + j] = srcV[i / 2 * w / 2 + j / 2];
				desV[i*w + j + 1] = srcV[i / 2 * w / 2 + j / 2];
				desV[(i + 1)*w + j] = srcV[i / 2 * w / 2 + j / 2];
				desV[(i + 1)*w + j + 1] = srcV[i / 2 * w / 2 + j / 2];
			}
		}
	}
	void YUV444TOYUV420(unsigned char *inbuf, unsigned char *outbuf, int w, int h) {
		unsigned char *srcY = NULL, *srcU = NULL, *srcV = NULL;
		unsigned char *desY = NULL, *desU = NULL, *desV = NULL;
		srcY = inbuf;//Y
		srcU = srcY + w * h;//U
		srcV = srcU + w * h;//V

		desY = outbuf;
		desU = desY + w * h;
		desV = desU + w * h / 4;

		int half_width = w / 2;
		int half_height = h / 2;
		memcpy(desY, srcY, w * h * sizeof(unsigned char));//Y分量直接拷贝即可
		 //UV
		for (int i = 0; i < half_height; i++) {
			for (int j = 0; j < half_width; j++) {
				*desU = *srcU;
				*desV = *srcV;
				desU++;
				desV++;
				srcU += 2;
				srcV += 2;
			}
			srcU = srcU + w;
			srcV = srcV + w;
		}
	}

	unsigned char* convertYUV(unsigned char* inbuf, int w, int h, ConvertColorSpace type) {
		int bufLen = 0;
		if (type == CS_YUV420TOYUV444) {//定义保存CS_YUV444的数组
			bufLen = w * h * 3;
			unsigned char* pDst = new unsigned char[bufLen];//定义保存YUV420的数组
			YUV420TOYUV444(inbuf, pDst, w, h);
			return pDst;
		}
		else if (type == CS_YUV444TOYUV420) {
			bufLen = w*h * 3 / 2;
			unsigned char* pDst = new unsigned char[bufLen];//定义保存YUV420的数组
			YUV444TOYUV420(inbuf, pDst, w, h);
			return pDst;
		}
	}
	cv::Mat YUVBufTOYUVMat(unsigned char* inbuf,int w,int h, ColorSpace colorType) {
		if (colorType == CS_YUV444) {//定义保存CS_YUV444的数组
			cv::Mat yuvMat = cv::Mat::zeros(h, w, CV_8UC3);
			std::vector yuvChannels;
			cv::split(yuvMat, yuvChannels);
			yuvChannels.at(0).data = (unsigned char*)inbuf;//Y
			yuvChannels.at(1).data = (unsigned char*)inbuf + w * h;//U
			yuvChannels.at(2).data = (unsigned char*)inbuf + w * h * 2;//V
			cv::merge(yuvChannels, yuvMat);
			return yuvMat;
		}
		else if (colorType == CS_YUV420) {//定义保存YUV420的数组
			int bufLen = w*h * 3 / 2;
			cv::Mat yuvMat= cv::Mat::zeros(h * 3 / 2, w, CV_8UC1);
			memcpy(yuvMat.data, inbuf, bufLen * sizeof(unsigned char));
			return yuvMat;
		}

	}

	unsigned char* YUVMatTOYUVBUF(cv::Mat yuvMat, ColorSpace colorType) {
		if (colorType == CS_YUV444) {//定义保存CS_YUV444的数组
			int w = yuvMat.cols;
			int h = yuvMat.rows;
			int bufLen = w*h * 3;
			unsigned char* outbuf = new unsigned char[bufLen];
			std::vector yuvChannels;
			cv::split(yuvMat, yuvChannels);
			memcpy(outbuf, yuvChannels.at(0).data, w * h * sizeof(unsigned char));//Y分量直接拷贝即可
			memcpy(outbuf + w * h, yuvChannels.at(1).data, w * h * sizeof(unsigned char));//Y分量直接拷贝即可
			memcpy(outbuf + w * h * 2, yuvChannels.at(2).data, w * h * sizeof(unsigned char));//Y分量直接拷贝即可
			return outbuf;
		}
		else if (colorType == CS_YUV420) {//定义保存YUV420的数组
			int w = yuvMat.cols;
			int h = yuvMat.rows;
			int bufLen = yuvMat.total();//w*h ;
			unsigned char* outbuf = new unsigned char[bufLen];
			//outbuf = yuvMat.data;
			memcpy(outbuf,yuvMat.data, bufLen * sizeof(unsigned char));
			return outbuf;
		}
	}
}

    YUV444与YUV420互转测试:

int main() {
	string fileYUV = "D:\\imageEnhance\\YUVdata\\YUV444_1366_768.yuv";
	//string fileYUV = "D:\\imageEnhance\\YUVdata\\yuv444_image_640_480.yuv";
	//string fileYUV = "D:\\imageEnhance\\YUVdata\\sintel_640_360.yuv";
	int w = 1366;
	int h = 768;
	char unsigned *yuv444_buf= cs::read_yuvImage(fileYUV, w, h, cs::CS_YUV444);//读取YUV数据
	cs::cv_imageshow("CS_YUV444", yuv444_buf, w, h, cs::CS_YUV444);
	char unsigned *yuv420_buf = cs::convertYUV(yuv444_buf, w, h, cs::CS_YUV444TOYUV420);//将YUV444转为YUV420
	cs::cv_imageshow("CS_YUV420", yuv420_buf, w, h, cs::CS_YUV420);
	cin.get();
	return 0;
}

    YUV444与YUV420播放测试:

int main() {
	//string fileYUV = "D:\\imageEnhance\\YUVdata\\YUV444_1366_768.yuv";
	//string fileYUV = "D:\\imageEnhance\\YUVdata\\yuv444_image_640_480.yuv";
	string fileYUV = "D:\\imageEnhance\\YUVdata\\sintel_640_360.yuv";
	int w = 640;
	int h = 360;
	cs::cvPlayYUV420(fileYUV,w,h);
	cin.get();
	return 0;
}

YUV420与YUV444互转,YUV420与YUV444读取和保存,YUV的显示和播放功能_第4张图片


参考资料:

[1]《YUV420转YUV444》https://blog.csdn.net/lin453701006/article/details/53053185,这个博客的思路不错,就是代码太烂了。

[2]《YUV444与YUV422下采样》https://blog.csdn.net/qq_36113899/article/details/68958344

如果你觉得该帖子帮到你,还望贵人多多支持,鄙人会再接再厉,继续努力的~

你可能感兴趣的:(图像处理,OpenCV,C,读写YUV444和YUV420,YUV444与YUV420互转,UV420与YUV444显示,UV420转YUV444)