2D人脸识别之Camera篇
一、概述
本文主要介紹2D人臉識別中的Camera圖像采集部分,目前市面上Android陣營中大部分機型都使用的是2D人臉識別;圖像采集主要是獲取目標的RGB圖像,2D人臉圖像獲取相對簡單,只需要獲取到RGB的圖像信息,不需要深度信息。獲取圖像數據信息的方式只需要一個普通攝像頭模組即可,簡單方便。
二、Camera組件架構
圖1 Camera架構
圖1主要展示了Camera HAL及以上的組件架構,Android 5.0對拍照API進行了全新的設計,新增了全新設計的Camera v2 API,這些API不僅大幅提高了Android系統拍照的功能,還能支持RAW照片輸出,甚至允許程序調整相機的對焦模式、曝光模式、快門等。
三、Camera數據流
1. Camera整體模型
圖2 Camera整體模型圖
應用框架針對捕獲的結果向相機子系統發出請求。一個請求對應一組結果。請求包含有關捕獲和處理這些結果的所有配置信息。其中包括分辨率和像素格式;手動傳感器、鏡頭和閃光燈控件;3A 操作模式;RAW 到YUV 處理控件;以及統計信息的生成等。這樣一來,便可更好地控制結果的輸出和處理。一次可發起多個請求,而且提交請求時不會出現阻塞。請求始終按照接收的順序進行處理。
2.Camera Pipeline
圖3 Camera Pipeline
圖3主要描述Camera Sensor出圖后經過不同圖像處理后輸出不同處理的圖像。
其中Image Processing模塊里面的一些參數是可以由應用層進行配置,比如Feature參數;
舉例說明:
問題:在實際項目調試人臉識別Feature過程中就遇到過因為圖質量較差的原因導致識別失敗的問題,那如何解決?
解決方法:既然是圖差的原因導致的,那就只能正向去優化人臉圖像質量了,在圖像處理模塊中有一個模塊叫NoiseReduction(降噪),查詢默認參數為NOISE_REDUCTION_MODE_OFF
即Camera模塊是關閉降噪功能的,將此參數設定為:
NOISE_REDUCTION_MODE_HIGH_QUALITY后再dump出人臉圖像效果改善明顯;
四、Camera Capture和Preview
1. Camera Capture
應用層發起Capture請求且注冊onImageCaptureCallback函數后,Camera模塊拍照完會回調到應用端獲取拍照圖像buf;
如何設定Camera拍照參數:
android.hardware.camera2.CaptureRequest.Builder
A builder for capture requests.
CaptureRequest?表示一個捕捉的請求。可以為不同的場景(預覽、拍照)創建不同的捕捉請求,并可以配置不同的捕捉屬性,如:預覽分辨率,預覽目標,對焦模式、曝光模式等等。
通過 CameraDevice 對象的 createCaptureRequest() 方法得到一個 CaptureRequest.Builder 對象,基本配置都是通過該構造者來配置;最后通過CaptureRequest.Builder 對象的 build() 方法便可得到 CaptureRequest 實例。
上述代碼中就是通過CONTROL_AF_MODE_CONTINUOUS_PICTURE參數打開自動對焦功能;
captureBuilder.set(CaptureRequest.CONTROL_AF_MODE,CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE);
2. Camera Preview
應用層下發Surface資源到Camera模塊且設定重復拍照模式setRepeatingRequest即可實現拍照預覽;
五、Camera拍照詳細流程
1.?拍照詳細流程
上圖所示為Camera拍照的完整鏈路,其中最重要的函數調用就是connectDevice操作,這個步驟是實現Client連接CameraServer的關鍵一環,在項目開發過程中可能經常會遇到connect CameraServer失敗的問題,比如Client進程對Camera權限沒有配置,或者ImagerReader對象上一次操作沒有釋放導致沒有調用disconnect還處于connect狀態最終導致無法connect,又或者Camera資源本身被相機占用提示busy狀態等等,針對各種場景需要對應措施進行處理,目的都是為了能否使Client和server連接成功。
同時在開發階段,也需要對上述中異常情況進行提前預判并加入防呆措施,比如OpenCamera多次retry。
2.?圖像格式說明
初始化時已經設置了圖像格式為AIMAGE_FORMAT_YUV_420_888
下面解釋下YUV三通道的數據格式排布:
planes[0] 總是Y ,planes[1] 總是U(Cb), planes[2]總是V(Cr)。
在ImageFormat中,YUV_420_888格式的數值是35,當前格式是YUV_420_888,根據image的分辨率是 640 x 480 ,像素點個數是307200。下面分別對plane[0]、plane[1]、plane[2]作分析。
plane[0]表示Y,rowStride是640 ,其pixelStride是1 ,Y存儲時中間無間隔,每行640個像素全是Y值,buffer size 是 plane[0]的1/4 ,buffer size / rowStride= 480,Y有480行。
plane[1]表示U,rowStride是320,其pixelStride也是1,連續的U之間沒有間隔,每行只存儲了320個數據,buffer size 是 plane[0]的1/4 ,buffer size / rowStride = 240 可知U有240行,對于U來說橫縱都是1/2采樣。
pane[2]和plane[1]相同。
六、Android人臉框架
1.?人臉架構
2.?人臉狀態圖
七、人臉Preview關鍵參數鏈路
1. Preview
2.?Surface資源傳遞
APP層會創建Surface資源
?創建Surface之后會將此資源經過Face框架一直傳遞到FaceHAL如上圖所示,FaceHAL會將Surface資源傳遞到Camera模塊。具體Surface資源格式如下圖所示:
Surface資源是個動態Buf,可變參數長度由numFds、numInts決定,在version numFds numInts后的參數個數為numFds+numInts。
3. native_handle_t
在代碼中結構體信息如下:
在代碼中結構體信息如下:
native_handle_t只是定義了一個描述buffer的結構體原型,這個原型和平臺 無關,方便buffer在各個進程之間傳遞;附錄NDK_VENDOR CameraAPI2預覽和拍照代碼實現
1. 代碼鏈接
https://android.googlesource.com/platform/frameworks/av/+/refs/heads/master/camera/ndk/ndk_vendor/tests/AImageReaderVendorTest.cpp
2. Camera拍照流程-initImageReader
?首先會調用initImageReader()函數,創建ImageReader對象,初始化圖像寬度、高度、圖像輸出格式、用戶屬性、最大圖像數量;
通過AImageReader_setImageListener設置回調函數onImageAvailable
在回調函數中調用AImageReader_acquireNextImage從ImageReader隊列中請求圖像數據
接著調用AImage_getHardwareBuffer從HardwareBuffer對象中獲取像素的實際數據:
下面是判斷獲取的數據是否符合預期大小:
?判斷完圖像長度、寬度后就是如何解析出真正需要的圖像數據,使用AImage_getPlaneData函數可以解析出對應通道的像素矩陣;
3. Camera拍照流程-CameraHelper
在初始化完ImagerReader后,會新建cameraHelper對象,cameraHelper會調用initCamera()函數對Camera相關進行初始化;
那cameraHelper.initCamera里面具體細節干了什么呢?我們繼續往下分析
(1)ACameraManager_openCamera
?initCamera函數中首先調用的是ACameraManager_openCamera操作,client發起對Camera的連接動作,我們可以繼續追蹤下ACameraManager_openCamera具體實現如下:
繼續調用ACameraManager類中的openCamera函數:
?進入到這個函數已經是比較底層,就不一個個函數講了,其中最重要的函數調用就是connectDevice操作,這個步驟是實現Client連接CameraServer的關鍵一環,在項目開發過程中可能經常會遇到connect CameraServer失敗的問題,比如Client進程對Camera權限沒有配置,或者ImagerReader對象上一次操作沒有釋放導致沒有調用disconnect還處于connect狀態最終導致無法connect,又或者Camera資源本身被相機占用提示busy狀態等等,針對各種場景需要對應措施進行處理,目的都是為了能否使Client和server連接成功。
順便再提下open_camera中還有一個大家熟知的獲取Camera服務操作:
(2)ACaptureSessionOutputContainer_create
創建ACaptureSessionOutput類型集合mOutputs
然后mOutputs集合添加ACaptureSessionOutput新建的對象mImgReaderOutput
(3)cameraHelper.takePicture
takePicture()函數被封裝為觸發Camera執行拍照動作;
具體實現為:
4. Camera Preview
(4)關鍵資源參數Surface
上面說了這么多拍照的流程,大家一定想知道Camera Preview是怎么實現的?繼續跟蹤Android源碼;
我們先找到人臉錄入的相關代碼:
鏈接:
https://android-opengrok.bangnimang.net/android-12.0.0_r3/xref/frameworks/base/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceEnrollClient.java?r=659b4ee9#54
其中有一個跟預覽很關鍵的參數:Surface previewSurface
上面人臉錄入是AndroidS以前經常用的HIDL接口方式,下面是AIDL接口
鏈接:
https://android-opengrok.bangnimang.net/android-12.0.0_r3/xref/frameworks/base/services/co
re/java/com/android/server/biometrics/sensors/face/aidl/FaceEnrollClient.java?r=659b4ee9
那有人就會問了,這個previewSurface是從哪里傳下來的呢?
咱們再接著一層層挖掘。
(5)settings
代碼鏈接:
https://android-opengrok.bangnimang.net/android-12.0.0_r3/xref/packages/apps/Settings/src/c
om/android/settings/biometrics/face/FaceEnrollSidecar.java?r=35e1f0d1#51
對于使用VENDOR_NDK Camera API2接口傳遞Surface是使用FaceManager中的
enroll方法;
目前能從原生代碼中找到的是Java層控制Camera如下:
(6)FaceManager
代碼鏈接:
https://android-opengrok.bangnimang.net/android-12.0.0_r3/xref/frameworks/base/core/java/a
ndroid/hardware/face/FaceManager.java?r=436f041e
(7)FaceService
代碼鏈接:
https://android-opengrok.bangnimang.net/android-12.0.0_r3/xref/frameworks/base/services/co
re/java/com/android/server/biometrics/sensors/face/FaceService.java?r=88a506aa
(8)FaceProvider
代碼鏈接:
https://android-opengrok.bangnimang.net/android-12.0.0_r3/xref/frameworks/base/services/co
re/java/com/android/server/biometrics/sensors/face/aidl/FaceProvider.java?r=88a506aa#334
(9)FaceEnrollClient
調用到FaceEnrollClient這層時會再調用到startHalOperation函數
總結
以上是生活随笔為你收集整理的2D人脸识别之Camera篇的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: React Native通信机制详解
- 下一篇: 基于OpenCV与MFC的人脸识别