导言:本文是对OpenCV for Android的配置详细过程的展示,并且为在此过程中的一些坑总结整理可行的解决方案,这些解决方案大多来自网络,感谢各位大手。本文最后讲实现在Android手机上运行OpenCV的人脸识别。

一、创建一个支持NDK的Android程序

opencv打开安卓 opencv android_opencv打开安卓

opencv打开安卓 opencv android_Android_02

opencv打开安卓 opencv android_opencv打开安卓_03

opencv打开安卓 opencv android_android_04

opencv打开安卓 opencv android_opencv打开安卓_05

二、进行OpenCV库的导入和环境的配置

opencv打开安卓 opencv android_OpenCV_06

opencv打开安卓 opencv android_android_07

opencv打开安卓 opencv android_opencv打开安卓_08

刚把库导入,Android Sdudio进行编译的时候回出现如下问题:

opencv打开安卓 opencv android_android_09

我们只需要点击选项框,进入Project显示,修改openCVLibraryxxx(xxx代表opencv的版本号)中的build.gradle中compileSdkVersion、minSdkVersion、targetSdkVersion后面的数字和app中的build.gradle相同。如下图:

opencv打开安卓 opencv android_opencv打开安卓_10

点击最上面的Try Again之后,上述错误消失,我们需要将opencv3.so库放到我们自己的工程文件夹下,进行如下操作:

将G:\Andriod_NDK_wsp\OpenCV_demo1\app\src\mai目录下新建一个目录jniLibs,将G:\Andriod_NDK_wsp\OpenCV-android-sdk\sdk\native\libs目录下的文件复制到jniLibs目录

opencv打开安卓 opencv android_android_11

在Android Studio中Android目录中,app的build.gradle中添加:

sourceSets {
        main {
            jniLibs.srcDirs =['src/main/jniLibs']
        }
}

Dependencies中添加:

implementation project(':openCVLibrary341')
compile project(':openCVLibrary341')

opencv打开安卓 opencv android_android_12

opencv打开安卓 opencv android_opencv打开安卓_13

在MainActivity类中最开始添加:

static{
       System.loadLibrary("opencv_java3");
}

这个使我们不用安装OpenCV的支持App了。

opencv打开安卓 opencv android_opencv打开安卓_14

三、运行人脸识别的Demo

在布局文件activity_main.xml中添加布局:

<org.opencv.android.JavaCameraView

        android:id="@+id/cameraView"

       android:layout_width="match_parent"

       android:layout_height="match_parent"

       app:camera_id="back"/>

在AndroidManifest.xml文件中添加摄像头权限:

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

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

将OpenCV官方训练的人脸识别的模型文件lbpcascade_frontalface.xml从G:\Andriod_NDK_wsp\OpenCV-android-sdk\sdk\etc\lbpcascades文件夹下复制到自己工程目录下的app\src\main\res\raw目录下(如果没有,就自己创建一个)。


在MainActivity.java文件如下:

