人脸识别google有自带api,Androoid 原生的人脸识别是利用相机的摄像功能和android.media.FaceDetector这个类来实现的
1、android.hardware.camera
1.1 简介
Camera 类用于设置图像捕获设置、开始/停止预览、抓拍图片和检索帧以进行视频编码
Camera的源码
1.2 人脸识别步骤:
1. 初始化相机并设置相机参数;
2. 设置预览监听setPreviewDisplay();
3. 开始预览startPreview();
4. 实现预览的接口onPreviewFrame并处理每一帧的数据转成Bitmap;
5. 通过 faceDetector.findFaces()发现人脸,注意:faceDetector只能识别Bitmap.Config.RGB_565的Bitmap
1.3 示例类
工具类CameraUtils
package com.zw.camera2test.camera;import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.ImageFormat;
import android.graphics.Matrix;
import android.graphics.PixelFormat;
import android.graphics.Rect;
import android.graphics.YuvImage;
import android.hardware.Camera;
import android.media.MediaRecorder;
import android.os.SystemClock;
import android.util.Log;
import android.view.SurfaceHolder;
import android.view.SurfaceView;import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.List;public class CameraUtils implements Camera.PreviewCallback {private static final String TAG = "Camera2Utils";private SurfaceView mSurfaceView;private FaceRectView mFaceRectView;private static CameraUtils mCameraUtils;private SurfaceHolder mSurfaceViewHolder;private int cameraPosition = 1; //0 后置 1 前置private Camera mCamera;private int CAMERA_WIDHT = 640;private int CAMERA_HEIGHT = 480;// private int CAMERA_WIDHT = 320;
// private int CAMERA_HEIGHT = 240;private boolean detectFace = false;private MediaRecorder mediaRecorder;/***录制视频的videoSize*/private int height = 480, width = 640;/***保存的photo的height ,width*/private int heightPhoto = 480, widthPhoto = 640;private CameraErrorCallback cameraErrorCallback;public static CameraUtils getInstance() {if (mCameraUtils == null) {synchronized (CameraUtils.class) {if (mCameraUtils == null) {mCameraUtils = new CameraUtils();}}}return mCameraUtils;}public void initCamera(SurfaceView surfaceView, FaceRectView faceRectView) {this.mSurfaceView = surfaceView;this.mFaceRectView = faceRectView;mSurfaceViewHolder = mSurfaceView.getHolder();mSurfaceViewHolder.setFormat(PixelFormat.OPAQUE);surfaceHolderCB = new SurfaceHolderCB();}SurfaceHolderCB surfaceHolderCB;public void startCamera() {if (mSurfaceViewHolder != null) {mSurfaceViewHolder.addCallback(surfaceHolderCB);}}public void stopCamera() {stopPreview();}//打开摄像头失败public static final int ERROR_CAMERA_OPEN = 100001;public void startPreview() {Log.i(TAG, "--11111---2-----" + mCamera);if (mCamera != null) {stopCamera();}if (mCamera == null) {Log.i(TAG, "startPreview: " + cameraPosition);try {mCamera = Camera.open(cameraPosition);Log.i(TAG, "startPreview:------ " + (Camera.getNumberOfCameras() - 1));} catch (RuntimeException e) {Log.i(TAG, "startPreview: 方法有问题");if (cameraErrorCallback != null) {cameraErrorCallback.onCameraError(ERROR_CAMERA_OPEN);}return;}Camera.Parameters parameters = setParameters(mCamera, cameraPosition);if (parameters.getMaxNumDetectedFaces() > 0) {Log.e("tag", "【FaceDetectorActivity】类的方法:【startFaceDetection】: " + parameters.getMaxNumDetectedFaces());} else {Log.e("tag", "【FaceDetectorActivity】类的方法:【startFaceDetection】: " + "不支持");}mCamera.setDisplayOrientation(0);mCamera.setErrorCallback(cameraErrorCallback);try {if (cameraPosition == 0) {parameters.setFocusMode(Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE); //后置必须聚焦设置}mCamera.setPreviewDisplay(mSurfaceViewHolder);mCamera.setParameters(parameters);if (mCamera != null) {mCamera.setPreviewCallback(this);}} catch (IOException e) {Log.i(TAG, "startPreview: " + e.getMessage());Camera.Parameters parameters1 = mCamera.getParameters();// 得到摄像头的参数mCamera.setParameters(parameters1);}mCamera.startPreview();mCamera.cancelAutoFocus();//聚焦detectFace = true;}}/*** 脸部检测接口*/private class FaceDetectorListener implements Camera.FaceDetectionListener {@Overridepublic void onFaceDetection(Camera.Face[] faces, Camera camera) {if (faces.length > 0) {int score = faces[0].score;Log.i(TAG, "onFaceDetection: score " + score);detectFace = true;mFaceRectView.drawFaceRects(faces, mSurfaceView, cameraPosition);} else {Log.i(TAG, "onFaceDetection: 没有人脸 ");detectFace = false;mFaceRectView.clearRect();}}}public void setDetectFace(boolean detectFace) {this.detectFace = detectFace;}/*** 切换前后相机*/public void changeCamera(CurrentCameraPositionInterface cameraPositionInterface) {int numberOfCameras = Camera.getNumberOfCameras();Camera.CameraInfo cameraInfo = new Camera.CameraInfo();if (numberOfCameras >= 2) {if (cameraPosition == 0) { //现在为后置,变成为前置Camera.getCameraInfo(1, cameraInfo);if (cameraInfo.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) { //CAMERA_FACING_FRONT 前置方位 CAMERA_FACING_BACK 后置方位if (mCamera != null) {stopPreview();}cameraPosition = 1;startPreview();}} else if (cameraPosition == 1) {//前置更改为后置相机Camera.getCameraInfo(0, cameraInfo);if (cameraInfo.facing == Camera.CameraInfo.CAMERA_FACING_BACK) {if (mCamera != null) {stopPreview();}cameraPosition = 0;startPreview();}}}cameraPositionInterface.cameraPosition(cameraPosition);}private class SurfaceHolderCB implements SurfaceHolder.Callback {@Overridepublic void surfaceCreated(SurfaceHolder holder) {Log.i(TAG, "--11111--------" + holder);startPreview();}@Overridepublic void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {Log.i(TAG, "--2222222--------" + holder);}@Overridepublic void surfaceDestroyed(SurfaceHolder holder) {holder.removeCallback(this);stopPreview();}}@Overridepublic void onPreviewFrame(byte[] data, Camera camera) {if (detectFace) {Bitmap bmp = byte2bitmap(data, camera);mCurrentDetectFaceInterface.detectFace(bmp);mCurrentDetectFaceInterface.detectFace2(bmp);}}private Bitmap byte2bitmap(byte[] bytes, Camera camera) {Bitmap bitmap = null;Camera.Size size = camera.getParameters().getPreviewSize(); // 获取预览大小final int w = size.width; // 宽度final int h = size.height;final YuvImage image = new YuvImage(bytes, ImageFormat.NV21, w, h,null);ByteArrayOutputStream os = new ByteArrayOutputStream(bytes.length);if (!image.compressToJpeg(new Rect(0, 0, w, h), 100, os)) {return null;}byte[] tmp = os.toByteArray();bitmap = BitmapFactory.decodeByteArray(tmp, 0, tmp.length);Matrix matrix = new Matrix();
// matrix.setRotate(-90);Log.i(TAG, "byte2bitmap: " + bitmap.getWidth() + "---" + bitmap.getHeight());bitmap = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, true);return bitmap;}private void stopPreview() {if (mCamera != null) {try {mCamera.setPreviewCallback(null);mCamera.setPreviewDisplay(null);mCamera.stopPreview();mCamera.release();mCamera = null;} catch (Exception e) {e.printStackTrace();}}detectFace = false;mFaceRectView.clearRect();}/*** 开启闪光灯*/public void turnFlash() {try {if (mCamera == null || mCamera.getParameters() == null|| mCamera.getParameters().getSupportedFlashModes() == null) {return;}Camera.Parameters parameters = mCamera.getParameters();String mode = parameters.getFlashMode();if (Camera.Parameters.FLASH_MODE_OFF.equals(mode)) {parameters.setFlashMode(Camera.Parameters.FLASH_MODE_TORCH);} else if (Camera.Parameters.FLASH_MODE_TORCH.equals(mode)) {parameters.setFlashMode(Camera.Parameters.FLASH_MODE_OFF);}mCamera.setParameters(parameters);} catch (Exception e) {e.printStackTrace();}}/*** 调节预览的焦距** @param maxValue* @param currentValue*/public void changZoom(int maxValue, int currentValue) {if (mCamera != null) {Camera.Parameters parameters = mCamera.getParameters();int maxZoom = parameters.getMaxZoom();Log.i(TAG, "changZoom: " + maxZoom);float setZoom = ((float) maxZoom / maxValue) * currentValue;parameters.setZoom((int) setZoom);mCamera.setParameters(parameters);}}/*** @param path 保存的路径* @param name 录像视频名称(包含后缀)*/public void startRecord(String path, String name) {if (mCamera == null)return;//解锁摄像头并将其分配给MediaRecordermCamera.unlock();mediaRecorder = new MediaRecorder();mediaRecorder.setCamera(mCamera);//指定用于录制的输入源mediaRecorder.setAudioSource(MediaRecorder.AudioSource.CAMCORDER);mediaRecorder.setVideoSource(MediaRecorder.VideoSource.CAMERA);//mediaRecorder.setProfile(CamcorderProfile.get(CamcorderProfile.QUALITY_LOW));//设置配置文件,或者定义输出格式,音频视频编码器,帧速以及输出尺寸mediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4);mediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.DEFAULT);// 设置录制的视频编码h263 h264mediaRecorder.setVideoEncoder(MediaRecorder.VideoEncoder.H264);// 设置录制的视频帧率。必须放在设置编码和格式的后面,否则报错mediaRecorder.setVideoEncodingBitRate(700 * 1024);
// 设置视频录制的分辨率。必须放在设置编码和格式的后面,否则报错mediaRecorder.setVideoSize(width, height);
// mediaRecorder.setVideoFrameRate(24); //容易报错 还有上面的setVideoSize 都是需要底层支持File file = new File(path);if (!file.exists()) {file.mkdirs();}//指定一个输出文件mediaRecorder.setOutputFile(path + File.separator + name);File file1 = new File(path + File.separator + name);if (file1.exists()) {file1.delete();}//预览视频流,在指定了录制源和输出文件后,在prepare前设置mediaRecorder.setPreviewDisplay(mSurfaceView.getHolder().getSurface());/***不设置时,录制的视频总是倒着,翻屏导致视频上下翻滚*/if (cameraPosition == 1) {//前置相机mediaRecorder.setOrientationHint(180);} else if (cameraPosition == 0) {mediaRecorder.setOrientationHint(0);}try {//准备录制mediaRecorder.prepare();mediaRecorder.start();} catch (IOException e) {e.printStackTrace();}}/*** 停止录制*/public void stopRecord() {mediaRecorder.release();mCamera.release();mediaRecorder = null;SystemClock.sleep(200);if (mCamera != null) {mCamera = Camera.open();mediaRecorder = new MediaRecorder();doChange(mSurfaceView.getHolder());}}/*** 拍照使用** @param photoPath* @param photoName*/public void takePicture(String photoPath, String photoName, TakePictureSuccess mTakePictureSuccess) {File file = new File(photoPath);if (!file.exists()) {file.mkdir();}Camera.ShutterCallback shutter = new Camera.ShutterCallback() {@Overridepublic void onShutter() {}};Camera.Parameters parameters = mCamera.getParameters();parameters.setPictureFormat(ImageFormat.JPEG);//图片格式 必设置 否则无法获取图片信息parameters.set("jpeg-quality", 90);//设置图片的质量mCamera.setParameters(parameters);mCamera.takePicture(shutter, null, new PictureCallBack(photoPath, photoName, mTakePictureSuccess));
// mCamera.takePicture(null, null, new PictureCallBack(photoPath, photoName));}/*** 拍照功能*/private class PictureCallBack implements Camera.PictureCallback {/*** 照片保存的路径和名称*/private String path;private String name;private TakePictureSuccess mTakePictureSuccess;public PictureCallBack(String path, String name, TakePictureSuccess mTakePictureSuccess) {this.path = path;this.name = name;this.mTakePictureSuccess = mTakePictureSuccess;}@Overridepublic void onPictureTaken(byte[] bytes, Camera camera) {File file = new File(path, name);if (file.exists()) {file.delete();}FileOutputStream fos = null;try {fos = new FileOutputStream(file);try {fos.write(bytes);} catch (IOException e) {e.printStackTrace();}} catch (FileNotFoundException e) {e.printStackTrace();}Bitmap bitmap = BitmapFactory.decodeByteArray(bytes, 0, bytes.length);Log.i(TAG, "onPictureTaken: ----" + cameraPosition);Matrix matrix = new Matrix();if (cameraPosition == 0) {matrix.postRotate(90);} else {matrix.postRotate(270);
// matrix.postRotate(180);matrix.postTranslate(-1, 0);}bitmap = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, true);mTakePictureSuccess.takePictureSuccess(bitmap);//拍照结束 继续预览camera.startPreview();}}public interface TakePictureSuccess {void takePictureSuccess(Bitmap bitmap);}/*** 切换时,重新开启** @param holder*/private void doChange(SurfaceHolder holder) {try {mCamera.setPreviewDisplay(holder);mCamera.setDisplayOrientation(90);mCamera.startPreview();} catch (IOException e) {e.printStackTrace();}}/*** 设置相机参数** @param camera* @param cameraPosition* @return*/public Camera.Parameters setParameters(Camera camera, int cameraPosition) {Camera.Parameters parameters = null;if (camera != null) {parameters = camera.getParameters();Log.i(TAG, "setParameters: ----" + parameters.getMaxNumDetectedFaces());if (cameraPosition == 0) {parameters.setFocusMode(Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE);parameters.setPreviewFrameRate(25);}parameters.setPictureFormat(ImageFormat.NV21);//手机支持的预览尺寸集合List<Camera.Size> sizeList = parameters.getSupportedPreviewSizes();Log.i(TAG, "setParameters:----111----- " + CAMERA_WIDHT);Log.i(TAG, "setParameters:----支持预览----- " + sizeList.size());CAMERA_WIDHT = 640;CAMERA_HEIGHT = 480;/* if (sizeList.size() > 1) {Iterator<Camera.Size> itor = sizeList.iterator();while (itor.hasNext()) {Camera.Size cur = itor.next();Log.i(TAG, "setParameters:----size----- "+cur.width +"-----"+cur.height) ;if (cur.width >= CAMERA_WIDHT&& cur.height >= CAMERA_HEIGHT) {CAMERA_WIDHT = cur.width;CAMERA_HEIGHT = cur.height;break;}}}*///每秒3帧 每秒从摄像头里面获得3个画面
// parameters.setPreviewFrameRate(3);//设置拍出来的屏幕大小parameters.setPictureSize(CAMERA_WIDHT, CAMERA_HEIGHT);//获得摄像区域的大小parameters.setPreviewSize(CAMERA_WIDHT, CAMERA_HEIGHT);//对焦模式设置List<String> supportedFocusModes = parameters.getSupportedFocusModes();if (supportedFocusModes != null && supportedFocusModes.size() > 0) {if (supportedFocusModes.contains(Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE)) {parameters.setFocusMode(Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE);} else if (supportedFocusModes.contains(Camera.Parameters.FOCUS_MODE_CONTINUOUS_VIDEO)) {parameters.setFocusMode(Camera.Parameters.FOCUS_MODE_CONTINUOUS_VIDEO);} else if (supportedFocusModes.contains(Camera.Parameters.FOCUS_MODE_AUTO)) {parameters.setFocusMode(Camera.Parameters.FOCUS_MODE_AUTO);}}}return parameters;}/**** 获取SupportedVideoSizes 控制输出视频width在300到600之间(尽可能小)* 获取PictureSize的大小(控制在w:1000-2000之间)*/public void getVideoSize() {if (mCamera == null)return;Camera.Parameters parameters = mCamera.getParameters();List<Camera.Size> videoSize = parameters.getSupportedVideoSizes();for (int i = 0; i < videoSize.size(); i++) {int width1 = videoSize.get(i).width;int height1 = videoSize.get(i).height;if (width1 >= 300 && width1 <= 600) {if (height1 >= 200 && height1 <= 600) {width = width1;height = height1;}}
// Log.d(TAG, "getVideoSize:----w:-- " + videoSize.get(i).width + "---h:--" + videoSize.get(i).height);Log.d(TAG, "width " + width + "---height" + height);}List<Camera.Size> photoSize = parameters.getSupportedPictureSizes();for (int i = 0; i < photoSize.size(); i++) {int width1 = photoSize.get(i).width;int height1 = photoSize.get(i).height;if (width1 >= 1000 && width1 <= 2000) {if (height1 >= 600 && height1 <= 2000) {widthPhoto = width1;heightPhoto = height1;}}}Log.i(TAG, "getVideoSize: " + widthPhoto + "----" + heightPhoto);}public interface CurrentCameraPositionInterface {void cameraPosition(int cameraPosition);}public void setCurrentDetectFaceInterface(CurrentDetectFaceInterface currentDetectFaceInterface) {mCurrentDetectFaceInterface = currentDetectFaceInterface;}private CurrentDetectFaceInterface mCurrentDetectFaceInterface;public interface CurrentDetectFaceInterface {void detectFace(Bitmap bitmap);void detectFace2(Bitmap bitmap);}public void setCameraErrorCallback(CameraErrorCallback cameraErrorCallback) {this.cameraErrorCallback = cameraErrorCallback;}
}
发现人脸的方法
public Bitmap getCutBitmap(Bitmap bitmap) {Bitmap cutBitmap = null;//由于Android内存有限,图片太大的话,会出现无法加载图片的异常,图片的格式必须为Bitmap RGB565格式Bitmap bitmapDetect = bitmap.copy(Bitmap.Config.RGB_565, true);Log.i(TAG, "getCutBitmap:---宽度-- "+bitmapDetect.getWidth());Log.i(TAG, "getCutBitmap:---高度-- "+bitmapDetect.getHeight());// 设置你想检测的数量,数值越大错误率越高,所以需要置信度来判断,但有时候置信度也会出问题FaceDetector faceDetector = new FaceDetector(bitmapDetect.getWidth(), bitmapDetect.getHeight(), 1);// 返回找到图片中人脸的数量,同时把返回的脸部位置信息放到faceArray中,过程耗时,图片越大耗时越久int face = faceDetector.findFaces(bitmapDetect, faces);Log.i(TAG, "getCutBitmap:--------- "+face);
// if (faces[0] != null) {//检测到人脸if (face > 0) {//检测到人脸// 获取传回的第一张脸信息FaceDetector.Face face1 = faces[0];// 获取该部位为人脸的可信度,0~1float confidence = face1.confidence();Log.i(TAG, "------人脸可信度---- "+confidence);// 获取双眼的间距float eyesDistance = face1.eyesDistance();Log.i(TAG, "------人脸双眼的间距---- "+eyesDistance);// 传入X则获取到x方向上的角度,传入Y则获取到y方向上的角度,传入Z则获取到z方向上的角度float angle = face1.pose(FaceDetector.Face.EULER_X);if (confidence < 0.5 || eyesDistance<0.3){return null;}cutBitmap = bitmap;Log.i(TAG, "getCutBitmap: ");}bitmapDetect.recycle();
// bitmap.recycle();return cutBitmap;}
2 . Demo地址
下载地址