OpenCV Development -- migrating official examples to projects

I. overview

  • Detectionbasedtracker provided by the official example Detectionbasedtracker under Java and JNI_ jni. CPP interaction
  • When the project is migrated, the package name changes. If the content under JNI is copied to the new package, detectionbasedtracker Java cannot find detectionbasedtracker under JNI_ jni. Cpp file
  • Rewrite the file under JNI to generate DetectionBasedTracker_jni.h and DetectionBasedTracker_jni.cpp

II. Relationship between FdActivity, DetectionBasedTracker and jni

2.1 calling relationship between

Calling DetectionBasedTracker. in FdActivity When using the start() method in Java

Execute detectionbasedtracker native method in Java nativeStart(long thiz)

DetectionBasedTracker. The native method in Java has been in the detectionbasedtracker in JNI_ jni. Declaration in H

DetectionBasedTracker_ jni. Detectionbasedtracker is implemented in CPP_ jni. Method declared in H

2.2 schematic diagram of calling relationship

III. key points of knowledge

  • Knowledge: NDK and JNI
  • Dependencies: OpenCV, javacpp and javacv

IV. project migration

4.1 create a new project (e.g. MyOpenCV)

4.2 add opencv and dependencies

4.2.1 importing opencv SDK

Click File - > New - > import module from source to import opencv SDK

4.2.2 configure NDK

After importing, the following errors may be displayed (which may be caused by not downloading NDK or configuring NDK)

Install NDK: click SDK Manager - > appearance & behavior > System Settings > Android SDK - > SDK tools to install NDK and CMake

Configure SDK: click File - > project structure - > SDK location, and select the NDK File location

settings. Configure opencv SDK in gradle (because it is in the same directory as the project, opencvsdk = ', the upper directory, opencvsdk ='... / ')

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

app/build. Add opencv and javacpp under gradle, javacv

  //opencv face detection
  implementation project(':opencv')
  //Face recognition
  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 migration project code (code file + jni file + layout file)

  • Code files: migrate the files (FdActivity and DetectionBasedTracker) to java / [package name] of the new project
  • Layout files: layout / face_ detect_ surface_ view. Migrate XML to new project layout
  • Resource file: raw/lbpcascade_frontalface.xml is migrated to the res directory of the new project
  • jni: face detection / jni migrate to the main directory of the new project

4.4 generate the data under jni according to the native method h and cpp file

Under jni h and The cpp file is generated according to the package name. The native of the new project cannot recognize the old project h and Cpp file error

Detectionbasedtracker in JNI folder_ jni. H and DetectionBasedTracker_jni.cpp is deleted. At this time, only

Android.mk
Application.mk
CMakeLists.txt

Right click on main/java - > open in terminal to open the CMD terminal. At this time, the code display position in CMD is

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

Execute the javah command to generate the corresponding from the native method h header file

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

explain:

  • Javah: it is a set of javah commands, which can perform operation generation h header file
  • -d: Destination file location:/ jni: indicates the jni directory at the upper level of java
  • -JNI: generate JNI style header file (default) (when you enter javah, you can display the options option to view)
  • com.example.myopencv.DetectionBasedTracker: the path of the file where the native method is located (package name + class name)

Delete package name prefix com_example_myopencv_, The file name is DetectionBasedTracker_jni.h. At the same time, DetectionBasedTracker_jni.h make a copy and rename it DetectionBasedTracker_jni.cpp (because Android.mk specifies the file name of 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 configuring 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 {  //Configuration address modification
        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'  //Configuration address modification
        }
    }     

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 (minSdkVersion of OpenCV SDK is 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)

Please set minSdkVersion to 21

minSdkVersion 21

4.5.4 OS independent conflict

phenomenon

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

solve

 packagingOptions {
        exclude 'META-INF/proguard/androidx-annotations.pro'
        exclude 'META-INF/native-image/**'
}        

4.5.5 modify DetectionBasedTracker_jni.cpp file

copy the header file in the sample project to DetectionBasedTracker_jni.cpp header

#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);
    }
};

The implementation copcy of each method in the sample project is to the corresponding method (nativechreateobject as an example)

Before modification

/*
 * 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);

After modification

/*
 * 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 adding permissions

  <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 renderings

Keywords: Java OpenCV JNI NDK

Added by kevinjo on Wed, 19 Jan 2022 05:11:27 +0200