【尊重原创,转载请注明出处】: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有三个分量:Y(Luminance/Luma:亮度)、U和V表示色差,体现的是图片的色彩信息。相对于RGB彩色空间,将亮度信息和色彩信息分离。这种编码模式也更加适应于人眼,据研究表明,人眼对亮度信息比色彩信息更加敏感。而YUV下采样就是根据人眼的特点,将人眼相对不敏感的色彩信息进行压缩采样,得到相对小的文件进行播放和传输。与YUV相像YCbCr其实与其有少许不同,体现在参数的大小上,本质上都是将亮度信息与色彩信息相分开。
YUV444:对于每一个像素都对应一个Y分量、一个U分量、一个V分量。
YUV422:对于一个像素都对应一个Y分量,但是每两个像素(或者说Y分量)对应一个U分量和一个V分量。
YUV420:对于一个像素都对应一个Y分量,但是每四个像素(或者说Y分量)对应一个U分量和一个V分量。
YUYUV在存储时是以数组的形式存储的,可以看做连续的三个一维数组。三个数组分别单独存储Y、U、V分量。以一幅100×100的YUV444图像为例,下图表示的YUV444的存储格式和YUV420的存储格式。
从总数据量来看,YUV444需要存储100×100×3个值,YUV422需要存储100×100×2个值,YUV420需要存储100×100×3/2个值。
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的视频文件。
保存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);
}
OpenCV提供了RGB与YUV420/YUV444互转的接口:cvtColor(),但根尴尬OpenCV就是没有提供YUV444与YUV420互转的接口,如果你想用OpenCV转换,那就只能间接的方法了,将YUV420转为BGR(CV_YUV2BGR_I420),然后再将BGR转为YUV444(CV_BGR2YUV),这很蛋疼!效率太低了。
实质上,只要我们理解了YUV420与YUV444的数据格式,完全可以自己转换的,下面使用C++实现YUV420与YUV444互转,这部分不依赖OpenCV,因此效率十分高的!!!
YUV420转YUV444,实际就是对UV两个分量色度进行上采样,最为简单的实现思路是直接填充。
以U分量为例,其序列如下
对U进行插值,每个田字格四个位置使用一个U值。
在代码中,可以直接通过对数组循环赋值即可完成。对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];
}
}
}
与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数据需要转到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);
}
}
上面提供的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;
}
[1]《YUV420转YUV444》https://blog.csdn.net/lin453701006/article/details/53053185,这个博客的思路不错,就是代码太烂了。
[2]《YUV444与YUV422下采样》https://blog.csdn.net/qq_36113899/article/details/68958344
如果你觉得该帖子帮到你,还望贵人多多支持,鄙人会再接再厉,继续努力的~