筆者今年作了一個和人臉有關的android產品,主要是獲取攝像頭返回的預覽數據流,判斷該數據流是否包含了人臉,有人臉時顯示攝像頭預覽框,無人臉時攝像頭預覽框隱藏,看上去這個功能並不複雜,其實在開發過程當中,遇到的問題也很少,所有都處理了,在正式推出前,這個產品在公司內部也測試了幾個月,也沒發現bug,但最近實施人員,在客戶公司作實施時,反饋回來各類問題,這些問題有部分是程序bug,也有一部分是和硬件有關,由於測試環境有限,筆者沒法對各類型號,各個廠家的硬件進行測試,這篇文章主要是記錄,攝像頭給咱們帶來的一些坑,分享給涉及到人臉開發的朋友,讓你們少走彎路。android
android有原生的api作人臉檢測,經過android.media.FaceDetector來檢測bitmap是否包含人臉,android.media.FaceDetector.Face來檢測人臉位置信息,咱們須要在activity中實現Carema.PreviewCallBack接口,該接口有一個onPreviewFrame方法,這個方法返回攝像頭實時圖像的數據流,因爲這個方法返回的數據流時nv21格式,咱們須要轉換bitmap才能進行人臉檢測,轉換過程以下:byte[] --> YuvImage --> ByteArrayOutputStream --> byte[] --> bitmap ,具體轉換的代碼以下:git
Camera.Size size = mtCamera.getParameters().getPreviewSize(); YuvImage yuvImage = new YuvImage(mData, ImageFormat.NV21, size.width, size.height, null); yuvImage.compressToJpeg(new Rect(0, 0, size.width, size.height), 100, mBitmapOutput); options.inPreferredConfig = Bitmap.Config.RGB_565; bitmap = BitmapFactory.decodeByteArray(mBitmapOutput.toByteArray(), 0, mBitmapOutput.toByteArray().length, options); mBitmapOutput.reset(); bitmap = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), mMatrix, false);
經過上面的轉換,咱們已經獲得了人臉檢測的bitmap,此時只須要進行人臉檢測就ok了,代碼以下:github
detector = new FaceDetector(source.getWidth(),source.getHeight(), maxFaceNum); Face[] faces = new Face[maxFaceNum];
detector.findFaces(source, faces);
代碼基本上就哪麼多,因爲受到硬件的影響,上面的代碼有不少地雷。api
產品上線後,主要問題有,人站在攝像頭面前,app沒法識別人臉,軟件運行性能也會降低,出現嚴重卡頓等問題,當前我比較鬱悶,明明在測試環境都運行幾個月了,都沒有出現這些問題,正式實施的時候,問題不斷,經過近兩個月的整理,主要問題有如下幾個。app
因爲我在測試的時候,攝像頭圖像是垂直的,沒有任何問題,但正式使用時,攝像頭來自不一樣商家,致使攝像頭圖像是水平的了,以下圖:post
圖像角度都不對了,固然沒法識別人臉了,此時咱們須要獲得攝像頭的默認旋轉的角度,再做處理,特別聲明:setDisplayOrientation() 這個方法是逆時針旋轉,代碼以下:性能
public void setCameraDisplayOrientation (Activity activity, int cameraId, android.hardware.Camera camera) { android.hardware.Camera.CameraInfo info = new android.hardware.Camera.CameraInfo(); android.hardware.Camera.getCameraInfo (cameraId , info); int rotation = activity.getWindowManager ().getDefaultDisplay ().getRotation (); int degrees = 0; switch (rotation) { case Surface.ROTATION_0: degrees = 0; break; case Surface.ROTATION_90: degrees = 90; break; case Surface.ROTATION_180: degrees = 180; break; case Surface.ROTATION_270: degrees = 270; break; } int result; if (info.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) { result = (info.orientation + degrees) % 360; result = (360 - result) % 360; // compensate the mirror } else { // back-facing result = ( info.orientation - degrees + 360) % 360; } mOrienta = result;//該值有其它用途 camera.setDisplayOrientation (result); }
這個坑太噁心了,當我把相機角度旋轉後,把app打包發一個給同事,結果同事告訴我,仍是不行,還好在公司借到一個銳士達1080p的攝像頭,而後我把onPreviewFrame返回的流畫到imageView,發現返回的圖像,和預覽的圖像,根本不同,我勒個去,雖然預覽圖像旋轉了,咱們還須要對onPreviewFrame返回的流進行處理,這個坑也讓我比較無語,害我找了很久。雖說解決的代碼只有簡短的幾句,但找出緣由過程只有本身能體會,而後我使用Matrix來旋轉onPreviewFrame返回的流,關於Matrix,徹底是參考android Matrix詳細,這篇文章寫得很是好,修改後的代碼以下。測試
//mOrienta來源於setCameraDisplayOrientation
mMatrix = new Matrix(); switch (mOrienta){ case 90: mMatrix.postRotate(270); break; case 270: mMatrix.postRotate(90); break; default: mMatrix.postRotate(mOrienta); break; }
初始化相機時,咱們須要設置攝像頭支持的預覽尺寸,若是不是相機支持的尺寸,會出現異常,根據項目須要,本地環境我直接指定一個下標,而後硬件變化後,這個值也跟着變了,以下圖:spa
此處根據實際狀況獲取,能夠計算每個尺寸的面積,經過一個基礎面積獲取適應的預覽尺寸。具體代碼就不帖了,只須要清楚有這一個坑就ok了。.net
若是程序的lock,和線程問題沒處理好,性能問題顯而易見。
若是隻是簡單的識別人臉,咱們能夠經過壓縮圖片的方法來解決這個問題。
BitmapFactory.Options options = new BitmapFactory.Options(); options.inSampleSize =2; options.inPreferredConfig = Bitmap.Config.RGB_565; bitmap = BitmapFactory.decodeByteArray(mBitmapOutput.toByteArray(), 0, mBitmapOutput.toByteArray().length, options);
最初軟件運行的時候,運行一段時間,app直接崩潰了,最後發現是,onPreviewFrame返回的流太快,網上說能夠在啓動相機時,設置流的頻率,常見設置的代碼
Camera.Parameters parameters = mCamera.getParameters(); parameters.setPreviewFrameRate(3);//設置每秒3幀,沒有效果
然而這樣設置後,徹底沒有用,如圖:
處理這個問題並非很複雜,只是判斷一個兩次處理流的時候,大於300毫秒(具體時間,根據需求變更)
public void onPreviewFrame(byte[] data, Camera camera) { Logger.i(TAG+"收到相機回調:onpreviewframe()"+index); if(data!=null&&data.length>0&&System.currentTimeMillis()-time>200){ time=System.currentTimeMillis(); mFaceHandle.post(new FaceThread(data,camera,(++index))); } }
經過以上描述咱們知道,相機預覽圖尺寸過大,致使刷臉人員走開幾秒鐘內,android設備屏,仍然顯示和人臉有關的信息,由於onPreviewFrame頻率較快,而處理人臉的時間過長,致使人臉對列愈來愈大,因此人走開後,屏纔會顯示相關信息,這裏須要控制,onPreviewFrame處理人臉的頻率大於,以及提高人臉識別的時間.
完整demo 下載地址:https://github.com/jlq023/democamera