1. 程式人生 > >android 拍攝預覽過程中人臉識別

android 拍攝預覽過程中人臉識別

仔細想想距離寫上篇文章已經很久了,最近都忙於工作和睡覺打遊戲(朋友的名字叫遊溪),以至於沒有寫接下來的東西。。記得上篇文章寫的是關於自定義camera的(http://blog.csdn.net/lzan13/article/details/54017224)。。那麼該篇將在原有的基礎上加上人臉識別的功能。

已經親測,可以實現!!!

三個類,很簡單,其他難得我也不會搞:

 主要通過:FaceDetectionListener 實現

廢話不多說直接上程式碼:

AutioTakeActiity  該頁面只有一個跳轉按鈕,和一個用來顯示拍攝照片的imageview
package com.example.auto_take_picture;
import android.Manifest; import android.app.Activity; import android.content.Intent; import android.content.pm.PackageManager; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.graphics.Matrix; import android.os.Bundle; import android.support.annotation.NonNull; import
android.support.v4.app.ActivityCompat; import android.support.v4.content.ContextCompat; import android.view.View; import android.widget.ImageView; import android.widget.Toast; /** * Created by Administrator on 2017/5/5 0005. */ public class AutioTakeActiity extends Activity { private static final int
REQUEST_CAMERA_CODE_TEACHER = 1;// 拍照 private ImageView ima; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_takephoto_result); ima=(ImageView)findViewById(R.id.ima); } /** * 點選拍照 * @param view */ public void takePhoto(View view){ getPerssiom(); } /** * android6.0 申請拍照許可權 */ private void getPerssiom(){ if (ContextCompat.checkSelfPermission(this, Manifest.permission.CAMERA) == PackageManager.PERMISSION_GRANTED && ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED && ContextCompat.checkSelfPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED) { gotoTake(); } else { ActivityCompat.requestPermissions(this, new String[]{ Manifest.permission.WRITE_EXTERNAL_STORAGE, Manifest.permission.CAMERA, Manifest.permission.READ_EXTERNAL_STORAGE }, REQUEST_CAMERA_CODE_TEACHER); } } /** * 跳轉至自定義的拍照頁面 */ private void gotoTake(){ Intent intent =new Intent(this,AutioTakePhotoActivity.class); startActivityForResult(intent ,1); } /** * 拍照完成之後拿到路徑設定imageview * @param requestCode * @param resultCode * @param data */ @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); if(resultCode==RESULT_OK){ if(requestCode==1){ boolean isBack= data.getBooleanExtra("isBack",true); String path = data.getStringExtra("picPath"); Bitmap bitmap = BitmapFactory.decodeFile(path); Matrix matrix=new Matrix(); if(isBack){//由於拍照時旋轉了90度。這裡通矩陣旋轉回來。(後置攝像頭) matrix.setRotate(90); }else{ matrix.setRotate(-90); // ( 前置攝像頭) } Bitmap bit= bitmap.createBitmap(bitmap,0,0,bitmap.getWidth(),bitmap.getHeight(),matrix,true); ima.setImageBitmap(bit); } } } @Override public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { super.onRequestPermissionsResult(requestCode, permissions, grantResults); switch (requestCode) { case REQUEST_CAMERA_CODE_TEACHER: if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED && grantResults.length > 1 && grantResults[1] == PackageManager.PERMISSION_GRANTED && grantResults.length > 2 && grantResults[2] == PackageManager.PERMISSION_GRANTED) { gotoTake(); } else { Toast.makeText(this, "獲取許可權失敗", Toast.LENGTH_SHORT).show(); } break; default: break; } } }
//上面類對應的佈局
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical" android:layout_width="match_parent"
android:layout_height="match_parent">
    <TextView
android:id="@+id/take"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="去拍照"
android:padding="15dp"
android:onClick="takePhoto"
android:layout_gravity="center"
/>
    <ImageViewandroid:id="@+id/ima"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:src="@drawable/show"
/>
</LinearLayout>

// 該類完成拍攝和展示,以及人臉識別的功能
package com.example.auto_take_picture;
import android.app.Activity;
import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Matrix;
import android.hardware.Camera;
import android.os.Bundle;
import android.os.Environment;
import android.os.Handler;
import android.os.Message;
import android.util.Log;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.View;
import android.view.WindowManager;
import android.widget.ImageView;
import android.widget.RelativeLayout;
import android.widget.TextView;
import android.widget.Toast;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.List;
/**
 * Created by Administrator on 2017/5/5 0005.
 */
public class AutioTakePhotoActivity extends Activity implements  SurfaceHolder.Callback{
    private SurfaceHolder holder;
    private SurfaceView surfaceView;
Camera mCamera;
    byte[] photoBytes;
    private ImageView image;
    private RelativeLayout rela_result;
    public static int cameraId;
    private boolean isBack=true;
    private FaceView faceView;
    private TextView txt_turn;
@Override
protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
setContentView(R.layout.activity_take_photo);
getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);//保持螢幕常亮
rela_result=(RelativeLayout)findViewById(R.id.result) ;  //在預覽介面之上覆蓋一層頁面,顯示拍攝的圖片
image=(ImageView)findViewById(R.id.image);               //顯示拍攝的圖片
surfaceView=(SurfaceView)findViewById(R.id.surfaceview);
holder= surfaceView.getHolder();
faceView=(FaceView)findViewById(R.id.face_view);        //自定義的view 。用來繪製檢測人臉的方框;
cameraId=findBackCamera();                              //攝像頭的id,區分前置還是後置攝像頭
holder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
txt_turn=(TextView)findViewById(R.id.turn);
txt_turn.setText("前置/後置------當前:後置");
holder.addCallback(this);
}

    public void take(View view){          //點選拍照的方法。
Camera.Parameters parameters =mCamera.getParameters();
parameters.setFocusMode(Camera.Parameters.FOCUS_MODE_AUTO);
mCamera.autoFocus(new Camera.AutoFocusCallback() {
            @Override
public void onAutoFocus(boolean b, Camera camera) {
                if(b){        //如果焦點獲取成功,拍照
mCamera.takePicture(null,null,pictureCallBack);  //pictureCallBack 為拍照的回掉。
}
            }
        });
}

    /**
     * 攝像頭的切換
     * @param view
*/
public void turn(View view){
            if(isBack){
                cameraId=findFrontCamera();  //前置攝像頭id
txt_turn.setText("前置/後置------當前:前置");
}else{
                cameraId=findBackCamera();  //後置
txt_turn.setText("前置/後置------當前:後置");
}
        isBack=!isBack;
        if (mCamera != null) {
            mCamera.stopPreview();
mCamera.release(); // 釋放照相機
}
        mCamera = Camera.open(cameraId);
setCameraParams(1080,1920);//隨便設的值
try {
            mCamera.setPreviewDisplay(holder);
mCamera.setDisplayOrientation(90);
mCamera.startPreview();
mainHandler.sendEmptyMessage(1);
} catch (IOException e) {
            e.printStackTrace();
}
    }


    /**
     * 點選完成儲存圖片,並將路徑回傳給上個頁面
     * @param view
*/
public void finish(View view){          //完成
File file=new File(Environment.getExternalStorageDirectory().getPath() + "/test2.jpg");
        if (file.exists()) {
            file.delete();
}
        try {
            FileOutputStream fo=new FileOutputStream(file);
fo.write(photoBytes);
fo.close();
Toast.makeText(AutioTakePhotoActivity.this,"已儲存圖片", Toast.LENGTH_SHORT).show();
Intent intent =new Intent(AutioTakePhotoActivity.this,AutioTakeActiity.class);                         //完成拍照,回到mainActivity,並將圖片的路徑回傳
intent.putExtra("picPath", Environment.getExternalStorageDirectory().getPath() + "/test2.jpg");
intent.putExtra("isBack",isBack);
setResult(RESULT_OK,intent);
finish();
} catch (FileNotFoundException e) {
            e.printStackTrace();
} catch (IOException e) {
            e.printStackTrace();
}
    }

    /**
     * 取消,放棄該圖片
     * @param view
*/
public void cancle(View view){          //取消
photoBytes=null;
rela_result.setVisibility(View.GONE);
mCamera.startPreview();
}


    /**
     * 顯示拍出的圖片
     */
private  void show(byte[] photoBytes){
        Bitmap bitmap = BitmapFactory.decodeByteArray(photoBytes, 0, photoBytes.length);
Matrix matrix=new Matrix();
        if(isBack){  //後置攝像頭
matrix.setRotate(90);
}else{
            matrix.setRotate(-90); //前置攝像頭
}
        Bitmap bit=  bitmap.createBitmap(bitmap,0,0,bitmap.getWidth(),bitmap.getHeight(),matrix,true);
        if(bitmap!=null&&!bitmap.isRecycled()){
            bitmap.recycle();
}
        if(bit!=null){
            image.setImageBitmap(bit);
}
        rela_result.setVisibility(View.VISIBLE);
}

    private Camera.PictureCallback  pictureCallBack =new Camera.PictureCallback() {
        @Override
public void onPictureTaken(byte[] bytes, Camera camera) {     //bytes 即是拍照回來的內容,將內容寫入本地即可
photoBytes=bytes;
show(photoBytes);
}
    };
/**
     * 拿到前置攝像頭id
     */
public static int findFrontCamera() {
        int cameraId = -1;
        int numberCameras = Camera.getNumberOfCameras();
        for (int i = 0; i < numberCameras; i++) {
            Camera.CameraInfo info = new Camera.CameraInfo();
Camera.getCameraInfo(i, info);
            if (info.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) {
                cameraId = i;
                break;
}
        }
        return cameraId;
}

    /**
     * 拿到後置攝像頭id
     */
public static int findBackCamera() {
        int cameraId = -1;
        int numberCameras = Camera.getNumberOfCameras();
        for (int i = 0; i < numberCameras; i++) {
            Camera.CameraInfo info = new Camera.CameraInfo();
Camera.getCameraInfo(i, info);
            if (info.facing == Camera.CameraInfo.CAMERA_FACING_BACK) {
                cameraId = i;
                break;
}
        }
        return cameraId;
}
    @Override
public void surfaceCreated(SurfaceHolder holder) {
        Log.e("zjun","surfaceCreated");
        try {
            if(mCamera==null){
                mCamera= Camera.open(cameraId);
mCamera.setPreviewDisplay(holder);
setCameraParams(1080,1920);
}
        } catch (IOException e) {
            e.printStackTrace();
}
    }

    @Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
        Log.e("zjun","surfaceChanged");
mCamera.startPreview();
mainHandler.sendEmptyMessage(1);
}



    @Override
