得到的YUV是三个通道,分别对应Y通道、U通道和V通道,且宽高大小一样
int main()
{
cv::Mat inImage = imread("./QJPQ1_1_0.jpg");
std::cout << " bgr H: "<< inImage.rows << std::endl;
std::cout << " bgr W: "<< inImage.cols << std::endl;
std::cout << " bgr C: "<< inImage.channels() << std::endl;
cv::Mat imageYUV;
cv::cvtColor(inImage,imageYUV,CV_BGR2YUV);
std::cout << "yuv H: "<< imageYUV.rows << std::endl;
std::cout << " yuv W: "<< imageYUV.cols << std::endl;
std::cout << " yuv C: "<< imageYUV.channels() << std::endl;
cv::Mat imageY(inImage.rows,inImage.cols,1);
cv::Mat imageU(inImage.rows,inImage.cols,1);
cv::Mat imageV(inImage.rows,inImage.cols,1);
cv::cvtColor(inImage,imageYUV,CV_BGR2YUV);
std::vector<Mat> mv;
split(inImage, (vector<Mat>&)mv);
imageY = mv[0].clone();
imageU = mv[1].clone();
imageV = mv[2].clone();
std::cout << "hello world" << std::endl;
return 0;
}
输出
bgr H: 1080
bgr W: 1920
bgr C: 3
yuv H: 1080
yuv W: 1920
yuv C: 3
经过OpenCV的转换函数,发现经过cvtColor转换后的mat的通道数只有1,且高的大小是BGR的mat的1.5倍。
以下原理摘自:RGB、YUV420、NV21、I420编码区别
I420也是YUV420编码格式的一种,由于android手机厂商的原因,摄像头采集到的数据永远都是经过NV21编码的数据,但是对于这种数据不能够显示在苹果或windows平台,那么需要对这个编码格式的数据需要重新编码,其中I420这种编码格式,所有的厂商都是适配的。
I420编码格式:比如一张19201280的图片,经过I420编码后,会变成前面19201280字节全是Y,从19201280字节长度开始,会先排列U,总字节长度为19201280/4,从19201280+19201280/4开始排列V,字节长度为1920*1280/4,所以总的字节长度适合NV21一样的,只是UV的编码顺序不一样。请看下图:
代码分析,我们利用cv::cvtColor(inImage,imageYUV,CV_BGR2YUV_I420)函数,将BGR转成YUV_I420,并做用imwrite函数保存出来,对比保存出来的图和原图,来进一步理解YUV_I420的数据内存排列
cv::Mat inImage = imread("./QJPQ1_1_0.jpg");
std::cout << " bgr H: "<< inImage.rows << std::endl;
std::cout << " bgr W: "<< inImage.cols << std::endl;
std::cout << " bgr C: "<< inImage.channels() << std::endl;
cv::Mat imageYUV;
cv::cvtColor(inImage,imageYUV,CV_BGR2YUV_I420);
//
std::cout << "yuv H: "<< imageYUV.rows << std::endl;
std::cout << " yuv W: "<< imageYUV.cols << std::endl;
std::cout << " yuv C: "<< imageYUV.channels() << std::endl;
cv::imwrite("./yuv.jpg", imageYUV);
输出:
bgr H: 1080
bgr W: 1920
bgr C: 3
yuv H: 1620
yuv W: 1920
yuv C: 1
int main()
{
cv::Mat inImage = imread("./demo.jpg");
cv::Mat imageYUV;
cv::cvtColor(inImage,imageYUV,CV_BGR2YUV_I420);
cv::imwrite("./yuv.jpg", imageYUV);
int s32BufLen = inImage.rows * inImage.cols ;
cv::Mat imageY;
imageY.create( inImage.rows/4 , inImage.cols, CV_8UC1);
memcpy(imageY.data, imageYUV.data + s32BufLen, s32BufLen/4 );
cv::imwrite("./u.jpg", imageY);
//
std::cout << "===========" << std::endl;
return 0;
}
I420的排列为 前面 hw字节都是Y,再排序U,总字节长度是 WH/4,再排序V,总字节长度是 W*H/4
NV12则是先排序Y,然后uv交替。Opencv没有提供 bgr转NV12的函数,这里根据原理自己实现一遍
void swapYUV_I420toNV12(unsigned char* i420bytes, unsigned char* nv12bytes, int width, int height)
{
int nLenY = width * height;
int nLenU = nLenY / 4;
memcpy(nv12bytes, i420bytes, width * height);
for (int i = 0; i < nLenU; i++)
{
nv12bytes[nLenY + 2 * i] = i420bytes[nLenY + i]; // U
nv12bytes[nLenY + 2 * i + 1] = i420bytes[nLenY + nLenU + i]; // V
}
}
void BGR2YUV_nv12(cv::Mat src, cv::Mat &dst)
{
int w_img = src.cols;
int h_img = src.rows;
dst = cv::Mat(h_img*1.5, w_img, CV_8UC1, Scalar(0));
cv::Mat src_YUV_I420(h_img*1.5, w_img, CV_8UC1, Scalar(0)); //YUV_I420
cvtColor(src, src_YUV_I420, CV_BGR2YUV_I420);
swapYUV_I420toNV12(src_YUV_I420.data, dst.data, w_img, h_img);
}
Opencv有函数提供
cv::cvtColor(SrcYUV, RGBImage, cv::COLOR_YUV2RGB_NV12);
cv::cvtColor(SrcYUV, RGBImage, cv::COLOR_YUV2RGB_I420)