1. 程式人生 > >Android Camera2.0 API實現攝像頭預覽並獲取人臉關鍵座標

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了。