OpenCV Mat格式存储YUV图像

您所在的位置:网站首页 opencv读取yuv图像 OpenCV Mat格式存储YUV图像

OpenCV Mat格式存储YUV图像

2023-08-21 10:54| 来源: 网络整理| 查看: 265

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_ it((cv::Mat_*)&yuv_nv12, width); cv::MatIterator_ u_src_it = u_temp.begin(); cv::MatIterator_ v_src_it = v_temp.begin(); 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图像的更多相关文章 使用VS2010调用matlab的mat格式文件

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

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

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

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

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

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

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



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3