標籤:opencv c++ android yuv jni
Windows的映像格式和Android行動裝置上的映像格式存在差異,使得處理存在一些問題!簡單來講
Camera得到的資料是:YUV,而在移動端裝置上顯示的資料又是:RGBA, 但是C++程式中處理的資料又是RGB。因此需要做資料的轉換。具體的操作如下:
0. 使用前的準備。
Camera的使用需要先在AndroidManifest.xml 檔案當中加入camera的許可權:
<uses-permission android:name="android.permission.CAMERA" />
<uses-feature android:name="android.hardware.camera" />
<uses-feature android:name="android.hardware.camera.autofocus" />
一些小Tips:
Android 2.3之後,可以使用Camera.open(int)來擷取特定的相機。
API Level 9之後,可以使用 Camera.getCameraInfo()來查看相機是在裝置前面還是後面,還可以得到映像的方向。
相機是裝置資源,被所有應用共用,當應用不使用相機時應當及時釋放,應當在Activity.onPause()中釋放。
如果不及時釋放,後續的相機請求(包括你自己的應用和其他的應用發出的)都將失敗並且導致應用退出。
1. 資料的獲得:
需要先開啟網路攝影機:
Camera mCamera = Camera.open();Camera.Parameters p = mCamera.getParameters();p.setPreviewFormat(ImageFormat.NV21);/*這是唯一值,也可以不設定。*/
mCamera.setParameters(p);mCamera.startPreview();
Camera提供了這個介面,用法如下:( take care of the function format here):
mCamera.setPreviewCallback(new PreviewCallback(){ @Override public void onPreviewFrame(byte[] data, Camera camera) { //你的操作 } });
在這個回調裡我們就能夠擷取到當前幀的資料,我們可以對其進行預先處理,比如壓縮、加密、特效處理等,不過byte[]這個buffer裡面的資料是YUV格式的,一般是YUV420SP,而Android提供的SurfaceView、GLSurfaceView、TextureView等控制項只支援RGB格式的渲染,因此我們需要一個演算法來解碼。也就是做資料的轉化。
2.資料的轉換(+JNI 的方法 總共有三種):
第一種方式:
@Override public void onPreviewFrame(byte[] data, Camera camera) { Size size = camera.getParameters().getPreviewSize(); try{ YuvImage image = new YuvImage(data, ImageFormat.NV21, size.width, size.height, null); if(image!=null){ ByteArrayOutputStream stream = new ByteArrayOutputStream(); image.compressToJpeg(new Rect(0, 0, size.width, size.height), 80, stream); Bitmap bmp = BitmapFactory.decodeByteArray(stream.toByteArray(), 0, stream.size()); stream.close(); } }catch(Exception ex){ Log.e("Sys","Error:"+ex.getMessage()); } }
其實在獲得資料流以後沒必要壓縮,會降低速度:
以320×240大小的視頻傳輸為例
| 方案 |
壓縮率 |
壓縮/傳輸方式 |
即時性 |
平均流量消耗 |
傳輸距離 |
| 用camera的回呼函數發送原始的yuv420資料 |
0 |
無壓縮,按幀傳輸 |
高(20~30 fps) |
很高(6.5 Mbps)太恐怖了O_O |
近距離有線或無線 |
| 用MediaRecorder對yuv420進行H264寫入程式碼後發送 |
高(95%) |
幀間壓縮,視頻流傳輸 |
高(20 fps) |
低(30~70 Kbps) |
可以遠距離 |
| 調用本地H264編碼庫(JNI)對一幀YUV420資料編碼後發送 |
高(97%) |
幀間壓縮,按幀傳輸 |
低(2 fps) |
低(20 Kbps) |
可以遠距離 |
| 對一幀資料用GZIP庫壓縮後發送(很奇葩的做法) |
較高(70%~80%) |
幀內壓縮,按幀傳輸 |
低(5 fps) |
較高(300 Kbps) |
可以遠距離 |
| 對一幀資料用JPEG方式壓縮後傳輸 |
一般(60%左右) |
幀內壓縮,按幀傳輸 |
高(25 fps) |
高(170 Kbps) |
可以遠距離(頻寬允許的話) |
<span style="background-color: rgb(255, 255, 255);">BitmapFactory.decodeByteArray</span>
據說也很慢!
其實可以用它獲得資料:
byte[] tmp = stream.toByteArray();
再用其他的方法處理。
第二種:
public Bitmap rawByteArray2RGBABitmap2(byte[] data, int width, int height) { int frameSize = width * height; int[] rgba = new int[frameSize]; for (int i = 0; i < height; i++) for (int j = 0; j < width; j++) { int y = (0xff & ((int) data[i * width + j])); int u = (0xff & ((int) data[frameSize + (i >> 1) * width + (j & ~1) + 0])); int v = (0xff & ((int) data[frameSize + (i >> 1) * width + (j & ~1) + 1])); y = y < 16 ? 16 : y; int r = Math.round(1.164f * (y - 16) + 1.596f * (v - 128)); int g = Math.round(1.164f * (y - 16) - 0.813f * (v - 128) - 0.391f * (u - 128)); int b = Math.round(1.164f * (y - 16) + 2.018f * (u - 128)); r = r < 0 ? 0 : (r > 255 ? 255 : r); g = g < 0 ? 0 : (g > 255 ? 255 : g); b = b < 0 ? 0 : (b > 255 ? 255 : b); rgba[i * width + j] = 0xff000000 + (b << 16) + (g << 8) + r; } Bitmap bmp = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888); bmp.setPixels(rgba, 0 , width, 0, 0, width, height); return bmp; }
第三種:
JNIEXPORT void JNICALL Java_com_dvt_pedDetec_pedDetec_yuv2rgb( JNIEnv* env, jobject, jint width, jint height, jbyteArray yuv,jintArray bgr)//JNIEXPORT void JNICALL Java_<package>_<class>_<function>( JNIEnv* env, jobject, <Args>){ jbyte* _yuv =env->GetByteArrayElements(yuv,0); jint* _bgr =env->GetIntArrayElements(bgr,0); Mat myuv(height+height/2, width, CV_8UC1, (uchar *)_yuv); Mat mbgr(height, width, CV_8UC3, (uchar *)_bgr); cvtColor(myuv, mbgr, CV_yuv420sp2BGR); //cvtColor(mbgr, mbgra,CV_BGR2BGRA); for display}
更正:
cvtColor(myuv, mbgr, CV_yuv420sp2BGR)
裡面的更正為:CV_YUV420sp2BGR。
Android裝置上opencv開發映像格式