基于opencv实现人脸检测

原文地址:

https://blog.csdn.net/qq_34902522/article/details/82464516


基于opencv实现人脸检测

opencv简述

opencv是一个开源的计算机视觉库,它有着C++,Python,Java等接口,支持Windows,Linux,Mac OS,IOS 和 Android平台.Opencv 是使用C/C++所写的,可以利用多核处理.通过OpenCL启用,它可以利用底层异构计算平台的硬件加速。关于Opencv的详细介绍可以去其官网查看.Opencv 官网

注意

1.如果对opencv还没有接触过的,可以先参考参考这篇文章,了解如何在Android项目接入opencv.android 接入opencv的3种方式 .建议结合Opencv 的Tutorials看,效果更加.
2.这边接入使用的opencv library是利用github上面opencv和opencv-contrib库里的源码来编译的适用于Android平台的库.这个库的地址在opencv+opencv-contrib-lib4Android 编的这个库是3.4.2版本,Android的各个平台都有.还是很全的.
如果想自己编译的话,可以参考我之前的文章编译opencv+opencv-contrib 遇到的坑 .
当然你也可以直接从opencv的官网下载人家已经编译好的库.但是从其官网下载的库内容不全,比如Tracker这一块的内容,就没有.(PS:后面会更一篇利用OpenCV实现物体追踪功能的文章。就是需要tracker)这是在opencv-contrib库里的.
那什么是opencv-contrib库?opencv-contrib库是一个额外库,里面包含的是一些较新,高级些的功能模块.

目标

利用Opencv的分类器CascadeClassifier,对从Camera读取的yuv数据进行实时检测,并且把结果显示在屏幕上.

CascadeClassifier介绍#####

CascadeClassifier不仅仅可以检测人脸,也可以检测眼睛,身体,嘴巴等.通过加载一个想要检测的.xml的分类器文件就可以.

开撸####

要实现在相机预览画面的实时人脸检测,那么关于Android Camera的部分肯定要先弄起来.这边为了方便演示,简单封装了一个CameraModule库.来实现相机预览,Camera 数据回调.

            CameraApi.getInstance().setCameraId(CameraApi.CAMERA_INDEX_BACK);
            CameraApi.getInstance().initCamera(this, this);
            CameraApi.getInstance().setPreviewSize(new Size(previewWidth, previewHeight));
            CameraApi.getInstance().setFps(30).configCamera();
            CameraApi.getInstance().startPreview(holder);

在成功获取Camera 的preview数据之后,我们开始处理opencv部分的逻辑.
1.首先我们需要创建CascadeClassifier.
在Activity的onResume回调中,先加载Opencv的library.

@Override
    protected void onResume() {
        super.onResume();
        if (!OpenCVLoader.initDebug()) {
            Log.d(TAG, "Internal OpenCV library not found. Using OpenCV Manager for initialization");
            OpenCVLoader.initAsync(OpenCVLoader.OPENCV_VERSION_3_4_0, this, mLoaderCallback);
        } else {
            Log.d(TAG, "OpenCV library found inside package. Using it!");
            mLoaderCallback.onManagerConnected(LoaderCallbackInterface.SUCCESS);
        }
    }

在加载成功的回调中,创建需要的属于opencv的对象.代码如下:

