OpenCV Mat格式存儲YUV圖像


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 * 3 / 2, height, CV_8UC1) int i, j; int height2 = height / 2, width2 = width / 2; 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 = 0; 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, 1); // Get U data and rotate
    line_src = uPixel; for (i = 0; i < height2; i++) { line_dest = u_temp.ptr(i); uchar *ptr = line_src; for (j = 0; j < width2; j++) { *line_dest++ = *ptr; ptr += uvPixelStride; } line_src += uvStride; } cv::transpose(u_temp, u_temp); cv::flip(u_temp, u_temp, 1); // Get V data and rotate
    line_src = vPixel; for (i = 0; i < height2; i++) { line_dest = v_temp.ptr(i); uchar *ptr = line_src; for (j = 0; j < width2; j++) { *line_dest++ = *ptr; ptr += uvPixelStride; } line_src += uvStride; } cv::transpose(v_temp, v_temp); cv::flip(v_temp, v_temp, 1); // Write Y data to yuv_nv12
    for (i = 0; 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 = 0; 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,不一一列舉了。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM