opencv 解析yuv_OpenCV Mat格式存储YUV图像
YUV圖像用的比較多,而且YUV圖像的格式眾多(YUV格式可以參考YUV pixel formats),如何用OpenCV的Mat類型來存儲YUV圖像也是經(jīng)常遇到的問題。
對于YUV444圖像來說,就很簡單。YUV的三個分量的采樣方法一致,因此YUV三個分量的大小一致,可以用Mat的三個channel分別表示YUV即可。假設(shè)src是OpenCV默認(rèn)的BGR三通道圖像,和YUV444的轉(zhuǎn)換如下,圖像大小不變。
//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,在水平和垂直方向上的數(shù)據(jù)都只有Y分量的一半。因此總體來說,U/V分量的數(shù)據(jù)量分別只有Y分量的1/4,不能作為Mat類型的一個channel。所以通常YUV420圖像的全部數(shù)據(jù)存儲在Mat的一個channel,比如CV_8UC1,這樣對于Mat來說,圖像的大小就有變化。對于MxN(rows x cols,M行N列)的BGR圖像(CV_8UC3),其對應(yīng)的YUV420圖像大小是(3M/2)xN(CV_8UC1)。前MxN個數(shù)據(jù)是Y分量,后(M/2)xN個數(shù)據(jù)是U/V分量,UV數(shù)據(jù)各占一半。
U/V分量如何存儲,和YUV420的格式有關(guān)。YUV420有所謂的420p(420planar/420面)和420sp(420 semi-planar/420半面)格式。所謂420面格式,YUV三個分量按順序存儲完一個分量所有圖像數(shù)據(jù),稱為一個面,再存儲下一個分量的面,因此有三個面數(shù)據(jù)。420半面格式下,只有Y分量是作為一個單獨的面存儲,U/V分量按照像素排列順序交錯存儲,算作一個面,因此稱為半面。
420p
420sp
YUV順序
YVU順序
UVUV交錯
VUVU交錯
I420/IYUV
YV12
NV12
NV21
420p或者420sp都是先存儲Y分量的面,然后根據(jù)UV分量的存儲順序,又各分為兩種格式。420p按照YUV的順序存儲三個面,是I420格式,或者叫IYUV格式。按照YVU的順序存儲三個面,叫YV12格式。420sp的U/V交錯面,如果按照UVUV的順序交錯存儲,稱為NV12格式。反之,按照VUVU的順序交錯存儲,稱為NV21格式。
OpenCV現(xiàn)在從BGR到Y(jié)UV420的顏色空間變化僅支持轉(zhuǎn)換到420p的兩種格式,不支持轉(zhuǎn)換到420sp。但可以支持420p或者420sp轉(zhuǎn)換到BGR。假設(shè)src是OpenCV默認(rèn)的BGR三通道圖像,和420p的轉(zhuǎn)換如下。
//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);
假設(shè)src是YUV420的420sp圖像數(shù)據(jù),到BGR的轉(zhuǎn)換如下。
//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函數(shù),當(dāng)前僅支持從420sp轉(zhuǎn)換到BGR,但是Y面和U/V交錯面存儲在兩個Mat結(jié)構(gòu)中。
下面的代碼片段把height x width的YUV圖像數(shù)據(jù)順時針旋轉(zhuǎn)90°存儲到Mat,格式是NV12。yPixel, uPixel, vPixel分別是指向YUV數(shù)據(jù)的指針,yStride,uvStride分別是Y和UV的行stride,uvPixelStride是UV數(shù)據(jù)像素stride。代碼分別把YUV數(shù)據(jù)存儲到一個臨時Mat中,然后調(diào)用OpenCV的transpose()和flip()函數(shù)把圖像順時針旋轉(zhuǎn)90°。較新版本的OpenCV提供了函數(shù)rotate()可以做90°,180°和270°的旋轉(zhuǎn),可以使用。最后分別把旋轉(zhuǎn)后的YUV數(shù)據(jù)寫到Mat中,最后的格式是NV12,注意height和width交換了,UV數(shù)據(jù)是交錯存儲的。如果不使用OpenCV的函數(shù),自己寫一段代碼來做旋轉(zhuǎn)也是可以的。不過我試過了,肯定沒有OpenCV的函數(shù)快。OpenCV的函數(shù)至少要比我們用循環(huán)寫出來的代碼快25%。所以有現(xiàn)成的庫函數(shù)盡量使用他們。
//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)inti, 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_ 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 = 0; i < wh2; i++) {*it++ = *u_src_it++;*it++ = *v_src_it++;
}
至于YUV422圖像,我沒有試過。OpenCV不支持從BGR轉(zhuǎn)到Y(jié)UV422,但是可以從YUV422轉(zhuǎn)會BGR。大概看了下,YUV422圖像用Mat類型存儲應(yīng)該也是用一個channel來存儲所有YUV數(shù)據(jù),而且應(yīng)該是用所謂的緊湊格式(packed format),而不是前面提到的面格式(planar format)。所謂緊湊格式,就是對每個像素的YUV三個分量按照一定的順序交錯存儲,每4個數(shù)據(jù)組成一個所謂的宏像素。因為YUV422垂直方向沒有downsample,只有水平方向有,所以每兩個Y對應(yīng)一個U和一個V,組成一個宏像素。比如UYVY格式(按照UYVY交錯存儲),YUY2格式(按照YUYV交錯存儲),YVYU格式等等。它們都有對應(yīng)的轉(zhuǎn)BGR的code,比如COLOR_YUV2BGR_UYVY,不一一列舉了。
總結(jié)
以上是生活随笔為你收集整理的opencv 解析yuv_OpenCV Mat格式存储YUV图像的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 腾安基金销售(深圳)有限公司扣款怎么追回
- 下一篇: idea启动webservice_Int