private LoaderCallbackInterface mLoaderCallback = new LoaderCallbackInterface() {
        @Override
        public void onManagerConnected(int status) {
            if (status == LoaderCallbackInterface.SUCCESS) {
                init();
                isLoadSuccess = true;
                try {
                    // load cascade file from application resources
                    InputStream is = getResources().openRawResource(R.raw.lbpcascade_frontalface);
                    File cascadeDir = getDir("cascade", Context.MODE_PRIVATE);
                    mCascadeFile = new File(cascadeDir, "lbpcascade_frontalface.xml");
                    FileOutputStream os = new FileOutputStream(mCascadeFile);

                    byte[] buffer = new byte[4096];
                    int bytesRead;
                    while ((bytesRead = is.read(buffer)) != -1) {
                        os.write(buffer, 0, bytesRead);
                    }
                    is.close();
                    os.close();

                    mFaceCascade = new CascadeClassifier(mCascadeFile.getAbsolutePath());
                    if (mFaceCascade.empty()) {
                        Log.e(TAG, "Failed to load cascade classifier");
                        mFaceCascade = null;
                    } else {
                        Log.e(TAG, "Loaded cascade classifier from " + mCascadeFile.getAbsolutePath());
                    }


                    cascadeDir.delete();

                } catch (IOException e) {
                    e.printStackTrace();
                    Log.e(TAG, "Failed to load cascade. Exception thrown: " + e);
                }


            }
        }

        @Override
        public void onPackageInstall(int operation, InstallCallbackInterface callback) {

        }
    };
private void init() {
        mSrcMat = new Mat(previewHeight, previewWidth, CvType.CV_8UC1);
        mDesMat = new Mat(previewHeight, previewWidth, CvType.CV_8UC1);
        matOfRect = new MatOfRect();
        initQueue();
    }

我们分析一下上面的代码,首先我们创建了一些必须的对象,比如Mat对象.
Mat - The Basic Image Container 在opencv里,对图像的处理,大都是先把图像数据转化成Mat对象,Mat对象就像是一个容器,对图像的处理就是对Mat的处理.
然后,我们从raw文件夹里读取了opencv训练好的,用于检测人脸的分类器文件lbpcascade_frontalface.xml.xml分类器文件,可以从opencv下载的包里面找到.你会发现,里面有两种类型的分类器文件,一种是haar的,一种是lbp的.关于这两种的不同.可以参考haar-vs-lbp .
这里我们选择的是lbp的.分类器文件获取到后,我们通过分类器文件,创建了我们想要的CascadeClassifier.

2.对相机预览数据的处理
这边设置的预览的FPS是30,因为人脸检测是耗时操作,为了不影响预览的画面流畅度.这边采用了,子线程+队列的处理方式.通过创建两个队列,来保证对相机数据的管理.

@Override
    public void onPreviewFrameCallback(byte[] data, Camera camera) {
        mCamera.addCallbackBuffer(data);
        if (isStart) {
            CameraRawData rawData = mFreeQueue.poll();
            if (rawData != null) {
                rawData.setRawData(data);
                rawData.setTimestamp(System.currentTimeMillis());
                mFrameQueue.offer(rawData);
            }
        }

    }

3.从队列获取数据,进行检测

/**
     * face detect thread
     */
    private class DetectThread extends Thread {
        DetectThread(String name) {
            super(name);
        }

        @Override
        public void run() {
            super.run();
            while (isStart && isLoadSuccess) {
                synchronized (mLock) {
                    try {
                        mCameraRawData = mFrameQueue.poll(20, TimeUnit.MILLISECONDS);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }

                    if (mCameraRawData == null) {
                        continue;
                    }
                    frameDatas = mCameraRawData.getRawData();
                    mSrcMat.put(0, 0, frameDatas);
                    Imgproc.cvtColor(mSrcMat, mDesMat, Imgproc.COLOR_YUV2GRAY_420);
                    mFaceCascade.detectMultiScale(mDesMat, matOfRect, 1.1, 5
                            , 2, mMinSize, mMaxSize);
                    if (matOfRect.toArray().length != 0) {
                        Rect rect = getBiggestFace(matOfRect.toArray());
                        mResultView.showFace(rect);
                    } else {
                        mResultView.clear();
                    }
                    mFreeQueue.offer(mCameraRawData);
                    mCamera.addCallbackBuffer(frameDatas);
                }

            }
        }
    }

如上面的代码所示,我们通过创建子线程,在里面进行face detect处理.
首先我们从队列中获取Camera data.接着为mSrcMat对象赋值.接着利用Opencv 的convert方法,对源数据进行灰度化处理.

 Imgproc.cvtColor(mSrcMat, mDesMat, Imgproc.COLOR_YUV2GRAY_420);

