Opencv中BGR、YUV、YUV_I420\NV12分析

文章目录

    • 一、CV_BGR2YUV
    • 二、CV_BGR2YUV_I420
    • 三、I420和NV12的区别以及Opencv中相互转换
    • 四、I420或者NV12转RGB

本地模拟视频解码YUV数据传输
无奈OpenCV中没有自带 BGR转NV12的接口
通过mat.data分析cv::cvtColor 函数中 CV_BGR2YUV、CV_BGR2YUV_I420区别,并尝试将I420转成NV12
本文默认已清楚 YUV中I420、nv12的原理
BGR图像大小是 1080 *1920 * 3

一、CV_BGR2YUV

得到的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

二、CV_BGR2YUV_I420

经过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的编码顺序不一样。请看下图:
Opencv中BGR、YUV、YUV_I420\NV12分析_第1张图片
代码分析,我们利用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

Opencv中BGR、YUV、YUV_I420\NV12分析_第2张图片
Opencv中BGR、YUV、YUV_I420\NV12分析_第3张图片尝试将U通道从I420中提取出来


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和NV12的区别以及Opencv中相互转换

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);
}

四、I420或者NV12转RGB

Opencv有函数提供

cv::cvtColor(SrcYUV, RGBImage, cv::COLOR_YUV2RGB_NV12); 
cv::cvtColor(SrcYUV, RGBImage, cv::COLOR_YUV2RGB_I420)

你可能感兴趣的:(个人笔记,opencv3.4.1深入浅出)