public void surfaceDestroyed(SurfaceHolder holder) {
        if (mCamera != null) {
            mCamera.release(); // 釋放照相機
mCamera = null;
}
    }

    /**
     * 設定引數
     * @param width
* @param height
*/
private void setCameraParams(int width, int height) {
        Camera.Parameters parameters = mCamera.getParameters();
// 獲取攝像頭支援的PictureSize列表
List<Camera.Size> pictureSizeList = parameters.getSupportedPictureSizes();
/**從列表中選取合適的解析度*/
Camera.Size picSize = getProperSize(pictureSizeList, ((float) height / width));
        if (null == picSize) {
            picSize = parameters.getPictureSize();
}
        // 根據選出的PictureSize重新設定SurfaceView大小
parameters.setPictureSize(picSize.width,picSize.height);
// 獲取攝像頭支援的PreviewSize列表
List<Camera.Size> previewSizeList = parameters.getSupportedPreviewSizes();
Camera.Size preSize = getProperSize(previewSizeList, ((float) height) / width);
        if (null != preSize) {
            Log.e("zjun", "preSize.width=" + preSize.width + "  preSize.height=" + preSize.height);
parameters.setPreviewSize(preSize.width, preSize.height);
}
        parameters.setJpegQuality(100); // 設定照片質量
if (parameters.getSupportedFocusModes().contains(Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE)) {
            parameters.setFocusMode(Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE);// 連續對焦模式
}
        mCamera.cancelAutoFocus();//自動對焦。
mCamera.setDisplayOrientation(90);// 設定PreviewDisplay的方向,效果就是將捕獲的畫面旋轉多少度顯示
mCamera.setParameters(parameters);
}

    private MainHandler mainHandler = new MainHandler();
    private void startGoogleDetect() {
        Camera.Parameters parameters =mCamera.getParameters();
        if (parameters.getMaxNumDetectedFaces() > 0) {
            if (faceView != null) {
                faceView.clearFaces();
faceView.setVisibility(View.VISIBLE);
}
            mCamera.setFaceDetectionListener(new GoogleDetectListenerImpl(AutioTakePhotoActivity.this, mainHandler));
mCamera.startFaceDetection();
}
    }
    private class MainHandler extends Handler {

        @Override
public void handleMessage(final Message msg) {
            int what = msg.what;
            switch (what) {
                case 1:
                    startGoogleDetect();
Log.e("renlei110", "開啟人臉識別");
                    break;
                case 2:
                    runOnUiThread(new Runnable() {
                        @Override
public void run() {
                            Camera.Face[] faces = (Camera.Face[]) msg.obj;
faceView.setFaces(faces);
Log.e("renlei111", "收到人臉識別的資訊");
}
                    });
                    break;
}
            super.handleMessage(msg);
}
    }

    /**
     * 從列表中選取合適的解析度
     * 預設w:h = 4:3
     * <p>注意:這裡的w對應螢幕的height
     *            h對應螢幕的width<p/>
*/
private Camera.Size getProperSize(List<Camera.Size> pictureSizeList, float screenRatio) {
        Log.i("zjun", "screenRatio=" + screenRatio);
Camera.Size result = null;
        for (Camera.Size size : pictureSizeList) {
            float currentRatio = ((float) size.width) / size.height;
            if (currentRatio - screenRatio == 0) {
                result = size;
                break;
}
        }

        if (null == result) {
            for (Camera.Size size : pictureSizeList) {
                float curRatio = ((float) size.width) / size.height;
                if (curRatio == 4f / 3) {// 預設w:h = 4:3
result = size;
                    break;
}
            }
        }

        return result;
}

}
//上面類對應佈局
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
    <SurfaceView
