安卓Bitmap影象格式轉為BGRA
阿新 • • 發佈:2019-01-02
最近在做opencv的一個專案,安卓人臉識別。
為了提高效率,完全拋棄javacv(opencv4android)的內容,完全使用jni開發,應用層做的工作只是把攝像頭獲取的影象資料傳到jni中,其餘人臉檢測、識別完全在jni中用opencv的c、c++介面開發完成。
那麼問題來了,安卓攝像頭預覽獲取的yuv420sp資料如果直接傳到jni中,用Mat矩陣來接收,格式是不支援的。使用opencv中的函式Mat image(height, width, CV_8UC4, (unsigned char*)pBuf);這個函式只能接收BGRA格式的影象資料。因此要對攝像頭獲取的資料進行轉化。
public void onPreviewFrame(byte[] data, Camera camera) { <span style="white-space:pre"> </span>if (data != null) { <span style="white-space:pre"> </span>int imageWidth = mCamera.getParameters().getPreviewSize().width; int imageHeight = mCamera.getParameters().getPreviewSize().height; int RGBData[] = new int[imageWidth * imageHeight]; <span style="white-space:pre"> </span>decodeYUV420SP(RGBData, data, imageWidth, imageHeight); <span style="white-space:pre"> </span>// 解碼,yuv420sp轉為RGB格式 <span style="white-space:pre"> </span>Bitmap bm = Bitmap.createBitmap(RGBData, imageWidth,imageHeight, Config.ARGB_8888);<span style="white-space:pre"> </span>//填到bitmap中 <span style="white-space:pre"> </span>byte[] bgra = getPixelsBGRA(bm);<span style="white-space:pre"> </span>//把bitmap中的ARGB_8888格式轉為Mat矩陣中可用的BGRA格式資料bgra <span style="white-space:pre"> </span>} }
public byte[] getPixelsBGRA(Bitmap image) { // calculate how many bytes our image consists of int bytes = image.getByteCount(); ByteBuffer buffer = ByteBuffer.allocate(bytes); // Create a new buffer image.copyPixelsToBuffer(buffer); // Move the byte data to the buffer byte[] temp = buffer.array(); // Get the underlying array containing the data. byte[] pixels = new byte[temp.length]; // Allocate for BGRA // Copy pixels into place for (int i = 0; i < (temp.length / 4); i++) { pixels[i * 4] = temp[i * 4 + 2]; //B pixels[i * 4 + 1] = temp[i * 4 + 1];<span style="white-space:pre"> </span>//G pixels[i * 4 + 2] = temp[i * 4 ]; //R pixels[i * 4 + 3] = temp[i * 4 + 3]; //A } return pixels; } static public void decodeYUV420SP(int[] rgb, byte[] yuv420sp, int width, int height) { final int frameSize = width * height; for (int j = 0, yp = 0; j < height; j++) { int uvp = frameSize + (j >> 1) * width, u = 0, v = 0; for (int i = 0; i < width; i++, yp++) { int y = (0xff & ((int) yuv420sp[yp])) - 16; if (y < 0) y = 0; if ((i & 1) == 0) { v = (0xff & yuv420sp[uvp++]) - 128; u = (0xff & yuv420sp[uvp++]) - 128; } int y1192 = 1192 * y; int r = (y1192 + 1634 * v); int g = (y1192 - 833 * v - 400 * u); int b = (y1192 + 2066 * u); if (r < 0) r = 0; else if (r > 262143) r = 262143; if (g < 0) g = 0; else if (g > 262143) g = 262143; if (b < 0) b = 0; else if (b > 262143) b = 262143; rgb[yp] = 0xff000000 | ((r << 6) & 0xff0000) | ((g >> 2) & 0xff00) | ((b >> 10) & 0xff); } } }
錯誤歷程:
原本以為bitmap中的格式ARGB_8888,中的資料是按照字母順序A、R、G、B來排列的,之前自己寫了函式按這種順序轉換為B、G、R、A的順序格式,傳到Mat矩陣中,發現影象顏色總是顯示不正確。幾經除錯,除錯了好幾天,才發現bitmap中所謂的ARGB_8888,實際的資料順序為R、G、B、A。這尼瑪不按套路出牌,還要靠自己摸索這麼久。真心坑爹啊。
以上的做法還是經歷了yuv420sp->RGB->Bitmap->BGRA的格式變換,貌似饒了一大圈,因為yuv420sp轉成的RGB資料傳到Mat矩陣中,還是不能正常顯示。。但是又沒有找到一種方法可以把yuv420sp直接轉為BGRA格式。。還待優化吧,若有人有辦法直接轉換,望不吝賜教。