美文网首页
Android 人脸检测 非人脸识别

Android 人脸检测 非人脸识别

作者: 最简单的实现 | 来源:发表于2017-07-19 13:33 被阅读0次

Note:文章以Google Android API提供的人脸探测技术进行讲解,并且使用的Camera class进行开发,而不是Google推荐的camera2,如果你开发中需要camera2可移步Detecting camera features with Camera2(需要梯子)
未引用OpenCV

人脸识别说明

人脸检测:检测并定位图片中的人脸,返回人脸框坐标。
人脸比对:比对两张脸为同一个人的可信度。
人脸关键点:定位返回人脸关键部位和四官坐标(眉、眼、鼻、口)
人脸属性:通过算法获取人脸属性,如:年龄、 性别、微笑程度、眼睛状态、人种等
说这么多是不是很激动,但别忘了题目是人脸检测,我们要讲的只有一个功能,人脸检测

API

Google官方给出的有两种方法:

  1. FaceDetector:通过传递Bitmap检测图中的人脸,同时返回眼睛部位,识别人数,未详细测试;
  2. Camera.FaceDetectionListener:通过打开摄像头(不是相机),实时获取人脸定位,最大5人。

讲解

FaceDetector传图识脸
 
    /**
     * Creates a FaceDetector, configured with the size of the images to
     * be analysed and the maximum number of faces that can be detected.
     * These parameters cannot be changed once the object is constructed.
     * Note that the width of the image must be even.
     * 
     * @param width  the width of the image
     * @param height the height of the image
     * @param maxFaces the maximum number of faces to identify
     *
     */
    public FaceDetector(int width, int height, int maxFaces){}

翻译:创建一个FaceDetector,配置要分析的图像的大小以及可以检测到的最大面孔数。一旦构建对象,这些参数就不能被更改。请注意,图像的宽度必须均匀。

用法:

public Bitmap detectionFace(Bitmap b) {        
            // 检测前必须转化为RGB_565格式。文末有详述连接
            Bitmap bitmap = b.copy(Bitmap.Config.RGB_565, true);
            b.recycle();
            // 设定最大可查的人脸数量
            int MAX_FACES = 5;
            FaceDetector faceDet = new FaceDetector(bitmap.getWidth(), bitmap.getHeight(), MAX_FACES);
            // 将人脸数据存储到faceArray 中
            FaceDetector.Face[] faceArray = new FaceDetector.Face[MAX_FACES];
            // 返回找到图片中人脸的数量,同时把返回的脸部位置信息放到faceArray中,过程耗时
            int findFaceCount = faceDet.findFaces(bitmap, faceArray);
            // 获取传回的脸部数组中的第一张脸的信息
            FaceDetector.Face face1 = faceArray[0];
            // 获取双眼的中心点,用一个PointF来接收其x、y坐标
            PointF point = new PointF();
            face1.getMidPoint(point);
            // 获取该部位为人脸的可信度,0~1
            float confidence = face1.confidence();
            // 获取双眼间距
            float eyesDistance = face1.eyesDistance();
            // 获取面部姿势
            // 传入X则获取到x方向上的角度,传入Y则获取到y方向上的角度,传入Z则获取到z方向上的角度
            float angle = face1.pose(FaceDetector.Face.EULER_X);

            // todo 在bitmap上绘制一个Rect框住脸,因为返回的是眼睛位置,所以还要做一些处理

            return bitmap;
}

以上就是通过传递一张Bitmap然后找出其中面部的具体方法
识别率:低

Camera.FaceDetectionListener实时检测脸部

通过调用摄像头,提供一个实时检测脸部的方案
流程:打开摄像头→给Camera类传递回调接口→从接口获取脸部位置信息

    /**
     * Callback interface for face detected in the preview frame.
     *
     * @deprecated We recommend using the new {@link android.hardware.camera2} API for new
     *             applications.
     */
    @Deprecated
    public interface FaceDetectionListener
    {
        /**
         * Notify the listener of the detected faces in the preview frame.
         *
         * @param faces The detected faces in a list
         * @param camera  The {@link Camera} service object
         */
        void onFaceDetection(Face[] faces, Camera camera);
    }

这是一个回调接口,供Camera使用。返回的数据包含脸部集合和摄像头对象。
用法:

  1. 在Activity的onCreate中提供一个SurfaceView给Camera显示图像
    private void initViews() {
        surfaceView = new SurfaceView(this);
        rectView = new DrawFacesView(this);
        addContentView(surfaceView, new FrameLayout.LayoutParams(FrameLayout.LayoutParams.MATCH_PARENT, FrameLayout.LayoutParams.MATCH_PARENT));
        addContentView(rectView, new FrameLayout.LayoutParams(FrameLayout.LayoutParams.MATCH_PARENT, FrameLayout.LayoutParams.MATCH_PARENT));
    }