android:id="@+id/surfaceview"
android:layout_width="match_parent"
android:visibility="visible"
android:layout_height="match_parent" />
    <TextView
android:id="@+id/take"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="15dp"
android:onClick="take"
android:text="拍照"
android:background="@android:color/holo_green_light"
android:layout_centerHorizontal="true"
android:layout_alignParentBottom="true"/>
    <TextView
android:id="@+id/turn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="15dp"
android:onClick="turn"
android:text="前置/後置"
android:background="@android:color/holo_green_light"
android:layout_centerHorizontal="true"/>
    <com.example.auto_take_picture.FaceView
android:id="@+id/face_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:visibility="gone"
></com.example.auto_take_picture.FaceView>
    <RelativeLayout
android:id="@+id/result"
android:layout_width="match_parent"
android:background="#222"
android:visibility="gone"
android:layout_height="match_parent">
        <ImageView
android:id="@+id/image"
android:layout_width="match_parent"
android:layout_alignParentTop="true"
android:layout_height="match_parent" />
        <TextView
android:id="@+id/finish"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="15dp"
android:onClick="finish"
android:text="完成"
android:background="@android:color/holo_green_light"
android:layout_alignParentBottom="true"/>
        <TextView
android:id="@+id/cancle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="15dp"
android:onClick="cancle"
android:text="取消"
android:layout_alignParentRight="true"
android:background="@android:color/holo_green_light"
android:layout_alignParentBottom="true"/>
    </RelativeLayout>
