1. 程式人生 > >Android中攝像頭獲取的YUV資料轉Opencv的Mat

Android中攝像頭獲取的YUV資料轉Opencv的Mat

背景

在onPreviewFrame方法中獲取的byte[] data資料為420sp格式,排列順序為width*height個Y(亮度資訊,就是我們常見的灰度影象),後面是UV(顏色資訊),4個Y共享一個U和V,故byte陣列的總大小是width*height*2/3
420sp通常是如下形式(UV交替屬於NV12)(或者VU交替屬於NV21):

[               [
	Y Y Y Y        Y Y Y Y
	Y Y Y Y  或    Y Y Y Y
	U V U V        V U V U
]               ]

先通過JNI把YUV420sp資料轉換成YUV420p資料。420p就是YYYYYYYYUUVV這樣存放的格式,相當於陣列中的資料交換位置而已,很好實現。(當然,後來發現Opencv可以直接處理sp的資料格式)。

https://www.cnblogs.com/samaritan/p/YUV.html,這個部落格詳細介紹了YUV資料的格式。
###opencv資料型別
Mat的資料型別分為8U,16S,32F,64F等。在這裡我們使用8U型別,YUV資料也是8位無符號型別的。C1,C2,C3,C4為通道數,YUV為單通道,即8UC1。

嘗試一(認為可行,結果失敗,求大神解答)

想著OpencvForAndroid中,初始化Mat中有一個方法為:

Mat mat = new Mat(int rows, int cols, int type, ByteBuffer data);

其中ByteBuffer為Nio包中的類,可以使用靜態方法ByteBuffer.wrap(data)

進行轉換。使用如下程式碼:

	Mat mat = new Mat((int)frameHeight*1.5,frameWidth,CvType.CV_8UC1,ByteBuffer.wrap(data));

執行這一句的時候,一直報CvException錯誤,檢視錯誤中是Mat的原生代碼報的錯,顯示data.total為0。一直不明白怎麼回事,放棄。

嘗試二

OpencvForAndroid中還有一個方法可以初始化矩陣,或者稱給矩陣賦值。

	Mat mat = new Mat(frameHeight*1.5,frameWidth,CvType.CV_8UC1);//初始化一個矩陣,沒資料
	mat.
put(0,0,data);//從(0,0)開始放資料,直到data放完或者矩陣被填滿(若是多通道,則把當前位置的通道全部填滿,才繼續下一個位置,data長度必須整除通道數). Mat bgr_i420 = new Mat(); Imgproc.cvtColor(mat , bgr_i420, Imgproc.COLOR_YUV2BGR_I420);//轉換顏色空間 //對bgr格式的mat做一些常見的處理. Mat result = new Mat(); Imgproc.cvtColor(bgr_i420, result , Imgproc.COLOR_BGR2YUV_I420);//轉換回YUV420p格式 byte[] data_result = new byte[(int)frameHeight*frameWidth*1.5]; result.get(0,0,data_result);//從(0,0)開始取資料,過程同上

通過上面的程式碼,可以把YUV資料轉成BGR格式,進行一系列處理,在轉換成YUV資料,儲存或者推流走。

結語

Imgproc.cvtColor(src, dst , Imgproc.COLOR_XXXXXX);
其中XXX有多種模式可選,可以直接將420SP(NV12/NV21)轉換成BGR,具體的常量,自己開啟工程試試就知道了。
data轉成Mat,基於Mat可以用來做人臉識別、美顏、貼圖、換臉等功能。