Android Camera2.0 API實現攝像頭預覽並獲取人臉關鍵座標
Android 5.0(API Level 21)以後推出了新的camera2.0 API,原有的Camera1.0已被廢棄,確實新的camera API有更好的架構,更低的耦合,可以使開發人員發揮更大的空間。
API簡介
主要的類有以下幾個:
1.CameraManager :所有camera的管理類,可以通過呼叫getSystemService()得到其例項,其中的方法getCameraCharacteristics()可以獲取代表camera特徵的類CameraCharacteristics的例項,該特徵類中封裝了攝像頭的各種屬性引數,比如是前置攝像頭還是後置攝像頭等等。
2.CameraDevice:代表一個攝像頭,可以通過其方法createCaptureSession()和 createCaptureRequest()建立CameraCaptureSession以及CaptureRequest的物件例項。
3.CameraDevice.StateCallback:CameraDevice內部類,該類用於接收相機的連線狀態的更新。比如當相機開啟成功後會回撥其中的onOpened方法,當相機連線斷開時會回撥其中的onDisconnected方法。
4.CameraCaptureSession:代表一次拍攝會話,通過setRepeatingRequest()可以開啟攝像頭預覽,capture()方法可以拍照,還有兩個內部類CameraCaptureSession.StateCallback以及CaptureCallback,和CamearDevice的StateCallback一樣,可以監聽預覽或拍攝的過程中出現的一些情況。
人臉檢測
主要使用android.hardware.camera2.params.Face,這是Camera2.0自帶的一個類,可以在createCaptureSession()中從CaptureResult得到,該Face類中封裝了代表人臉基本位置的矩形框,是一個Rect物件,其他還能返回的有兩眼和嘴巴的位置,分別都是Point陣列。
主要程式碼
我們使用TextureView作為攝像頭預覽輸出的載體,建立一個類實現TextureView.SurfaceTextureListener介面,在重寫的方法onSurfaceTextureAvailable()中開啟攝像頭
@Override
public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) {
//設定Camera初始引數
setUpCamera();
//獲取Surface表面
surfaceTexture = surface ;
//設定SurfaceTexture預設大小
surfaceTexture.setDefaultBufferSize(mPreviewSize.getWidth(),mPreviewSize.getHeight());
//開啟後臺執行緒
openBackgroundThread();
//開啟相機
openCamera();
}
一、首先是setUpCamera()函式設定攝像頭的初始化引數,包括人臉檢測的開啟
/**
* 設定camera2.0的初始化引數
*/
private void setUpCamera() {
cameraManager = (CameraManager)mContext.getSystemService(Context.CAMERA_SERVICE);
try{
for (String id : cameraManager.getCameraIdList()) {
//獲取代表攝像頭特徵類characteristics
characteristics = cameraManager.getCameraCharacteristics(id);
//如果是前置攝像頭
if (characteristics.get(CameraCharacteristics.LENS_FACING) == CameraCharacteristics.LENS_FACING_FRONT) {
mCameraId = id ;
StreamConfigurationMap streamConfigurationMap = characteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
sizes = streamConfigurationMap.getOutputSizes(SurfaceHolder.class);
//設定預覽大小
mPreviewSize = sizes[0];
//獲取人臉檢測引數
int[] FD =characteristics.get(CameraCharacteristics.STATISTICS_INFO_AVAILABLE_FACE_DETECT_MODES);
int maxFD=characteristics.get(CameraCharacteristics.STATISTICS_INFO_MAX_FACE_COUNT);
if (FD.length>0) {
List<Integer> fdList = new ArrayList<>();
for (int FaceD : FD
) {
fdList.add(FaceD);
Log.e(TAG, "setUpCameraOutputs: FD type:" + Integer.toString(FaceD));
}
Log.e(TAG, "setUpCameraOutputs: FD count" + Integer.toString(maxFD));
if (maxFD > 0) {
mFaceDetectSupported = true;
mFaceDetectMode = Collections.max(fdList);
}
}
}
}
} catch ( CameraAccessException e ){
e.printStackTrace();
}
}
二、然後openCamera()方法開啟攝像頭,在其中檢查是否開啟攝像頭許可權
/**
* 檢視攝像頭並開啟攝像機
*/
public void openCamera(){
try {
//判斷是否開啟攝像頭許可權
if (PermissionChecker.checkSelfPermission( mContext , Manifest.permission.CAMERA) == PackageManager.PERMISSION_GRANTED) {
cameraManager.openCamera(mCameraId, cameraCallback, mBackgroundHandler);
}else {
Toast.makeText( mContext ,"請開啟攝像頭許可權",Toast.LENGTH_SHORT).show();
}
} catch (CameraAccessException e) {
e.printStackTrace();
}
}
三、攝像頭開啟成功後,在CameraDevice.StateCallback的onOpened()方法中開啟預覽
//cameraCallback回撥介面
private CameraDevice.StateCallback cameraCallback = new CameraDevice.StateCallback(){
//若攝像機開啟成功則回撥此方法
@Override
public void onOpened(CameraDevice camera) {
//獲取cameraDevice
cameraDevice = camera;
//開啟預覽
startPreview();
}
//攝像機連線斷開回調此方法
@Override
public void onDisconnected(CameraDevice camera) {
if(cameraDevice != null ){
cameraDevice.close();
}
}
//出現異常回調此方法
@Override
public void onError(CameraDevice camera, int error) {
if(cameraDevice != null ){
cameraDevice.close();
}
}
};
四、startPreview()方法開啟預覽,並打印出返回的人臉位置座標
public void startPreview(){
try{
Surface surface = new Surface(surfaceTexture);
previewRequestBuilder = cameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
previewRequestBuilder.addTarget(surface);
/*previewRequestBuilder.addTarget(mImageReader.getSurface());*/
previewRequestBuilder.set(CaptureRequest.STATISTICS_FACE_DETECT_MODE,
CameraMetadata.STATISTICS_FACE_DETECT_MODE_FULL);
cameraDevice.createCaptureSession(Arrays.asList(surface,mImageReader.getSurface()), new CameraCaptureSession.StateCallback() {
@Override
public void onConfigured(@NonNull CameraCaptureSession session) {
try {
//構建captureRequest物件
captureRequest = previewRequestBuilder.build();
//設定人臉檢測
setFaceDetect(previewRequestBuilder,mFaceDetectMode);
captureSession = session;
captureSession.setRepeatingRequest(captureRequest, new CameraCaptureSession.CaptureCallback() {
/**
* 對攝像頭返回的結果進行處理,並獲取人臉資料
* @param result 攝像頭資料
*/
private void process(CaptureResult result) {
//獲得Face類
Face face[]=result.get(CaptureResult.STATISTICS_FACES);
//如果有人臉的話
if (face.length>0 ){
Log.e(TAG, "face detected " + Integer.toString(face.length));
//獲取人臉矩形框
Rect bounds = face[0].getBounds();
float y = mPreviewSize.getHeight()/2 - bounds.top ;
Log.e("height" , String.valueOf(mPreviewSize.getWidth()));
Log.e("top" , String.valueOf(y));
Log.e("left" , String.valueOf(bounds.left));
Log.e("right" , String.valueOf(bounds.right));
}
}
@Override
public void onCaptureStarted(CameraCaptureSession session, CaptureRequest request, long timestamp, long frameNumber) {
super.onCaptureStarted(session, request, timestamp, frameNumber);
}
@Override
public void onCaptureProgressed(CameraCaptureSession session, CaptureRequest request, CaptureResult partialResult) {
process(partialResult);
}
@Override
public void onCaptureCompleted(CameraCaptureSession session, CaptureRequest request, TotalCaptureResult result) {
process(result);
}
},mBackgroundHandler);
} catch (CameraAccessException e) {
e.printStackTrace();
}
}
@Override
public void onConfigureFailed(CameraCaptureSession session) {
}
}, null);
}catch (CameraAccessException e){
e.printStackTrace();
}
}
最後在佈局檔案中新增一個TextureView,並在Activity中獲取其檢視,為其設定SurfaceTextureListener,也就是剛才自定義的類,就OK了。