marclee44 / me

1 stars 0 forks source link

OpenCV初探之Android篇 #20

Open marclee44 opened 2 years ago

marclee44 commented 2 years ago

之前写了OpenCV初探之C#篇,今天我们来试试在Android上使用OpenCV

第一步 下载OpenCV Android SDK

可以在以下任一地址下载OpenCV Android SDK:

在任意版本下选择Android版本即可。 这里选用了4.5.3版本作为示例。 下载完成后,解压至固定文件夹待用。 嗯,如果开发是使用Java而非Kotlin,请顺便修改解压目录下的sdk\build.gradle文件,注释其中的apply plugin: 'kotlin-android'

apply plugin: 'com.android.library'
//apply plugin: 'kotlin-android'

def openCVersionName = "4.5.3"
def openCVersionCode = ((4 * 100 + 5) * 100 + 3) * 10 + 0

第二步 新建项目

新建一个Native C++项目,等待加载完成。此时,若你还未安装CMake和NDK,Android Studio会自动下载,并对项目进行基本配置。 当然,如果并不会用到一些C++特性的话,也可以新建一个普通项目。 不过这样,CMake和NDK就变成需要提前安装的前置任务了。这个我在Android超声波数据传输初探中已有详述。

第三步 导入OpenCV Android SDK

sdk.dir=D\:\\Android\\android-sdk
ndk.dir=D\:\\Android\\android-sdk\\ndk\\22.1.7171670
include ':opencv'
project(':opencv').projectDir = new File('D:\\Android\\OpenCV-android-sdk\\sdk')
dependencies {
    implementation fileTree(dir: 'libs', include: ['*.jar'])
    implementation project(':opencv')
    ...
}

好了,点击工具栏的小乌龟image同步项目(Sync Project with Gradle Files)。 等待opencv的导入及自动编译完成。

最后 从sample改一个人脸检测

嗯,由于OpenCV Android SDK自带的sample:face-detection始终无法编译通过,只能自己尝试改一个出来。

android {
    ...

    defaultConfig {
        applicationId "com.test.opencv"
        ...
        externalNativeBuild {
            cmake {
                arguments "-DOpenCV_DIR=" + project(':opencv').projectDir + "/native/jni",
                        "-DANDROID_TOOLCHAIN=clang",
                        "-DANDROID_STL=c++_shared"
                targets "detection_based_tracker"
            }
        }
    }
    ...
}
    private int mDetectorType = JAVA_DETECTOR;
    private Mat mRgba;
    private Mat mGray;
    private File mCascadeFile;
    private CascadeClassifier mJavaDetector;
    private DetectionBasedTracker  mNativeDetector;
    private CameraBridgeViewBase mOpenCvCameraView;
    ...
    private BaseLoaderCallback mLoaderCallback = new BaseLoaderCallback(this) {
        @Override
        public void onManagerConnected(int status) {
            switch (status) {
                case LoaderCallbackInterface.SUCCESS:
                {
                    //在OpenCV初始化完成后,加载native library
                    System.loadLibrary("detection_based_tracker");

                    try {
                        //加载Haar特征库文件
                        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();

                        mJavaDetector = new CascadeClassifier(mCascadeFile.getAbsolutePath());
                        if (mJavaDetector.empty()) {
                            //加载失败
                            mJavaDetector = null;
                        }

                        mNativeDetector = new DetectionBasedTracker(mCascadeFile.getAbsolutePath(), 0);
                        cascadeDir.delete();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                    mOpenCvCameraView.enableView();
                } break;
                default:
                {
                    super.onManagerConnected(status);
                } break;
            }
        }
    };

    public Mat onCameraFrame(CameraBridgeViewBase.CvCameraViewFrame inputFrame) {
        mRgba = inputFrame.rgba();
        mGray = inputFrame.gray();

        if (mAbsoluteFaceSize == 0) {
            int height = mGray.rows();
            if (Math.round(height * mRelativeFaceSize) > 0) {
                mAbsoluteFaceSize = Math.round(height * mRelativeFaceSize);
            }
            mNativeDetector.setMinFaceSize(mAbsoluteFaceSize);
        }

        MatOfRect faces = new MatOfRect();

        if (mDetectorType == JAVA_DETECTOR) {
            if (mJavaDetector != null)
                mJavaDetector.detectMultiScale(mGray, faces, 1.1, 2, 2,
                        new Size(mAbsoluteFaceSize, mAbsoluteFaceSize), new Size());
        }
        else if (mDetectorType == NATIVE_DETECTOR) {
            if (mNativeDetector != null)
                mNativeDetector.detect(mGray, faces);
        }
        else {
            Log.e(TAG, "Detection method is not selected!");
        }

        Rect[] facesArray = faces.toArray();
        for (int i = 0; i < facesArray.length; i++)
            Imgproc.rectangle(mRgba, facesArray[i].tl(), facesArray[i].br(), FACE_RECT_COLOR, 3);

        return mRgba;
    }

可以看到主要检测方法,与C#区别并不大