对于人脸检测,我们第一想到的肯定是利用相机功能,但是android我们都很了解,android从5.0之后 camera 有两套API分别是 CameraAPI 1 和 CameraAPI 2,希望大家还是先去熟悉以下Camera 1/2 的用法然后再来看本章,会更好理解Open cv 调用 Camera的流程。在OpenCV中已经为我们封装了两个类 JavaCameraView 和 JavaCamera2View 顾名思义,JavaCameraView 封装的就是我们的 Camera 1,另外一个就是2。今天我们主要讲讲 OpenCV 调用 Camera 1 的流程,下一篇记录OpenCV 调用Camera 2 的流程。

  1. 级联分类器
    (百度百科)首先我们要理解一个名词‘级联分类器’(Cascade Classifier),OpenCV 中人脸检测是基于Harr的级联分类和LBP的级联分类。
    Harr是在2001年,由Viola和Jones等人提出的,它的脸部检测的基本思想是:对于面部正面的大部分区域而言,会有眼睛所在的区域比前额和脸颊更暗,嘴巴应该比脸颊更暗等情况。和这样类似的比较大约有20个,通过这样的比较决定该区域是否为人脸。 
    LBP是在2006年由Ahonen等人提出的,相比于Harr,LBP有更快的速度。通过比较想读亮度直方图来确定是否为人脸。但是对于稳定性,LBP要弱于前者。
    OpenCV中提供了 Harr 和 LBP 两种分类器,我们主要使用的是LBP,获取LBP 的 xml 文件:

     

  2. 用LBP级联分类器实现人脸检测
    先把代码具体实现流程梳理一遍,然后再针对代码流程去了解 OpenCV 和 我们的 Camera 是如何联系的。
    1.首先我们把上面提到的 lbpcascade_frontalface.xml 文件拷贝到我们 raw 下。


    2.创建我们级联分类器和Camera对象,级联分类器英文:Cascade Classifier 所以OpenCV 给我们的类名是CascadeClassifier
        

    3.利用流的形式把 raw 下的文件拷贝到我们的程序中,用CascadeClassifier对象加载
        private void initClassifier() {        InputStream is = getResources().openRawResource(R.raw.lbpcascade_frontalface);        File cascadeDir = getDir("cascade", Context.MODE_PRIVATE);        File mCascadeFile = new File(cascadeDir, "lbpcascade_frontalface.xml");        FileOutputStream os = null;        try {            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();            cascadeClassifier = new CascadeClassifier(mCascadeFile.getAbsolutePath());        } catch (Exception e) {            e.printStackTrace();        }        mCameraView.enableView();    }

    4.利用OpenCV提供的camera 对象获取frame帧数,显示在screen上,具体显示流程接下来会把流程图画出
     
     mCameraView = findViewById(R.id.camera);        mCameraView.setCvCameraViewListener(new CameraBridgeViewBase.CvCameraViewListener() {            @Override            public void onCameraViewStarted(int width, int height) {                grayscaleImage = new Mat(height, width, CvType.CV_8UC4);                absoluteFaceSize = (int) (height * 0.2);            }            @Override            public void onCameraViewStopped() {            }            @Override            public Mat onCameraFrame(Mat aInputFrame) {                Imgproc.cvtColor(aInputFrame, grayscaleImage, Imgproc.COLOR_RGBA2RGB);                MatOfRect faces = new MatOfRect();                if (cascadeClassifier != null) {                    cascadeClassifier.detectMultiScale(grayscaleImage, faces, 1.1, 2, 2,                            new Size(absoluteFaceSize, absoluteFaceSize), new Size());                }                Rect[] facesArray = faces.toArray();                for (int i = 0; i 
    到此代码就这么多,详细代码地址:https://github.com/WangRain1/OpencvDemo
  3. OpenCV 中 JavaCameraView 如何调用 Camera API 1 流程分析
     
    首先重要类的集成关系: 

    可以看到 OpenCV 给我们提供的 JavaCameraView 和 JavaCamera2View 最终都是继承 SurfaceView 的
    CameraBridgeViewBase 就相当于一个桥梁,具体起到什么作用根据上述代码,我们流程从 setCvCameraViewListener() 接口开启:首先通过这个接口会会创建一个 CvCameraViewListenerAdapter 对象
      public void setCvCameraViewListener(CvCameraViewListener listener) {        CvCameraViewListenerAdapter adapter = new CvCameraViewListenerAdapter(listener);        adapter.setFrameFormat(mPreviewFormat);        mListener = adapter;    }
    然后 CvCameraViewListenerAdapter 的代码:
        protected class CvCameraViewListenerAdapter implements CvCameraViewListener2  {        public CvCameraViewListenerAdapter(CvCameraViewListener oldStypeListener) {            mOldStyleListener = oldStypeListener;        }        public void onCameraViewStarted(int width, int height) {            mOldStyleListener.onCameraViewStarted(width, height);        }        public void onCameraViewStopped() {            mOldStyleListener.onCameraViewStopped();        }        public Mat onCameraFrame(CvCameraViewFrame inputFrame) {             Mat result = null;             switch (mPreviewFormat) {                case RGBA:                    result = mOldStyleListener.onCameraFrame(inputFrame.rgba());                    break;                case GRAY:                    result = mOldStyleListener.onCameraFrame(inputFrame.gray());                    break;                default:                    Log.e(TAG, "Invalid frame format! Only RGBA and Gray Scale are supported!");            };            return result;        }        public void setFrameFormat(int format) {            mPreviewFormat = format;        }        private int mPreviewFormat = RGBA;        private CvCameraViewListener mOldStyleListener;    };
    可以看到只是 CvCameraViewListener2 的接口实现类,其中构造方法中传入了 CvCameraViewListener 然后调用
    CvCameraViewListener 的方法,我们明白了,就是为了用 CvCameraViewListener2 转化以下让我们在CameraBridgeViewBase 中只需写一份代码。就是 Camera 1 和 Camera 2 通用。

     
  4. 总结
    看代码的时候我们可以结合流程图看,方便理解。
    gitHub:
    https://github.com/WangRain1/OpencvDemo opencv的所有代码都在着一个demo里
    下一章学习 opencv 和 camera2 配合。
     

更多相关文章

  1. Android(安卓)M 新的运行时权限开发者需要知道的一切
  2. Android设计模式-责任链
  3. Android技术栈(四)Android(安卓)Jetpack MVVM 完全实践
  4. android dialog——自定义对话框之一
  5. 关于Android远程进程导致程序代码多次执行问题
  6. Android学习——windows下搭建NDK_r9环境
  7. Android高手进阶教程(十六)之---Android中万能的BaseAdapter(Spi
  8. Android(安卓)Activity横屏、竖屏、全屏
  9. 使用Clojure构建原生Android应用

随机推荐

  1. 干货丨前端chart组件展示DolphinDB数据教
  2. 算法面试经常需要你手写的三个排序算法(Py
  3. LeetCode 实战:「图解」K 个一组翻转链表
  4. 植树节,程序猿种的那些树
  5. 两分钟看完一道数学思想的算法题
  6. 这道算法题太简单?你忽略了时间复杂度的要
  7. 几道和散列(哈希)表有关的面试题
  8. 深度解析「正则表达式匹配」:从暴力解法到
  9. 五分钟知识小科普:什么是 Base64编码
  10. 几道和「二叉树」有关的算法面试题