这里主要看下第三个参数.因为我这边设置的image format 是NV21 ,所用了这个YUV420 to Gray的flag.我们进去可以看到:


这里写图片描述

我们发现,只要是YUV420的无论是P还是SP都是用的同一个Flag.还挺省事,省的格式转化了O(∩_∩)O.

我们接着看这行代码:

mFaceCascade.detectMultiScale(mDesMat, matOfRect, 1.1, 5
                            , 2, mMinSize, mMaxSize);

这就是检测的核心代码,这里说一下各个参数所代表的含义.
Parameters
image Matrix of the type CV_8U containing an image where objects are detected.
objects Vector of rectangles where each rectangle contains the detected object, the rectangles may be partially outside the original image.
scaleFactor Parameter specifying how much the image size is reduced at each image scale.
minNeighbors Parameter specifying how many neighbors each candidate rectangle should have to retain it.
flags Parameter with the same meaning for an old cascade as in the function cvHaarDetectObjects. It is not used for a new cascade.
minSize Minimum possible object size. Objects smaller than that are ignored.
maxSize Maximum possible object size. Objects larger than that are ignored. If maxSize == minSize model is evaluated on single scale.

参数的含义,来自官网,懒得翻译。

这里写图片描述

这里主要说下minNeighbors,minSize,maxSize.这三个参数.
通过这三个参数可以控制检测的精确度.
minNeighbors 值越大,检测的准确度越高,不过耗时也越久.酌情调整.
minSize 可以根据Screen 尺寸的一定比例来设置,别设置太小,不然会有一些错误干扰结果.
maxSize 最大可检测尺寸,酌情调整.

接着往下看,检测结果出来后,是一个rect集合,检测到的每一张脸是一个矩形....O__O "…
这边做的处理选择了一张脸最大的来显示.

这里写图片描述

现在我们把代码跑起来,看一下效果:

效果图

通过效果gif演示,我们可以很明显的看到,成功的检测到了人脸。这里要说个缺点,就是使用OpenCV的CascadeClassifier进行人脸检测,检测的结果是返回一个MatOfRect对象,可理解为rectangle集合,意思是只记录着检测到脸的位置。并没有其他的信息了,并不能记住识别人脸。注意人脸识别人脸检测的区别。关于OpenCV的人脸识别(FaceRecognizer)需要对目标先进行数据训练,训练好才能对目标成功识别。关于OpenCV人脸识别的内容,这边先不说了,之后的文章再讨论。

结语

关于使用OpenCV来进行人脸检测,就说到这,后期想到什么要补充的会再补充。人脸检测的实现,也写了一篇利用Firebase Vision ML Kit库来实现的文章,建议大家去看看,做做对比,根据需要选择合适的技术手段来实现。利用Google vision来实现人脸检测
演示demo地址如下。
OpencvFaceDetect

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

  • 转载请注明出处(https://www.jianshu.com/p/5f538820e370),您的打赏是小编继续...
    福later阅读 27,880评论 8 73
  • 1 实验目的 目前计算机视觉技术已经比较成熟,相关的开源项目与算法很多,可以将这些开源算法进行整合,进而做成一个小...
    YOUNG_FAN阅读 6,812评论 0 50
  • 有一天晚上我梦见了雨 它落在了屋檐上耗尽之前所有的喧嚣像是一位匆匆过客却余音绕梁 它落在了田野上成为广袤中的无数鳞...
    不安分大叔阅读 202评论 6 3
  • 在昨晚她进屋洗澡,反锁门的时候,我就意识到。她已经把我拒之门外了。所有一切都证明,我还是在她心门之外。 我觉得再多...
    d3993c4b32f3阅读 165评论 0 0
  • 一只鸟, 落在输电线上, 四处张望, 塔杆下的我, 不知所措。
    野_方阅读 125评论 1 0