筆者今年做了一個和人臉有關(guān)的android產(chǎn)品,主要是獲取攝像頭返回的預(yù)覽數(shù)據(jù)流,判斷該數(shù)據(jù)流是否包含了人臉,有人臉時顯示攝像頭預(yù)覽框,無人臉時攝像頭預(yù)覽框隱藏,看上去這個功能并不復(fù)雜,其實在開發(fā)過程中,遇到的問題也不多,全部都處理了,在正式推出前,這個產(chǎn)品在公司內(nèi)部也測試了幾個月,也沒發(fā)現(xiàn)bug,但最近實施人員,在客戶公司做實施時,反饋回來各種問題,這些問題有部分是程序bug,也有一部分是和硬件有關(guān),因為測試環(huán)境有限,筆者無法對各種型號,各個廠家的硬件進行測試,這篇文章主要是記錄,攝像頭給我們帶來的一些坑,分享給涉及到人臉開發(fā)的朋友,讓大家少走彎路。
創(chuàng)新互聯(lián)長期為近千家客戶提供的網(wǎng)站建設(shè)服務(wù),團隊從業(yè)經(jīng)驗10年,關(guān)注不同地域、不同群體,并針對不同對象提供差異化的產(chǎn)品和服務(wù);打造開放共贏平臺,與合作伙伴共同營造健康的互聯(lián)網(wǎng)生態(tài)環(huán)境。為吳起企業(yè)提供專業(yè)的成都做網(wǎng)站、成都網(wǎng)站制作,吳起網(wǎng)站改版等技術(shù)服務(wù)。擁有10年豐富建站經(jīng)驗和眾多成功案例,為您定制開發(fā)。
一:概述
Android SDK 中支持人臉檢測,它提供了一個直接在位圖上進行人臉檢測的方法,這個 API 是android.media.FaceDetector,源文件路徑是:
frameworks/base/media/java/android/media/FaceDetector.java
調(diào)用 findFaces 方法就可進行人臉檢測,該方法返回檢測到的人臉總數(shù),并且會將每個”人臉”的信息保存在FaceDetector.Face 的數(shù)組中。每個 Face 都包含下面幾點信息:
識別流程是這樣的:
1. 讀取一張圖片至 Bitmap,且該 Bitmap 必須是 565 格式。
2. 調(diào)用 findFaces 方法分析 Bitmap(注意待分析的 Bitmap 寬度必須是偶數(shù)),將探測到的人臉數(shù)據(jù)存儲在一個FaceDetector.Face 數(shù)組中,并返回檢測到的人臉總數(shù)。Android SDK 中的 FaceDetector 介紹
android有原生的api做人臉檢測,通過android.media.FaceDetector來檢測bitmap是否包含人臉,android.media.FaceDetector.Face來檢測人臉位置信息,我們需要在activity中實現(xiàn)Carema.PreviewCallBack接口,該接口有一個onPreviewFrame方法,這個方法返回攝像頭實時圖像的數(shù)據(jù)流,由于這個方法返回的數(shù)據(jù)流時nv21格式,我們需要轉(zhuǎn)換bitmap才能進行人臉檢測,轉(zhuǎn)換過程如下:byte[] --> YuvImage --> ByteArrayOutputStream --> byte[] --> bitmap ,具體轉(zhuǎn)換的代碼如下:
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);
通過上面的轉(zhuǎn)換,我們已經(jīng)得到了人臉檢測的bitmap,此時只需要進行人臉檢測就ok了,代碼如下:
detector = new FaceDetector(source.getWidth(),source.getHeight(), maxFaceNum); Face[] faces = new Face[maxFaceNum]; detector.findFaces(source, faces);
代碼基本上就哪么多,由于受到硬件的影響,上面的代碼有很多地雷。
二:人臉檢測常見問題
產(chǎn)品上線后,主要問題有,人站在攝像頭面前,app無法識別人臉,軟件運行性能也會下降,出現(xiàn)嚴重卡頓等問題,當前我比較郁悶,明明在測試環(huán)境都運行幾個月了,都沒有出現(xiàn)這些問題,正式實施的時候,問題不斷,通過近兩個月的整理,主要問題有以下幾個。
2.1 無法識別人臉
1):相機角度問題
由于我在測試的時候,攝像頭圖像是垂直的,沒有任何問題,但正式使用時,攝像頭來自不同商家,導(dǎo)致攝像頭圖像是水平的了,如下圖:
圖像角度都不對了,當然無法識別人臉了,此時我們需要得到攝像頭的默認旋轉(zhuǎn)的角度,再作處理,特別聲明:setDisplayOrientation() 這個方法是逆時針旋轉(zhuǎn),代碼如下:
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); }
2):相機設(shè)置旋轉(zhuǎn)后,預(yù)覽圖片和相機返回實時流角度問題
這個坑太惡心了,當我把相機角度旋轉(zhuǎn)后,把app打包發(fā)一個給同事,結(jié)果同事告訴我,還是不行,還好在公司借到一個銳士達1080p的攝像頭,然后我把onPreviewFrame返回的流畫到imageView,發(fā)現(xiàn)返回的圖像,和預(yù)覽的圖像,根本不一樣,我勒個去,雖然預(yù)覽圖像旋轉(zhuǎn)了,我們還需要對onPreviewFrame返回的流進行處理,這個坑也讓我比較無語,害我找了好久。雖然說解決的代碼只有簡短的幾句,但找出原因過程只有自己能體會,然后我使用Matrix來旋轉(zhuǎn)onPreviewFrame返回的流,關(guān)于Matrix,完全是參考android Matrix詳細,這篇文章寫得非常好,然而matrix的postRotate是順時針旋轉(zhuǎn),和camera.setDisplayOrientation()剛好相反,我勒個去,這兩個難兄難弟太不讓人省心,一個順時針,一個逆時針,超級無語,修改后的代碼如下。
//mOrienta來源于setCameraDisplayOrientation mMatrix = new Matrix(); switch (mOrienta){ case 90: mMatrix.postRotate(270); break; case 270: mMatrix.postRotate(90); break; default: mMatrix.postRotate(mOrienta); break; }
2.2 720p攝像頭和1080p攝像頭涉及到的問題
1):獲取攝像頭支持預(yù)覽尺寸遇到的問題
初始化相機時,我們需要設(shè)置攝像頭支持的預(yù)覽尺寸,如果不是相機支持的尺寸,會出現(xiàn)異常,根據(jù)項目需要,本地環(huán)境我直接指定一個下標,然后硬件變化后,這個值也跟著變了,如下圖:
此處根據(jù)實際情況獲取,可以計算每一個尺寸的面積,通過一個基礎(chǔ)面積獲取適應(yīng)的預(yù)覽尺寸。具體代碼就不帖了,只需要清楚有這一個坑就ok了。
2):獲取預(yù)覽偵寬高大小帶來的問題
如果程序的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);
3):攝像頭返回的流頻率過快,導(dǎo)致人臉識別處理速度根不上的解決辦法
最初軟件運行的時候,運行一段時間,app直接崩潰了,最后發(fā)現(xiàn)是,onPreviewFrame返回的流太快,網(wǎng)上說可以在啟動相機時,設(shè)置流的頻率,常見設(shè)置的代碼
Camera.Parameters parameters = mCamera.getParameters(); parameters.setPreviewFrameRate(3);//設(shè)置每秒3幀,沒有效果
然而這樣設(shè)置后,完全沒有用,如圖:
處理這個問題并不是很復(fù)雜,只是判斷一個兩次處理流的時候,大于300毫秒(具體時間,根據(jù)需求變動)
public void onPreviewFrame(byte[] data, Camera camera) { Logger.i(TAG+"收到相機回調(diào):onpreviewframe()"+index); if(data!=null&&data.length>0&&System.currentTimeMillis()-time>200){ time=System.currentTimeMillis(); mFaceHandle.post(new FaceThread(data,camera,(++index))); } }
2.3 刷臉的人員走開后,屏幕仍然顯示和人臉相關(guān)信息
通過以上描述我們知道,相機預(yù)覽圖尺寸過大,導(dǎo)致刷臉人員走開幾秒鐘內(nèi),android設(shè)備屏,仍然顯示和人臉有關(guān)的信息,因為onPreviewFrame頻率較快,而處理人臉的時間過長,導(dǎo)致人臉對列越來越大,所以人走開后,屏才會顯示相關(guān)信息,這里需要控制,onPreviewFrame處理人臉的頻率大于,以及提升人臉識別的時間.
完整demo 下載地址:https://github.com/jlq023/democamera
以上就是本文的全部內(nèi)容,希望對大家的學習有所幫助,也希望大家多多支持創(chuàng)新互聯(lián)。