Android OpenCV 实现人脸检测(手把手,一步步实现)

1.下载OpenCV

OpenCV 网址:OpenCV 

选择一个版本下载,这里以4.5.3版本为例,下载Android版本

 下载后解压后得到4个文件,如下所示:

 2.创建Android项目

打开Android studio ,新建一个工程,创建后点击Android studio左上角的File下的New菜单下的Import Module如下所示:

 找到刚下载好解压后的OpenCV目录,选择sdk作为module导入,如下所示:

可以修改module的名称

 导入后,需要修改openCV module下的build.gradle,修改的信息与你app下的build.gradle的版本信息一直即可

 需要注意的是,openCV4.5.3有使用到kotlin,所以你用Java开发的话,还需要在你项目下的build.gradle的文件下添加kotlin的支持,如下所示:

 之后,将module依赖到你的项目中,在app的build.gradle添加OpenCV modulde的依赖,如下所示:

 至此,OpenCV的环境搭建完毕,接下来,实现人脸检测

3.实现人脸检测

1.添加布局:

        新建一个布局,作为界面的展示,代码如下所示:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/di"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="#FAFFFFFF"
    android:orientation="vertical">

    <org.opencv.android.JavaCameraView
        android:id="@+id/cjv"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:background="#00090808" />

</LinearLayout>

opencv里自带相机预览控件,我们这里直接使用。

activity代码如下:


import org.opencv.android.BaseLoaderCallback;
import org.opencv.android.CameraBridgeViewBase.CvCameraViewFrame;
import org.opencv.android.LoaderCallbackInterface;
import org.opencv.android.OpenCVLoader;
import org.opencv.core.*;
import org.opencv.android.CameraBridgeViewBase;
import org.opencv.android.CameraBridgeViewBase.CvCameraViewListener2;
import org.opencv.imgproc.Imgproc;
import org.opencv.objdetect.CascadeClassifier;

import android.Manifest;
import android.annotation.SuppressLint;
import android.content.Context;
import android.os.Bundle;
import android.util.Log;
import android.widget.Toast;
import androidx.appcompat.app.AppCompatActivity;
import com.tbruyelle.rxpermissions2.RxPermissions;
import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStream;


public class OpenCVActivity extends AppCompatActivity implements CvCameraViewListener2 {
    private static final String TAG = "OCVSample::Activity";
    private CameraBridgeViewBase mOpenCvCameraView;
    private Mat mIntermediateMat;
    private CascadeClassifier classifier;
    private Mat mGray;
    private Mat mRgba;
    private int mAbsoluteFaceSize = 0;

    private BaseLoaderCallback mLoaderCallback = new BaseLoaderCallback(this) {
        @Override
        public void onManagerConnected(int status) {
            switch (status) {
                case LoaderCallbackInterface.SUCCESS: {
                    Log.i(TAG, "OpenCV loaded successfully");
                    getPermission();
                    mOpenCvCameraView.enableView();
                }
                break;
                default: {
                    super.onManagerConnected(status);
                }
                break;
            }
        }
    };

    public OpenCVActivity() {
        Log.i(TAG, "Instantiated new " + this.getClass());
    }

