1. 程式人生 > >Android Google人臉識別 利用雙層 SurfaceView 原始碼 實戰

Android Google人臉識別 利用雙層 SurfaceView 原始碼 實戰

雙層SurfaceView:

<FrameLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"  android:layout_gravity="center_vertical|center_horizontal" >      
        < SurfaceView
            android:id="@+id/usb_preview_content"
            android:layout_width="match_parent"
            android:layout_height="match_parent" />
        <SurfaceView
            android:id="@+id/mipi_preview_content"
            android:layout_width="match_parent"
            android:layout_height="match_parent" />
    </FrameLayout>

上層SurfaceView半透明:

surfaceview.setZOrderOnTop(true);  
surfaceview.getHolder().setFormat(PixelFormat.TRANSLUCENT);  

自適應角度和矩形調整(因為android設定的原始座標和普通的不一樣):

public static void prepareMatrix(Matrix matrix, boolean mirror, int displayOrientation,
            int viewWidth, int viewHeight) {
        // Need mirror for front camera.
        matrix.setScale(mirror ? -1 : 1, 1);
        // This is the value for android.hardware.Camera.setDisplayOrientation.
        matrix.postRotate(displayOrientation);
        // Camera driver coordinates range from (-1000, -1000) to (1000, 1000).
        // UI coordinates range from (0, 0) to (width, height).
        matrix.postScale(viewWidth / 2000f, viewHeight / 2000f);
        matrix.postTranslate(viewWidth / 2f, viewHeight / 2f);
    }

原始碼:

佈局檔案:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">
    <ScrollView
        android:layout_width="match_parent"
        android:layout_height="match_parent">
        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:orientation="vertical" >



            <FrameLayout
                android:layout_width="match_parent"
                android:layout_height="300dp"
                android:layout_gravity="center_vertical|center_horizontal" >
                <SurfaceView
                    android:id="@+id/sv_camera_face"
                    android:layout_width="match_parent"
                    android:layout_height="match_parent" />
                <SurfaceView
                    android:id="@+id/sv_camera"
                    android:layout_width="match_parent"
                    android:layout_height="match_parent" />
            </FrameLayout>
            <TextView
                android:id="@+id/sample_text"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="Hello World!" />
        </LinearLayout>

    </ScrollView>



</LinearLayout>

程式碼檔案(為了測試功能,所以沒做工程化開發,都放在一個檔案裡了):

package com.findai.xkk.ai_interviewer;

import android.Manifest;
import android.content.Context;
import android.content.pm.PackageManager;
import android.content.res.Configuration;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.PixelFormat;
import android.graphics.PorterDuff;
import android.graphics.Rect;
import android.graphics.RectF;
import android.hardware.Camera;
import android.support.v4.app.ActivityCompat;
import android.support.v4.content.ContextCompat;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.AttributeSet;
import android.util.Log;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.widget.TextView;

import java.io.IOException;

public class MainActivity extends AppCompatActivity {


    // Used to load the 'native-lib' library on application startup.
    static {
        System.loadLibrary("native-lib");
    }

    private Camera camera;
    private SurfaceHolder sh;
    private SurfaceView sv_camera;
    private Matrix matrix = new Matrix();
    private SurfaceView sv_camera_face;
    private SurfaceHolder sv_camera_face_holder;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        camera = Camera.open(1);

        // Example of a call to a native method
        TextView tv = (TextView) findViewById(R.id.sample_text);
        sv_camera = findViewById(R.id.sv_camera);
        sv_camera_face = findViewById(R.id.sv_camera_face);
        tv.setText(stringFromJNI());
//        Camera.open();
        sh = sv_camera.getHolder();
        sv_camera_face_holder = sv_camera_face.getHolder();
        sv_camera_face_holder.setFormat(PixelFormat.TRANSLUCENT);
//        sv_camera_face_holder.addCallback();

