OpenCV开发之——将官方示例迁移到项目上

一 概述

  • 官方示例提供的DetectionBasedTracker.java与jni下的DetectionBasedTracker_jni.cpp交互
  • 当进行项目迁移时,包名发生变化,若将jni下的内容copy到新包后,DetectionBasedTracker.java找不到jni下的DetectionBasedTracker_jni.cpp文件
  • 重新编写jni下的文件生成DetectionBasedTracker_jni.hDetectionBasedTracker_jni.cpp

二 FdActivity、DetectionBasedTracker和jni的关系

2.1 之间的调用关系

FdActivity中调用DetectionBasedTracker.java中的start()方法时

执行DetectionBasedTracker.java中的native方法nativeStart(long thiz)

DetectionBasedTracker.java中的native方法已在jni中的DetectionBasedTracker_jni.h中声明

DetectionBasedTracker_jni.cpp中实现了DetectionBasedTracker_jni.h中声明的方法

2.2 调用关系示意图

三 知识要点

  • 具备知识:NDK和JNI
  • 依赖:OpenCV和javacpp及javacv

四 项目迁移

4.1 创建新项目如(MyOpenCV)

4.2 添加opencv及依赖

4.2.1 导入opencv-sdk

依次点击:File——>New——>Import module from source,导入opencv-sdk

4.2.2 配置NDK

导入后,可能会显示如下错误(可能是未下载NDK或配置NDK引起)

安装NDK:点击SDK Manager——>Appearance&Behavior>System Settings>Android SDK——>SDK Tools,安装NDK和CMake

配置SDK:依次点击:File——>Project Struct——>SDK Location,选择NDK文件位置

settings.gradle中配置opencv-sdk(因为与项目在同一目录下,opencvsdk=‘’,上一级目录,opencvsdk=‘…/’)

def opencvsdk=''
//def opencvsdk='/<path to OpenCV-android-sdk>'
include ':opencv'
project(':opencv').projectDir = new File(opencvsdk + '/sdk')

app/build.gradle下添加opencv和javacpp,javacv

  //opencv-人脸检测
  implementation project(':opencv')
  //人脸识别
  implementation 'org.bytedeco:javacpp:1.5.5' //javacpp
  implementation 'org.bytedeco:javacv:1.5.5' //javac

  implementation group: 'org.bytedeco', name: 'javacv-platform', version: '1.5.5'
  implementation group: 'org.bytedeco', name: 'javacpp-platform', version: '1.5.5'

4.3 迁移项目代码(代码文件+jni文件+布局文件)

  • 代码文件:将文件(FdActivity和DetectionBasedTracker)迁移到新项目的java/[包名]下
  • 布局文件:layout/face_detect_surface_view.xml迁移到新项目layout下
  • 资源文件:raw/lbpcascade_frontalface.xml迁移到新项目res目录下
  • jni:face-detection/jni迁移到新项目的main目录下

4.4 根据native方法生成jni下的.h.cpp文件

jni下的.h.cpp文件是根据包名生成的,新项目的native识别不了旧项目的.h.cpp文件出错

将jni文件夹中的DetectionBasedTracker_jni.hDetectionBasedTracker_jni.cpp删除,此时jni下只有

Android.mk
Application.mk
CMakeLists.txt

在main/java右键——>Open in Terminal,打开CMD终端,此时cmd中代码显示位置为

D:\Code\Android\MyOpenCV\app\src\main\java>

执行javah命令,将将native方法生成对应的.h头文件

javah -d ../jni -jni com.example.myopencv.DetectionBasedTracker

说明:

  • javah:是javah命令集,可以执行操作生成.h头文件
  • -d:目的文件位置:../jni:表示java上一级的jni目录下
  • -jni:生成 JNI 样式的标头文件 (默认值)(输入javah时,可显示options选项查看)
  • com.example.myopencv.DetectionBasedTracker:native方法所在文件的路径(包名+类名)

删除包名前缀com_example_myopencv_,文件名为DetectionBasedTracker_jni.h,同时将DetectionBasedTracker_jni.h复制一份改名为DetectionBasedTracker_jni.cpp(因为Android.mk指定了cpp的文件名)

LOCAL_SRC_FILES  := DetectionBasedTracker_jni.cpp
LOCAL_C_INCLUDES += $(LOCAL_PATH)
LOCAL_LDLIBS     += -llog -ldl

LOCAL_MODULE     := detection_based_tracker

include $(BUILD_SHARED_LIBRARY)