    /**
     * Called when the activity is first created.
     */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        Log.i(TAG, "called onCreate");
        super.onCreate(savedInstanceState);
        setContentView(R.layout.image_manipulations_surface_view);
        mOpenCvCameraView = (CameraBridgeViewBase) findViewById(R.id.cjv);
        //这里的cjv也是我的项目中JavaCameraView的id,自己改一下
        mOpenCvCameraView.setCvCameraViewListener(this);
    }

    @Override
    public void onPause() {
        super.onPause();
        if (mOpenCvCameraView != null)
            mOpenCvCameraView.disableView();
    }

    @Override
    public 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, this, mLoaderCallback);
        } else {
            Log.d(TAG, "OpenCV library found inside package. Using it!");
            mLoaderCallback.onManagerConnected(LoaderCallbackInterface.SUCCESS);
        }
    }

    public void onDestroy() {
        super.onDestroy();
        if (mOpenCvCameraView != null)
            mOpenCvCameraView.disableView();
    }

    public void onCameraViewStarted(int width, int height) {
        mGray = new Mat();
        mRgba = new Mat();
    }

    public void onCameraViewStopped() {
        // Explicitly deallocate Mats
        if (mIntermediateMat != null)
            mIntermediateMat.release();
        mIntermediateMat = null;
        mGray.release();
        mRgba.release();
    }

    public Mat onCameraFrame(CvCameraViewFrame inputFrame) {
//        Mat rgba = inputFrame.rgba();
//        return rgba;
        mRgba = inputFrame.rgba();
        mGray = inputFrame.gray();
        float mRelativeFaceSize = 0.2f;
        if (mAbsoluteFaceSize == 0) {
            int height = mGray.rows();
            if (Math.round(height * mRelativeFaceSize) > 0) {
                mAbsoluteFaceSize = Math.round(height * mRelativeFaceSize);
            }
        }
        MatOfRect faces = new MatOfRect();
        if (classifier != null)
            classifier.detectMultiScale(mGray, faces, 1.1, 2, 2,
                    new Size(mAbsoluteFaceSize, mAbsoluteFaceSize), new Size());
        Rect[] facesArray = faces.toArray();
        Scalar faceRectColor = new Scalar(0, 255, 0, 255);
        for (Rect faceRect : facesArray)
            Imgproc.rectangle(mRgba, faceRect.tl(), faceRect.br(), faceRectColor, 1);
        return mRgba;
    }


    // 初始化人脸级联分类器,必须先初始化
    private void initClassifier() {
        try {
            InputStream is = getResources()
                    .openRawResource(R.raw.lbpcascade_frontalface);
            File cascadeDir = getDir("cascade", Context.MODE_PRIVATE);
            File cascadeFile = new File(cascadeDir, "lbpcascade_frontalface.xml");
            FileOutputStream os = new FileOutputStream(cascadeFile);
            byte[] buffer = new byte[4096];
            int bytesRead;
            while ((bytesRead = is.read(buffer)) != -1) {
                os.write(buffer, 0, bytesRead);
            }
            is.close();
            os.close();
            classifier = new CascadeClassifier(cascadeFile.getAbsolutePath());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }



    @SuppressLint("CheckResult")
    private void getPermission(){
        RxPermissions rxPermissions = new RxPermissions(OpenCVActivity.this);
        rxPermissions.request(Manifest.permission.CAMERA).subscribe(aBoolean -> {
            if (aBoolean) {
                initClassifier();
                mOpenCvCameraView.setCameraPermissionGranted();
            } else {
                Toast.makeText(OpenCVActivity.this, "您还没有开启相机权限,请前往设置->应用管理->开启", Toast.LENGTH_SHORT).show();
            }
        });
    }
}

代码中的 lbpcascade_frontalface文件可以在openCV module下etc/lbpcascades/lbpcascade_frontalface.xml获取到,

将其复制搭app下的raw目录下,没有raw则自行创建。

此外,需要注意我们这里有使用到相机,相机在android6.0以上除了需要在AndroidManifest.xml里添加权限,还需要动态申请,否则无法打开相机。AndroidManifest.xml权限如下所示:

    <uses-permission android:name="android.permission.CAMERA" />

    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>

    <uses-feature
        android:name="android.hardware.camera"
        android:required="true" />
    <uses-feature
        android:name="android.hardware.camera.autofocus"
        android:required="true" />
    <uses-feature
        android:name="android.hardware.camera.front"
        android:required="true" />
    <uses-feature
        android:name="android.hardware.camera.front.autofocus"
        android:required="true" />

4.运行效果

猜你喜欢

转载自blog.csdn.net/h5630/article/details/121790319