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


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的测试文件,可以这里


目录

​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的显示和播放功能_UV420转YUV444

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的显示和播放功能_UV420转YUV444_02


从总数据量来看,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分量为例,其序列如下

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

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

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

在代码中,可以直接通过对数组循环赋值即可完成。对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<cv::Mat> 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<cv::Mat> 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);
}

五、完整的代码

注意定义了namespace名称空间为cs

YUV444和YUV420的测试文件​​ ​​


YUV.h头文件:

#ifndef YUV_H
#define YUV_H
#include<opencv2/opencv.hpp>
#include <string>
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 <opencv2/opencv.hpp>

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<cv::Mat> 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<cv::Mat> 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<cv::Mat> 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<cv::Mat> 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的显示和播放功能_UV420转YUV444_05


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

jQuery判断当前页面为某个静态页面 jquery判断元素是否可见

jQuery判断一个元素是显示还是隐藏,jQuery使用is()方法来判断一个元素是否显示,反之则为隐藏核心代码if($("#username").is(":hidden")){ }实例代码:<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">

openresty redis模块 redistemplate.opsforhash.entries

RedisTemplate取值多双引号问题原本的配置类:@Bean public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) { RedisTemplate<String, Object> redisTemplate= new RedisTemplate&lt

javascript 双引号内 变量 js 双引号 单引号

单引号或者双引号没有根本区别,在于怎么用。如果外部用了双引,那么内部截断的时候也要用双引,只要对应正确即可。  双引号比单引号稍稍耗费点系统资源,因为双引号还需要负责某些特殊代码的转换工作,如 \n 及 \r 等。 其它时候,都是一样的。 所以,一般建议不包含转义字符的字符串,就用单引号来处理。