4.5 配置NDK

4.5.1 app/build.grale

defaultConfig

externalNativeBuild {
    cmake {
              arguments "-DOpenCV_DIR=" + project(':opencv').projectDir + "/native/jni",
                        "-DANDROID_TOOLCHAIN=clang",
                        "-DANDROID_STL=c++_shared"
              targets "detection_based_tracker"
                ///abiFilters  "armeabi-v7a" , "arm64-v8a", "x86", "x86_64"
          }
   }

android{}

sourceSets {  //配置地址修改
        main {
            java.srcDirs = ['src/main/java']
            aidl.srcDirs = ['src/main/java']
            res.srcDirs = ['src/main/res']
            manifest.srcFile 'src/main/AndroidManifest.xml'
        }
    }
externalNativeBuild {
        cmake {
            path 'src/main/jni/CMakeLists.txt'  //配置地址修改
        }
    }     
4.5.2 project/build.gradle(APP_ABI)
gradle.afterProject { project ->
    if (project.pluginManager.hasPlugin('com.android.application')
            || project.pluginManager.hasPlugin('com.android.library')
            || project.pluginManager.hasPlugin('com.android.test')
            || project.pluginManager.hasPlugin('com.android.feature') ) {
        if (true) {
            gradle.println("Override build ABIs for the project ${project.name}")
            project.android {
                splits {
                    abi {
                        enable true
                        universalApk false

//reset()
//include 'armeabi-v7a'
//include 'arm64-v8a'
//include 'x86'
//include 'x86_64'

                    }
                }
            }
        }

        if (true) {
            gradle.println("Override lintOptions for the project ${project.name}")
            project.android {
                lintOptions {
                    // checkReleaseBuilds false
                    abortOnError false
                }
            }
        }

        // (you still need to re-build OpenCV with debug information to debug it)
        if (true) {
            gradle.println("Override doNotStrip-debug for the project ${project.name}")
            project.android {
                buildTypes {
                    debug {
                        packagingOptions {
                            doNotStrip '**/*.so'  // controlled by OpenCV CMake scripts
                        }
                    }
                }
            }
        }
        if (false || project.hasProperty("doNotStrip")) {
            gradle.println("Override doNotStrip-release for the project ${project.name}")
            project.android {
                buildTypes {
                    release {
                        packagingOptions {
                            doNotStrip '**/*.so'  // controlled by OpenCV CMake scripts
                        }
                    }
                }
            }
        }

    }
}
4.5.3 OpenCV API level is android-21(opencv-sdk的minSdkVersion为21)
D:\Code\Android\MyOpenCV\app\src\main\jni\CMakeLists.txt : C/C++ debug|x86 : CMake Warning at D:/Code/Android/MyOpenCV/sdk/native/jni/abi-x86/OpenCVConfig.cmake:105 (message):
  Minimum required by OpenCV API level is android-21
Call Stack (most recent call first):
  D:/Code/Android/MyOpenCV/sdk/native/jni/OpenCVConfig.cmake:44 (include)
  CMakeLists.txt:8 (find_package)

请将minSdkVersion设置为21

minSdkVersion 21
4.5.4 OS independent 冲突

现象

More than one file was found with OS independent path 'META-INF/native-image/ios-x86_64/jnijavacpp/reflect-config.json'.

解决

 packagingOptions {
        exclude 'META-INF/proguard/androidx-annotations.pro'
        exclude 'META-INF/native-image/**'
}        
4.5.5 修改DetectionBasedTracker_jni.cpp文件

将示例项目中的头文件copy到DetectionBasedTracker_jni.cpp头部

#include <DetectionBasedTracker_jni.h>
#include <opencv2/core.hpp>
#include <opencv2/objdetect.hpp>

#include <string>
#include <vector>

#include <android/log.h>

#define LOG_TAG "FaceDetection/DetectionBasedTracker"
#define LOGD(...) ((void)__android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__))

using namespace std;
using namespace cv;

inline void vector_Rect_to_Mat(vector<Rect>& v_rect, Mat& mat)
{
    mat = Mat(v_rect, true);
}

class CascadeDetectorAdapter: public DetectionBasedTracker::IDetector
{
public:
    CascadeDetectorAdapter(cv::Ptr<cv::CascadeClassifier> detector):
            IDetector(),
            Detector(detector)
    {
        LOGD("CascadeDetectorAdapter::Detect::Detect");
        CV_Assert(detector);
    }