        sh.addCallback(new SurfaceCallback());
        if (ContextCompat.checkSelfPermission(this, Manifest.permission.CAMERA)
                == PackageManager.PERMISSION_GRANTED) {
            Log.i("TEST","Granted");
            //init(barcodeScannerView, getIntent(), null);
        } else {
            ActivityCompat.requestPermissions(this,
                    new String[]{Manifest.permission.CAMERA}, 1);//1 can be another integer
        }
    }

    /**
     * A native method that is implemented by the 'native-lib' native library,
     * which is packaged with this application.
     */
    public native String stringFromJNI();




    private class SurfaceCallback implements SurfaceHolder.Callback{

        @Override
        public void surfaceCreated(SurfaceHolder holder) {
            sh = holder;
            try {
                followScreenOrientation(getBaseContext(),camera);
                camera.startFaceDetection();
                camera.setFaceDetectionListener(new FaceDetectListener());

                final Camera.Parameters params = camera.getParameters();
                params.setFocusMode(Camera.Parameters.FOCUS_MODE_AUTO);

                params.setSceneMode(Camera.Parameters.SCENE_MODE_BARCODE);
                params.setJpegQuality(100); // 設定照片質量

                camera.setPreviewDisplay(holder);
                camera.startPreview();
                Log.i("camera","create");

            }catch (Exception ex){
                ex.printStackTrace();
            }
        }

        @Override
        public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
            Log.i("camera","change");
        }

        @Override
        public void surfaceDestroyed(SurfaceHolder holder) {
            camera.stopPreview();
            camera.setPreviewCallback(null );
            camera.release();
        }
    }

    public static void followScreenOrientation(Context context, Camera camera){
        final int orientation = context.getResources().getConfiguration().orientation;
        if(orientation == Configuration.ORIENTATION_LANDSCAPE) {
            camera.setDisplayOrientation(180);
        }else if(orientation == Configuration.ORIENTATION_PORTRAIT) {
            camera.setDisplayOrientation(90);
        }
    }

    private class FaceDetectListener implements Camera.FaceDetectionListener{


        @Override
        public void onFaceDetection(Camera.Face[] faces, Camera camera) {
            Log.i("face_position",faces.toString());
            if(faces.length>0) {
                Rect face_rect = faces[0].rect;
                Log.i("檢測到了",face_rect.toString());
                try{
                    Paint paint = new Paint();
                    paint.setColor(Color.RED);
                    paint.setStrokeWidth(5f);
                    paint.setAlpha(180);
                    paint.setStyle(Paint.Style.STROKE);
                    Canvas face_canvas = sv_camera_face_holder.lockCanvas();
                    face_canvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR);
//                    face_canvas.drawColor(Color.BLUE);

                    prepareMatrix(matrix, true, 90, sv_camera.getWidth(), sv_camera.getHeight());
                    RectF rf = new RectF(face_rect.left,
                            face_rect.top,
                            face_rect.right,
                            face_rect.bottom);


                    matrix.mapRect(rf);

                    face_rect =new Rect();
                    rf.round(face_rect);

                    face_canvas.drawRect(new Rect(face_rect.left,
                            face_rect.top,
                            face_rect.right,
                            face_rect.bottom),paint);//繪製矩形


                    sv_camera_face_holder.unlockCanvasAndPost(face_canvas );
                }catch (Exception ex){
                    Log.i("出錯了","2");
                }
            }
            //
//            Canvas face_canvas = sh.lockCanvas(new Rect(1200, 1200, 1220, 1220));
//            sh.unlockCanvasAndPost(face_canvas );
        }
    }


    /**
     * 因為對攝像頭進行了旋轉,所以同時也旋轉畫板矩陣
     * 詳細請檢視{@link android.hardware.Camera.Face#rect}
     * @return 旋轉後的矩陣
     */
    private Matrix updateFaceRect() {
        Matrix matrix = new Matrix();
        Camera.CameraInfo info = new android.hardware.Camera.CameraInfo();
        // Need mirror for front camera.
        boolean mirror = (info.facing == Camera.CameraInfo.CAMERA_FACING_FRONT);
        matrix.setScale(mirror ? -1 : 1, 1);
        // This is the value for android.hardware.Camera.setDisplayOrientation.
        // 剛才我們設定了camera的旋轉引數,所以這裡也要設定一下
        matrix.postRotate(90);
        // Camera driver coordinates range from (-1000, -1000) to (1000, 1000).
        // UI coordinates range from (0, 0) to (width, height).
        matrix.postScale(sv_camera.getWidth() / 2000f, sv_camera.getHeight() / 2000f);
        matrix.postTranslate(sv_camera.getWidth() / 2f, sv_camera.getHeight() / 2f);
        return matrix;
    }

    private class SurfaceView_face_Callback implements SurfaceHolder.Callback{
        @Override
        public void surfaceCreated(SurfaceHolder holder) {
            followScreenOrientation(getBaseContext(),camera);
        }

        @Override
        public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {

        }

        @Override
        public void surfaceDestroyed(SurfaceHolder holder) {

        }
    }
    public static void prepareMatrix(Matrix matrix, boolean mirror, int displayOrientation,
                                     int viewWidth, int viewHeight) {
        // Need mirror for front camera.
        matrix.setScale(mirror ? -1 : 1, 1);
        // This is the value for android.hardware.Camera.setDisplayOrientation.
        matrix.postRotate(displayOrientation);
        // Camera driver coordinates range from (-1000, -1000) to (1000, 1000).
        // UI coordinates range from (0, 0) to (width, height).
        matrix.postScale(viewWidth / 2000f, viewHeight / 2000f);
        matrix.postTranslate(viewWidth / 2f, viewHeight / 2f);
    }
}

配置檔案:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.findai.xkk.ai_interviewer">

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

    <uses-permission android:name="android.permission.CAMERA" />
    <uses-feature android:name="android.hardware.camera" />
    <uses-feature android:name="android.hardware.camera.autofocus" />

</manifest>