</RelativeLayout>
//FaceView 類用來繪製人臉識別的方框
package com.example.auto_take_picture;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.RectF;
import android.graphics.drawable.Drawable;
import android.hardware.Camera;
import android.util.AttributeSet;
import android.util.Log;
import android.widget.ImageView;
/**
 * Created by Administrator on 2017/5/9 0009.
 */
public class FaceView extends ImageView {
    private Context mContext;
    private Camera.Face[] mFaces;
    private Matrix mMatrix = new Matrix();
    private boolean mirror;
    private Paint mLinePaint;
    public FaceView(Context context, AttributeSet attrs) {
        super(context, attrs);
initPaint();
        this.mContext = context;
}

    public void setFaces(Camera.Face[] faces) {
        this.mFaces = faces;
invalidate();
}
    public void clearFaces(){
        mFaces = null;
invalidate();
}
    @Override
protected void onDraw(Canvas canvas) {
        if(mFaces == null || mFaces.length < 1){
            return;
}
        if (mFaces != null&&mFaces.length>=1) {
            canvas.translate(getWidth()/2,getHeight()/2);
canvas.rotate(-0);
mirror=(AutioTakePhotoActivity.cameraId== Camera.CameraInfo.CAMERA_FACING_FRONT) ;
Log.e("mFaces","mFaces"+mFaces.length);
            for (int i = 0; i < mFaces.length; i++) {
                Camera.Face face= mFaces[i];
                int width=face.rect.right-face.rect.left;
                int  needWidth=getWidth()*width/2000;
                if(!mirror){
                    int cx = -face.rect.centerY(); //因為之前對camera做了旋轉,所以這裡需要轉換一下坐   //後置攝像頭
int cy = face.rect.centerX();
RectF rectF = new RectF(getWidth()*cx/2000f-needWidth/2,getHeight()*cy/2000f-needWidth/2,g