这是Activity布局

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent" />
  1. 在onCreate()中打开相机,设置监听
    /**
     * 把摄像头的图像显示到SurfaceView
     */
    private void openSurfaceView() {
        mHolder = surfaceView.getHolder();
        mHolder.addCallback(new SurfaceHolder.Callback() {
            @Override
            public void surfaceCreated(SurfaceHolder holder) {
                if (mCamera == null) {
                    mCamera = Camera.open();
                    try {
                        // 设置脸部检测监听
                        mCamera.setFaceDetectionListener(new FaceDetectorListener());
                        mCamera.setPreviewDisplay(holder);
                        // 开始脸部检测
                        startFaceDetection();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }

            @Override
            public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
                if (mHolder.getSurface() == null) {
                    // preview surface does not exist
                    Log.e(TAG, "mHolder.getSurface() == null");
                    return;
                }

                try {
                    mCamera.stopPreview();

                } catch (Exception e) {
                    // ignore: tried to stop a non-existent preview
                    Log.e(TAG, "Error stopping camera preview: " + e.getMessage());
                }

                try {
                    mCamera.setPreviewDisplay(mHolder);
                    int measuredWidth = surfaceView.getMeasuredWidth();
                    int measuredHeight = surfaceView.getMeasuredHeight();
                    setCameraParms(mCamera, measuredWidth, measuredHeight);
                    mCamera.startPreview();

                    startFaceDetection(); // re-start face detection feature

                } catch (Exception e) {
                    // ignore: tried to stop a non-existent preview
                    Log.d(TAG, "Error starting camera preview: " + e.getMessage());
                }
            }

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

    /**
     * 在摄像头启动前设置参数
     *
     * @param camera
     * @param width
     * @param height
     */
    private void setCameraParms(Camera camera, int width, int height) {
        // 获取摄像头支持的pictureSize列表
        Camera.Parameters parameters = camera.getParameters();
        // /**/注释的地方非必须,参考来源  [Android 手把手带你玩转自定义相机](http://blog.csdn.net/qq_17250009/article/details/52795530)
        /*List<Camera.Size> pictureSizeList = parameters.getSupportedPictureSizes();
        // 从列表中选择合适的分辨率
        Camera.Size pictureSize = getProperSize(pictureSizeList, (float) height / width);
        if (null == pictureSize) {
            pictureSize = parameters.getPictureSize();
        }
        // 根据选出的PictureSize重新设置SurfaceView大小
        float w = pictureSize.width;
        float h = pictureSize.height;
        parameters.setPictureSize(pictureSize.width, pictureSize.height);

        surfaceView.setLayoutParams(new FrameLayout.LayoutParams((int) (height * (h / w)), height));

        // 获取摄像头支持的PreviewSize列表
        List<Camera.Size> previewSizeList = parameters.getSupportedPreviewSizes();
        Camera.Size preSize = getProperSize(previewSizeList, (float) height / width);
        if (null != preSize) {
            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);
        }
        camera.cancelAutoFocus();
        // 摄像头图像旋转90度,此处不严谨
        camera.setDisplayOrientation(90);// 可注释该行代码看看效果
        camera.setParameters(parameters);
    }

    /**
    * 启动脸部检测,如果getMaxNumDetectedFaces()!=0说明不支持脸部检测
    */
    public void startFaceDetection() {
        // Try starting Face Detection
        Camera.Parameters params = mCamera.getParameters();
        // start face detection only *after* preview has started
        if (params.getMaxNumDetectedFaces() > 0) {
            // mCamera supports face detection, so can start it:
            mCamera.startFaceDetection();
        } else {
            Log.e("tag", "【FaceDetectorActivity】类的方法:【startFaceDetection】: " + "不支持");
        }
    }
  1. 脸部回调接口
    /**
     * 脸部检测接口
     */
    private class FaceDetectorListener implements Camera.FaceDetectionListener {
        @Override
        public void onFaceDetection(Camera.Face[] faces, Camera camera) {
            if (faces.length > 0) {
                Camera.Face face = faces[0];
                Rect rect = face.rect;
                Log.d("FaceDetection", "可信度:" + face.score + "face detected: " + faces.length +
                        " Face 1 Location X: " + rect.centerX() +
                        "Y: " + rect.centerY() + "   " + rect.left + " " + rect.top + " " + rect.right + " " + rect.bottom);
                Log.e("tag", "【FaceDetectorListener】类的方法:【onFaceDetection】: ");
                Matrix matrix = updateFaceRect();
                facesView.updateFaces(matrix, faces);
            } else {
                // 只会执行一次
                Log.e("tag", "【FaceDetectorListener】类的方法:【onFaceDetection】: " + "没有脸部");
                facesView.removeRect();
            }
        }
    }
    /**
     * 因为对摄像头进行了旋转,所以同时也旋转画板矩阵
     * 详细请查看{@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(surfaceView.getWidth() / 2000f, surfaceView.getHeight() / 2000f);
        matrix.postTranslate(surfaceView.getWidth() / 2f, surfaceView.getHeight() / 2f);
        return matrix;
    }
  1. 现在插播一条消息:
    updateFaceRect()解释:在脸部检测时,获得的Rect并不是View的坐标,而是这样的一个坐标系,中心为原点,所以我们需要转换一下


    camera-area-coordinates.png
  2. 绘制脸部方框的View

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.PorterDuff;
import android.hardware.Camera;
import android.util.AttributeSet;
import android.view.View;
public class DrawFacesView extends View {

    private Matrix matrix;
    private Paint paint;
    private Camera.Face[] faces;
    private boolean isClear;

    public DrawFacesView(Context context) {
        this(context, null);
    }

    public DrawFacesView(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public DrawFacesView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        init();
    }

    private void init() {
        paint = new Paint();
        paint.setColor(Color.GREEN);
        paint.setAntiAlias(true);
        paint.setStyle(Paint.Style.STROKE);
        faces = new Camera.Face[]{};
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        canvas.setMatrix(matrix);
//        canvas.drawRect(rect, paint);
        for (Camera.Face face : faces) {
            if (face == null) break;
            canvas.drawRect(face.rect, paint);
            if (face.leftEye != null)
                canvas.drawPoint(face.leftEye.x, face.leftEye.y, paint);
            if (face.rightEye != null)
                canvas.drawPoint(face.rightEye.x, face.rightEye.y, paint);
            if (face.mouth != null)
                canvas.drawPoint(face.mouth.x, face.mouth.y, paint);
            // 因为旋转了画布矩阵,所以字体也跟着旋转
//            canvas.drawText(String.valueOf("id:" + face.id + "\n置信度:" + face.score), face.rect.left, face.rect.bottom + 10, paint);
        }
        if (isClear) {
            canvas.drawColor(Color.WHITE, PorterDuff.Mode.CLEAR);
            isClear = false;
        }
    }
    /**
     * 绘制脸部方框
     *
     * @param matrix 旋转画布的矩阵
     * @param faces 脸部信息数组
     */
    public void updateFaces(Matrix matrix, Camera.Face[] faces) {
        this.matrix = matrix;
        this.faces = faces;
        invalidate();
    }

    /**
     * 清除已经画上去的框
     */
    public void removeRect() {
        isClear = true;
        invalidate();
    }
}

权限

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

效果:

  1. 传入Bitmap识别
检测Bitmap2.jpg
  1. 摄像头实时检测
动态识别.png 动态识别多人.png

参考

Google Android Camera API
Android 手把手带你玩转自定义相机

体验

体验APK
源码Github

相关文章

  • Android实现人脸识别(人脸检测)初识

    title: Android实现人脸识别(人脸检测)初识categories: Androidtags: 人脸识别...

  • 安卓OpenCV开发(二)人脸检测

    重点是人脸检测,检测,检测。 就是把人脸检测出来,不是识别,不是识别,不是识别。识别的意思,就是检测到人脸,并且通...

  • 较为成熟的安卓项目--人面识别,手势识别向

    一、 人脸识别 1. 目标检测&目标追踪&人脸检测&人脸识别 GitHub:https://github.com/...

  • MTCNN

      人脸任务总体上分为:人脸检测、人脸关键点检测、人脸判别、人脸识别、人脸聚类等。  作者认为人脸检测和人脸关键点...

  • iOS ---人脸检测

    检测图像中的人脸 Core Image可以分析并找到图像中的人脸。它执行的是人脸检测,而不是识别。人脸检测是识别包...

  • 脸部服务参考

    Face++ 支持服务 人脸识别人脸检测人脸比对人脸搜索人脸关键点人脸属性beta情绪识别beta颜值评分beta...

  • 人脸识别

    图片人脸检测——OpenCV版(二) 图片人脸检测——Dlib版(四) 人脸识别之人脸对齐(一)--定义及作用

  • 人脸识别支付在城市公交领域应用存在的问题

    人脸识别技术的主要流程包括人脸图像采集、人脸检测、预处理、人脸特征点提取和人脸匹配/识别等几个环节,基于此,人脸...

  • Face数据集下载地址

    人脸公开数据集 人脸识别 人脸检测 人脸表情 人脸年龄 人脸性别 其他 1.PubFig: Public Figu...

  • 人脸活体检测

    人脸检测 关键点检测 人脸对齐(仿射变换) 活体检测/身份识别

网友评论

      本文标题:Android 人脸检测 非人脸识别

      本文链接:https://www.haomeiwen.com/subject/hrflkxtx.html