    void detect(const cv::Mat &Image, std::vector<cv::Rect> &objects)
    {
        LOGD("CascadeDetectorAdapter::Detect: begin");
        LOGD("CascadeDetectorAdapter::Detect: scaleFactor=%.2f, minNeighbours=%d, minObjSize=(%dx%d), maxObjSize=(%dx%d)", scaleFactor, minNeighbours, minObjSize.width, minObjSize.height, maxObjSize.width, maxObjSize.height);
        Detector->detectMultiScale(Image, objects, scaleFactor, minNeighbours, 0, minObjSize, maxObjSize);
        LOGD("CascadeDetectorAdapter::Detect: end");
    }

    virtual ~CascadeDetectorAdapter()
    {
        LOGD("CascadeDetectorAdapter::Detect::~Detect");
    }

private:
    CascadeDetectorAdapter();
    cv::Ptr<cv::CascadeClassifier> Detector;
};

struct DetectorAgregator
{
    cv::Ptr<CascadeDetectorAdapter> mainDetector;
    cv::Ptr<CascadeDetectorAdapter> trackingDetector;

    cv::Ptr<DetectionBasedTracker> tracker;
    DetectorAgregator(cv::Ptr<CascadeDetectorAdapter>& _mainDetector, cv::Ptr<CascadeDetectorAdapter>& _trackingDetector):
            mainDetector(_mainDetector),
            trackingDetector(_trackingDetector)
    {
        CV_Assert(_mainDetector);
        CV_Assert(_trackingDetector);

        DetectionBasedTracker::Parameters DetectorParams;
        tracker = makePtr<DetectionBasedTracker>(mainDetector, trackingDetector, DetectorParams);
    }
};

将示例项目中每个方法的实现copcy到对应方法上(nativeCreateObject为例)

修改前

/*
 * Class:     com_example_myopencv_DetectionBasedTracker
 * Method:    nativeCreateObject
 * Signature: (Ljava/lang/String;I)J
 */
JNIEXPORT jlong JNICALL Java_com_example_myopencv_DetectionBasedTracker_nativeCreateObject
  (JNIEnv *, jclass, jstring, jint);

修改后

/*
 * Class:     com_example_myopencv_DetectionBasedTracker
 * Method:    nativeCreateObject
 * Signature: (Ljava/lang/String;I)J
 */
JNIEXPORT jlong JNICALL Java_com_example_myopencv_DetectionBasedTracker_nativeCreateObject
        (JNIEnv * jenv, jclass, jstring jFileName, jint faceSize)
{
    LOGD("Java_org_opencv_samples_facedetect_DetectionBasedTracker_nativeCreateObject enter");
    const char* jnamestr = jenv->GetStringUTFChars(jFileName, NULL);
    string stdFileName(jnamestr);
    jlong result = 0;

    LOGD("Java_org_opencv_samples_facedetect_DetectionBasedTracker_nativeCreateObject");

    try
    {
        cv::Ptr<CascadeDetectorAdapter> mainDetector = makePtr<CascadeDetectorAdapter>(
                makePtr<CascadeClassifier>(stdFileName));
        cv::Ptr<CascadeDetectorAdapter> trackingDetector = makePtr<CascadeDetectorAdapter>(
                makePtr<CascadeClassifier>(stdFileName));
        result = (jlong)new DetectorAgregator(mainDetector, trackingDetector);
        if (faceSize > 0)
        {
            mainDetector->setMinObjectSize(Size(faceSize, faceSize));
            //trackingDetector->setMinObjectSize(Size(faceSize, faceSize));
        }
    }
    catch(const cv::Exception& e)
    {
        LOGD("nativeCreateObject caught cv::Exception: %s", e.what());
        jclass je = jenv->FindClass("org/opencv/core/CvException");
        if(!je)
            je = jenv->FindClass("java/lang/Exception");
        jenv->ThrowNew(je, e.what());
    }
    catch (...)
    {
        LOGD("nativeCreateObject caught unknown exception");
        jclass je = jenv->FindClass("java/lang/Exception");
        jenv->ThrowNew(je, "Unknown exception in JNI code of DetectionBasedTracker.nativeCreateObject()");
        return 0;
    }

    LOGD("Java_org_opencv_samples_facedetect_DetectionBasedTracker_nativeCreateObject exit");
    return result;
}

4.6 添加权限

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

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

4.7 效果图

五 说明

  • 因为添加了人脸识别导致apk的体积增大(700M左右)
  • 下面讲解如何通过修改依赖降低apk的体积
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值