YUV图像用的比较多,而且YUV图像的格式众多(YUV格式可以参考YUV pixel formats),如何用OpenCV的Mat类型来存储YUV图像也是经常遇到的问题。

对于YUV444图像来说,就很简单。YUV的三个分量的采样方法一致,因此YUV三个分量的大小一致,可以用Mat的三个channel分别表示YUV即可。假设src是OpenCV默认的BGR三通道图像,和YUV444的转换如下,图像大小不变。

// If src is CV_8UC3, dest is CV_8UC3
cvtColor(src, dest, COLOR_BGR2YUV);
cvtColor(dest, src, COLOR_YUV2BGR);

YUV422用的不多(其实我没用过),先说YUV420。YUV420图像的U/V分量在水平和垂直方向上downsample,在水平和垂直方向上的数据都只有Y分量的一半。因此总体来说,U/V分量的数据量分别只有Y分量的1/4,不能作为Mat类型的一个channel。所以通常YUV420图像的全部数据存储在Mat的一个channel,比如CV_8UC1,这样对于Mat来说,图像的大小就有变化。对于MxN(rows x cols,M行N列)的BGR图像(CV_8UC3),其对应的YUV420图像大小是(3M/2)xN(CV_8UC1)。前MxN个数据是Y分量,后(M/2)xN个数据是U/V分量,UV数据各占一半。

U/V分量如何存储,和YUV420的格式有关。YUV420有所谓的420p(420planar/420面)和420sp(420 semi-planar/420半面)格式。所谓420面格式,YUV三个分量按顺序存储完一个分量所有图像数据,称为一个面,再存储下一个分量的面,因此有三个面数据。420半面格式下,只有Y分量是作为一个单独的面存储,U/V分量按照像素排列顺序交错存储,算作一个面,因此称为半面。

420p

420sp

YUV顺序

YVU顺序

UVUV交错

VUVU交错

I420/IYUV

YV12

NV12

NV21

420p或者420sp都是先存储Y分量的面,然后根据UV分量的存储顺序,又各分为两种格式。420p按照YUV的顺序存储三个面,是I420格式,或者叫IYUV格式。按照YVU的顺序存储三个面,叫YV12格式。420sp的U/V交错面,如果按照UVUV的顺序交错存储,称为NV12格式。反之,按照VUVU的顺序交错存储,称为NV21格式。

OpenCV现在从BGR到YUV420的颜色空间变化仅支持转换到420p的两种格式,不支持转换到420sp。但可以支持420p或者420sp转换到BGR。假设src是OpenCV默认的BGR三通道图像,和420p的转换如下。

// If src is BGR CV_8UC3 with size 640x960, dest is CV_8UC1 with 960x960
cvtColor(src, dest, COLOR_BGR2YUV_I420);    // dest is I420
cvtColor(dest, src, COLOR_YUV2BGR_I420); cvtColor(src, dest, COLOR_BGR2YUV_YV12);    // dest is YV12
cvtColor(dest, src, COLOR_YUV2BGR_YV12);

假设src是YUV420的420sp图像数据,到BGR的转换如下。

// If src is NV12 CV_8UC1 with size 960x960, dest is BGR CV_8UC3 with 640x960
cvtColor(src, dest, COLOR_YUV2BGR_NV12); // If src is NV21 CV_8UC1 with size 960x960, dest is BGR CV8UC3 with 640x960
cvtColor(src, dest, COLOR_YUV2BGR_NV21);

OpenCV还提供了一个cvtColorTwoPlane函数,当前仅支持从420sp转换到BGR,但是Y面和U/V交错面存储在两个Mat结构中。

下面的代码片段把height x width的YUV图像数据顺时针旋转90°存储到Mat,格式是NV12。yPixel, uPixel, vPixel分别是指向YUV数据的指针,yStride,uvStride分别是Y和UV的行stride,uvPixelStride是UV数据像素stride。代码分别把YUV数据存储到一个临时Mat中,然后调用OpenCV的transpose()和flip()函数把图像顺时针旋转90°。较新版本的OpenCV提供了函数rotate()可以做90°,180°和270°的旋转,可以使用。最后分别把旋转后的YUV数据写到Mat中,最后的格式是NV12,注意height和width交换了,UV数据是交错存储的。如果不使用OpenCV的函数,自己写一段代码来做旋转也是可以的。不过我试过了,肯定没有OpenCV的函数快。OpenCV的函数至少要比我们用循环写出来的代码快25%。所以有现成的库函数尽量使用他们。

    // Original image with size height x width
