OpenCV Mat格式存儲YUV圖像

YUV圖像用的比較多,並且YUV圖像的格式衆多(YUV格式能夠參YUV pixel formats),如何用OpenCV的Mat類型來存儲YUV圖像也是常常遇到的問題。php

對於YUV444圖像來講,就很簡單。YUV的三個份量的採樣方法一致,所以YUV三個份量的大小一致,能夠用Mat的三個channel分別表示YUV便可。假設src是OpenCV默認的BGR三通道圖像,和YUV444的轉換以下,圖像大小不變。ide

// 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份量按照像素排列順序交錯存儲,算做一個面,所以稱爲半面。ui

420pspa

420sp指針

YUV順序code

YVU順序orm

UVUV交錯blog

VUVU交錯ip

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,不一一列舉了。

相關文章
相關標籤/搜索