private static final int TAKE_PHOTO_REQUEST_CODE = 1;
    static{
        System.loadLibrary("opencv_java3");
    }
    private CameraBridgeViewBase openCvCameraView;
    private CascadeClassifier cascadeClassifier;
    private Mat grayscaleImage;
    private int absoluteFaceSize;


    private BaseLoaderCallback mLoaderCallback = new BaseLoaderCallback(this) {
        @Override
        public void onManagerConnected(int status) {
            switch (status) {
                case LoaderCallbackInterface.SUCCESS:
                    initializeOpenCVDependencies();
                    break;
                default:
                    super.onManagerConnected(status);
                    break;
            }
        }
    };


    private void initializeOpenCVDependencies() {
        try {
            // Copy the resource into a temp file so OpenCV can load it
            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 = 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();


            // Load the cascade classifier
            cascadeClassifier = new CascadeClassifier(mCascadeFile.getAbsolutePath());
        } catch (Exception e) {
            Log.e("OpenCVActivity", "Error loading cascade", e);
        }


        // And we are ready to go
        openCvCameraView.enableView();
    }


    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
        openCvCameraView = new JavaCameraView(this, -1);
        setContentView(openCvCameraView);
        if(Build.VERSION.SDK_INT>22){
            if(ContextCompat.checkSelfPermission(MainActivity.this, android.Manifest.permission.CAMERA)!= PackageManager.PERMISSION_GRANTED){
                ActivityCompat.requestPermissions(MainActivity.this, new String[]{android.Manifest.permission.CAMERA},TAKE_PHOTO_REQUEST_CODE);
            }
        }
        openCvCameraView.setCvCameraViewListener(this);
    }


    @Override
    public void onCameraViewStarted(int width, int height) {
        grayscaleImage = new Mat(height, width, CvType.CV_8UC4);
        // The faces will be a 20% of the height of the screen
        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();

        // Use the classifier to detect faces
        if (cascadeClassifier != null) {
            cascadeClassifier.detectMultiScale(grayscaleImage, faces, 1.1, 3, 0,
                    new Size(absoluteFaceSize, absoluteFaceSize), new Size());
        }

        // If there are any faces found, draw a rectangle around it
        Rect[] facesArray = faces.toArray();
        for (int i = 0; i <facesArray.length; i++)
            Imgproc.rectangle(aInputFrame, facesArray[i].tl(), facesArray[i].br(), new Scalar(0, 255, 0, 255), 3);
        return aInputFrame;
    }


    @Override
    public void onResume() {
        super.onResume();

        if (!OpenCVLoader.initDebug()) {
            Log.e("log_wons", "OpenCV init error");
            // Handle initialization error
        }
        initializeOpenCVDependencies();
        //OpenCVLoader.initAsync(OpenCVLoader.OPENCV_VERSION_2_4_6, this, mLoaderCallback);
    }
    @Override
    public void onRequestPermissionsResult(int requestCode,String[] permissions,int[] grantResults) {
        switch (requestCode){
            case TAKE_PHOTO_REQUEST_CODE:
                if (grantResults.length>0&&grantResults[0]==PackageManager.PERMISSION_GRANTED){
                    //这里已经获取到了摄像头的权限,想干嘛干嘛了可以

                }else {
                    //这里是拒绝给APP摄像头权限,给个提示什么的说明一下都可以。
                    Toast.makeText(MainActivity.this,"请手动打开相机权限",Toast.LENGTH_SHORT).show();
                }
                break;
            default:
                break;
        }
}

如果出现摄像头的预览不是竖屏的全屏,更改OpenCV的CameraBridgeViewBase类中的deliverAndDrawFrame方法,更改成如下:

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

 

    <uses-featureandroid:name="android.hardware.camera"android:required="false"/>

    <uses-featureandroid:name="android.hardware.camera.autofocus"android:required="false"/>

    <uses-featureandroid:name="android.hardware.camera.front"android:required="false"/>

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



Mat modified;

    if (mListener != null) {
        modified = mListener.onCameraFrame(frame);
    } else {
        modified = frame.rgba();
    }

    boolean bmpValid = true;
        if (modified != null) {
            try {
                Utils.matToBitmap(modified, mCacheBitmap);
            } catch (Exception e) {
                Log.e(TAG, "Mat type: " + modified);
                Log.e(TAG, "Bitmap type: " + mCacheBitmap.getWidth() + "*" + mCacheBitmap.getHeight());
                Log.e(TAG, "Utils.matToBitmap() throws an exception: " + e.getMessage());
                bmpValid = false;
            }
        }

        if (bmpValid && mCacheBitmap != null) {
            Canvas canvas = getHolder().lockCanvas();
            if (canvas != null) {
                canvas.drawColor(0, android.graphics.PorterDuff.Mode.CLEAR);

                // 修改预览旋转90度问题
                canvas.rotate(90,0,0);
                float scale = canvas.getWidth() / (float)mCacheBitmap.getHeight();
                float scale2 = canvas.getHeight() / (float)mCacheBitmap.getWidth();
                if(scale2 > scale){
                    scale = scale2;
                }
                if (scale != 0) {
                    canvas.scale(scale, scale,0,0);
                }
                canvas.drawBitmap(mCacheBitmap, 0, -mCacheBitmap.getHeight(), null);
                // 修改预览旋转90度问题end

                if (mFpsMeter != null) {
                    mFpsMeter.measure();
                    mFpsMeter.draw(canvas, 20, 30);
                }
                getHolder().unlockCanvasAndPost(canvas);
            }
        }

到此我们就完成了整个流程,如果在过程中或者刚打开Android Studio出现如下情况,直接点击菜单栏中Build->reBuild就好了,目前还没有发下别的有效的方法(重装都不管用)

opencv打开安卓 opencv android_OpenCV_15

OpenCV4Android SDK搬运(如果自己下载慢,可以选择这个):

链接:https://pan.baidu.com/s/1iunqc5NcigH7ZS45xkRvSg 密码:cd4y