// int32_t width, height; original image width and height
// uint8_t *yPixel, *uPixel, *vPixel; pointers to YUV data
// int32_t yStride, uvStride, uvPixelStride; line stride and uv pixel stride cv::Mat yuv_nv12(width * / , height, CV_8UC1)
int i, j;
int height2 = height / , width2 = width / ; cv::Mat y_temp(height, width, CV_8UC1);
cv::Mat u_temp(height2, width2, CV_8UC1);
cv::Mat v_temp(height2, width2, CV_8UC1);
// Get Y data and rotate
line_src = yPixel;
for (i = ; i < height; i++) {
line_dest = y_temp.ptr(i);
memcpy(line_dest, line_src, width);
line_src += yStride;
}
cv::transpose(y_temp, y_temp);
cv::flip(y_temp, y_temp, );
// Get U data and rotate
line_src = uPixel;
for (i = ; i < height2; i++) {
line_dest = u_temp.ptr(i);
uchar *ptr = line_src;
for (j = ; j < width2; j++) {
*line_dest++ = *ptr;
ptr += uvPixelStride;
}
line_src += uvStride;
}
cv::transpose(u_temp, u_temp);
cv::flip(u_temp, u_temp, );
// Get V data and rotate
line_src = vPixel;
for (i = ; i < height2; i++) {
line_dest = v_temp.ptr(i);
uchar *ptr = line_src;
for (j = ; j < width2; j++) {
*line_dest++ = *ptr;
ptr += uvPixelStride;
}
line_src += uvStride;
}
cv::transpose(v_temp, v_temp);
cv::flip(v_temp, v_temp, );
// Write Y data to yuv_nv12
for (i = ; i < width; i++) {
line_dest = yuv_nv12.ptr(i);
line_src = y_temp.ptr(i);
memcpy(line_dest, line_src, height);
}
// Write UV data to yuv_nv12
cv::MatIterator_<uchar> it((cv::Mat_<uchar>*)&yuv_nv12, width);
cv::MatIterator_<uchar> u_src_it = u_temp.begin<uchar>();
cv::MatIterator_<uchar> v_src_it = v_temp.begin<uchar>();
int wh2 = width2 * height2;
for (i = ; i < wh2; i++) {
*it++ = *u_src_it++;
*it++ = *v_src_it++;
}

至于YUV422图像,我没有试过。OpenCV不支持从BGR转到YUV422,但是可以从YUV422转会BGR。大概看了下,YUV422图像用Mat类型存储应该也是用一个channel来存储所有YUV数据,而且应该是用所谓的紧凑格式(packed format),而不是前面提到的面格式(planar format)。所谓紧凑格式,就是对每个像素的YUV三个分量按照一定的顺序交错存储,每4个数据组成一个所谓的宏像素。因为YUV422垂直方向没有downsample,只有水平方向有,所以每两个Y对应一个U和一个V,组成一个宏像素。比如UYVY格式(按照UYVY交错存储),YUY2格式(按照YUYV交错存储),YVYU格式等等。它们都有对应的转BGR的code,比如COLOR_YUV2BGR_UYVY,不一一列举了。

