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. HTML入门第三天

    三. 表格table(表格类数据): 1.表格标签(重中之重) 基本结构标签: <table>:定义表格区域 <tr>:定义表格的行 <td>:定义表格中的单元格 ...

  2. Oracel递归查询start with ...connect by prior在Mysql中的实现

    Oracle是一个强大的数据库,有很多的函数和语法可以带来很多便利,有些函数和语法在Mysql中有代替的,但是有些没有现成可用的方法,比如Oracle的递归,在Mysql中怎么实现呢? 例子: Ora ...

  3. d3.js d3.transform 方法移除的解决方案

    rt d3.transform在新版本中移除 需要自行写出该功能 function getTranslation(transform) { // Create a dummy g for calcul ...

  4. 破损的键盘 (Broken Keyboard)--又名悲剧文本(线性表)

     题目: 你有一个破损的键盘.键盘上的所有键都可以正常工作,但有时Home键或者End键会自 动按下.你并不知道键盘存在这一问题,而是专心地打稿子,甚至连显示器都没打开.当你 打开显示器之后, 展现在 ...

  5. 使用CP进行应用层程序控制

    测试版本:R80.20 Step1:开启软刀片的URL过滤和APP控制,如下图: Step2:编辑访问策略,在层编辑器中勾选刀片的“应用程序和URL过滤”,“内容识别”,如下图: Step3:新建一条 ...

  6. CentOS7+CDH5.14.0安装全流程记录,图文详解全程实测-6CM安装前环境检查

    检查环境 在正式开始安装CDH之前最好先检查一下能不能相互免密ssh,以及防火墙是否关闭,集群中的时间是否统一,java版本是否是oracle的版本,主节点mysql是否安装正确等. ssh测试 例如 ...

  7. XML Linq 学习笔记

    XML如下: <?xml version="1.0" encoding="utf-8"?> <Dishes> <Dish> ...

  8. Spring BeanDefinitionRegistryPostProcessor BeanPostProcessor作用

    写博客,写博客,把自己知道的小知识点全部记录,

  9. 4-16 css

    1.背景是图片 <style> body {background-image:url('bgdesert.jpg');} </style> 2. 十六进制 - 如:" ...

  10. Oracle 数据库中对记录进行分页处理

    学习到 oracle 的视图的时候,了解到对 Oracle 中数据的记录进行分页处理和 Mysql 提供的 limit 来进行分页处理大有不同,limit 是 mysql 中特有的关键字. 那么在 o ...