OpenCV Mat格式存储YUV图像的更多相关文章

  1. 使用VS2010调用matlab的mat格式文件

    做实验需要将matlab实现的meanshift的结果中的region的Iabels矩阵,需要把labels.mat读入VS2010中,实现功能,在此把实现过程记录下来. C++读取mat文件的步骤如 ...

  2. python,opencv,imread,imwrite,存储,读取图像像素不一致,这种情况是label使用jpg格式

    最近在做图像分割,需要使用一些分割图片的label,但是发现存储的分割label感觉被平滑过了,即使使用 image = cv2.imread(info['path'],cv2.IMREAD_UNCH ...

  3. opencv中Mat格式的数据访问.at

    opencv3中图形存储基本为Mat格式,如果我们想获取像素点的灰度值或者RGB值,可以通过image.at<uchar>(i,j)的方式轻松获取. Mat类中的at方法对于获取图像矩阵某 ...

  4. C++ Opencv Mat类型使用的几个注意事项及自写函数实现Laplace图像锐化

    为了提升自己对Opencv中Mat数据类型的熟悉和掌握程度,自己尝试着写了一下Laplace图像锐化函数,一路坎坷,踩坑不断.现将代码分享如下: #include <opencv2/opencv ...

  5. 从 AVFrame 中取出帧(YUV)保存为 Mat 格式

    由于 cnblogs 不支持科学公式,完整内容请移步原文链接 原文地址:从 AVFrame 中取出帧(YUV)保存为 Mat 格式 从 AVFrame 中取出帧(YUV)保存为 Mat 格式 本文档针 ...

  6. 快速遍历OpenCV Mat图像数据的多种方法和性能分析 | opencv mat for loop

    本文首发于个人博客https://kezunlin.me/post/61d55ab4/,欢迎阅读! opencv mat for loop Series Part 1: compile opencv ...

  7. OpenCv Mat操作总结

    Author:: Maddock Date: 2015-03-23 16:33:49 转载请注明出处:http://blog.csdn.net/adong76/article/details/4053 ...

  8. OpenCV Mat数据类型及位数总结(转载)

    OpenCV Mat数据类型及位数总结(转载) 前言 opencv中很多数据结构为了达到內存使用的最优化,通常都会用它最小上限的空间来分配变量,有的数据结构也会因为图像文件格式的关系而给予适当的变量, ...

  9. [OpenCV]Mat类详解

    http://blog.csdn.net/yang_xian521/article/details/7107786 Preface Mat:Matrix Mat类可以被看做是opencv中C++版本的 ...

随机推荐

  1. android sdk更新后出现please update ADT to the latest version的解决方法

    参考:https://www.cnblogs.com/wicub/p/3613353.html 把 1.修改android-sdk-windows/tools/lib下的plugin.pro文件,用记 ...

  2. Servlet第二篇(介绍、ServletConfig;ServletContext)

    什么是Serlvet? Servlet其实就是一个遵循Servlet开发的java类.Serlvet是由服务器调用的,运行在服务器端. 为什么要用到Serlvet? 我们编写java程序想要在网上实现 ...

  3. Huawei BGP和OSPF双边界重分布(一)

    网络拓扑: PS:本例使用明细前缀列表双边界引入 S5700-LSW1 ================================================================ ...

  4. checkpoint NGFW VM安装

    step1:在VMworkstation中创建虚拟机向导,选择Linux 2.6内核 64位如下图: 虚拟机的配置建议如下: RAM:至少8GB Disk:120G CPU:四核 step2:使用IO ...

  5. 100-days: twenty-one

    Title: Not so fantastic(<口>极好的,棒的): can Japan end its love affair(喜爱,热爱) with plastic(塑料)? A : ...

  6. [BZOJ2252]矩阵距离(BFS)

    题意 输入矩阵m行n列(m<=500,n<=500),只含0.1,输出离每个元素距离最近的1的距离,其中距离定义为D(aij,akl)=abs(i-k)+abs(j-l). 示例: 输入: ...

  7. Wordpress 后台更改网址

    在 `wp_options` 数据库执行下面两条命令 ```sql update wp_options set option_value = 'your_new_url' where option_n ...

  8. linux上遇到tomcat报Out of Memory错误,导致jenkins崩溃的问题

    今天遇到一个问题,就是JENKINS在同时部署两个前端应用时会出现崩溃的现象. 排查过程如下 查看tomcat-jenkins/bin/hs_err_pid27127.log发现: Out of Me ...

  9. Git那些事儿

    Git是目前世界上最先进的分布式版本控制系统,适合多人协作开发的大型项目.我平常也经常使用git,来管理自己的几个小项目.简单说说git的原理和git的特点!(只有知道了一个工具的运行原理,设计思路, ...

  10. netcore发布到 iis 设置 部署 环境 变量

    原文链接:https://www.cnblogs.com/Ambre/p/9646445.html 打开IIS,选择本机,找到管理-配置编辑器 1:打开IIS,选择本机,找到管理-配置编辑器 2.节选 ...