4

Currently I'm working on a project that uses OpenCV3 for realtime video processing (applying a set of color filters), and I managed to get the it to work with Java without any problem.

What I try to do next is to implement all my Java logic to C++ through NDK (in order to improve performance). This way I won't need to convert MAT object to byte buffers back and fort, saving a couple of cycles. But I'm really stuck trying to link the .so, .a static libraries and headers files using gradle to be used on my .CPP files.

This is the gradle file that I'm using:

apply plugin: 'com.android.model.application'

def opencvandroid_sdk_path = file(project(':opencvandroid').projectDir).absolutePath + "/src/main"

model {

    repositories {
        libs(PrebuiltLibraries) {
            opencv {
                headers.srcDir "${opencvandroid_sdk_path}/jni/include/opencv2"
                binaries.withType(StaticLibraryBinary) {
                    staticLibraryFile = file("${opencvandroid_sdk_path}/jniLibs/${targetPlatform.getName()}/libopencv_calib3d.a")
                }
            }

            opencv1 {
                headers.srcDir "${opencvandroid_sdk_path}/jni/include/opencv2"
                binaries.withType(StaticLibraryBinary) {
                    staticLibraryFile = file("${opencvandroid_sdk_path}/jniLibs/${targetPlatform.getName()}/libopencv_core.a")
                }
            }

            opencv2 {
                headers.srcDir "${opencvandroid_sdk_path}/jni/include/opencv2"
                binaries.withType(StaticLibraryBinary) {
                    staticLibraryFile = file("${opencvandroid_sdk_path}/jniLibs/${targetPlatform.getName()}/libopencv_features2d.a")
                }
            }

            opencv3 {
                headers.srcDir "${opencvandroid_sdk_path}/jni/include/opencv2"
                binaries.withType(StaticLibraryBinary) {
                    staticLibraryFile = file("${opencvandroid_sdk_path}/jniLibs/${targetPlatform.getName()}/libopencv_flann.a")
                }
            }

            opencv4 {
                headers.srcDir "${opencvandroid_sdk_path}/jni/include/opencv2"
                binaries.withType(StaticLibraryBinary) {
                    staticLibraryFile = file("${opencvandroid_sdk_path}/jniLibs/${targetPlatform.getName()}/libopencv_highgui.a")
                }
            }

            opencv5 {
                headers.srcDir "${opencvandroid_sdk_path}/jni/include/opencv2"
                binaries.withType(StaticLibraryBinary) {
                    staticLibraryFile = file("${opencvandroid_sdk_path}/jniLibs/${targetPlatform.getName()}/libopencv_imgcodecs.a")
                }
            }

            opencv6 {
                headers.srcDir "${opencvandroid_sdk_path}/jni/include/opencv2"
                binaries.withType(StaticLibraryBinary) {
                    staticLibraryFile = file("${opencvandroid_sdk_path}/jniLibs/${targetPlatform.getName()}/libopencv_imgproc.a")
                }
            }

            opencv7 {
                headers.srcDir "${opencvandroid_sdk_path}/jni/include/opencv2"
                binaries.withType(StaticLibraryBinary) {
                    staticLibraryFile = file("${opencvandroid_sdk_path}/jniLibs/${targetPlatform.getName()}/libopencv_ml.a")
                }
            }

            opencv8 {
                headers.srcDir "${opencvandroid_sdk_path}/jni/include/opencv2"
                binaries.withType(StaticLibraryBinary) {
                    staticLibraryFile = file("${opencvandroid_sdk_path}/jniLibs/${targetPlatform.getName()}/libopencv_objdetect.a")
                }
            }

            opencv9 {
                headers.srcDir "${opencvandroid_sdk_path}/jni/include/opencv2"
                binaries.withType(StaticLibraryBinary) {
                    staticLibraryFile = file("${opencvandroid_sdk_path}/jniLibs/${targetPlatform.getName()}/libopencv_photo.a")
                }
            }

            opencv10 {
                headers.srcDir "${opencvandroid_sdk_path}/jni/include/opencv2"
                binaries.withType(StaticLibraryBinary) {
                    staticLibraryFile = file("${opencvandroid_sdk_path}/jniLibs/${targetPlatform.getName()}/libopencv_shape.a")
                }
            }

            opencv11 {
                headers.srcDir "${opencvandroid_sdk_path}/jni/include/opencv2"
                binaries.withType(StaticLibraryBinary) {
                    staticLibraryFile = file("${opencvandroid_sdk_path}/jniLibs/${targetPlatform.getName()}/libopencv_stitching.a")
                }
            }

            opencv12 {
                headers.srcDir "${opencvandroid_sdk_path}/jni/include/opencv2"
                binaries.withType(StaticLibraryBinary) {
                    staticLibraryFile = file("${opencvandroid_sdk_path}/jniLibs/${targetPlatform.getName()}/libopencv_superres.a")
                }
            }

            opencv13 {
                headers.srcDir "${opencvandroid_sdk_path}/jni/include/opencv2"
                binaries.withType(StaticLibraryBinary) {
                    staticLibraryFile = file("${opencvandroid_sdk_path}/jniLibs/${targetPlatform.getName()}/libopencv_video.a")
                }
            }

            opencv14 {
                headers.srcDir "${opencvandroid_sdk_path}/jni/include/opencv2"
                binaries.withType(StaticLibraryBinary) {
                    staticLibraryFile = file("${opencvandroid_sdk_path}/jniLibs/${targetPlatform.getName()}/libopencv_videoio.a")
                }
            }

            opencv15 {
                headers.srcDir "${opencvandroid_sdk_path}/jni/include/opencv2"
                binaries.withType(StaticLibraryBinary) {
                    staticLibraryFile = file("${opencvandroid_sdk_path}/jniLibs/${targetPlatform.getName()}/libopencv_videostab.a")
                }
            }
        }
    }

    android {
        compileSdkVersion = 23
        buildToolsVersion = "23.0.2"

        defaultConfig.with {
            applicationId = "com.example"
            minSdkVersion.apiLevel = 16
            targetSdkVersion.apiLevel = 23
            versionCode = 1
            versionName = "1.0"
        }
    }

    /*
     * native build settings
     */
    android.ndk {
        moduleName = "hello-jni"
        /*
         * Other ndk flags configurable here are
         * cppFlags += "-fno-rtti"
         * cppFlags += "-fno-exceptions"
         * ldLibs    = ["android", "log"]
         * stl       = "system"
         */
        platformVersion = 16 //same as minSdkVersion.apiLevel for better compatibility

        /*def jniPath = opencvandroid_sdk_path + "jniLibs"
        cppFlags += "-I${file(jniPath)}".toString()
        file(jniPath).eachDirRecurse { dir ->
            cppFlags += "-I${file(dir)}".toString()
        }*/

        stl = "c++_static"
        ldLibs.addAll(["atomic", "log", "android"])
    }

    android.sources {
        main {
            jni {
                dependencies {
                    library "opencv1" linkage "static"
                    library "opencv2" linkage "static"
                    library "opencv3" linkage "static"
                    library "opencv4" linkage "static"
                    library "opencv5" linkage "static"
                    library "opencv6" linkage "static"
                    library "opencv7" linkage "static"
                    library "opencv8" linkage "static"
                    library "opencv9" linkage "static"
                    library "opencv10" linkage "static"
                    library "opencv11" linkage "static"
                    library "opencv12" linkage "static"
                    library "opencv13" linkage "static"
                    library "opencv14" linkage "static"
                    library "opencv15" linkage "static"
                }
            }
        }
    }

    android.buildTypes {
        release {
            minifyEnabled = false
        }
    }
}

dependencies {
    compile fileTree(dir: 'libs', include: ['*.jar'])
    testCompile 'junit:junit:4.12'
    compile 'com.android.support:appcompat-v7:23.1.1'
    compile 'com.android.support:design:23.1.1'
    compile project(':opencvandroid')
}

I'm basing my gradle file on this example that I found on github. Now, one of the issues that I discover trying to do so, is that OPENCV have a couple of static libraries files, so as this post says, I have to add each one on a different lib on gradle in order to link them, so that's why my gradle file is filled with repeating library definitions.

Now when I'm trying to include the headers files on my .CPP code, Android Studio cannot find them (not even the namespace to use). This is my CPP code:

#include <jni.h>
#include "opencv2/core/core.hpp"
#include "opencv2/imgproc/imgproc.hpp"
#include "opencv2/features2d/features2d.hpp"
#include <vector>

using namespace std;
using namespace cv;

extern "C" {
JNIEXPORT void JNICALL Java_com_example_Tutorial2Activity_FindFeatures(JNIEnv*, jobject, jlong addrGray, jlong addrRgba);

JNIEXPORT void JNICALL Java_com_example_Tutorial2Activity_FindFeatures(JNIEnv*, jobject, jlong addrGray, jlong addrRgba)
{
    Mat& mGr  = *(Mat*)addrGray;
    Mat& mRgb = *(Mat*)addrRgba;
    vector<KeyPoint> v;

    Ptr<FeatureDetector> detector = FastFeatureDetector::create(50);
    detector->detect(mGr, v);
    for( unsigned int i = 0; i < v.size(); i++ )
    {
        const KeyPoint& kp = v[i];
        circle(mRgb, Point(kp.pt.x, kp.pt.y), 10, Scalar(255,0,0,255));
    }
}
}

Am I missing something in order to link them correctly?

vicmns
  • 724
  • 1
  • 8
  • 20
  • 1
    You referenced the OpenCV static libraries, which is important to *link* your binary. But to *compile* your code, you must specify **cppFlags**. Why did you comment out that piece with `def jniPath` and on? – Alex Cohn Jan 10 '16 at 08:18
  • 1
    OMG you were right! I removed the cppFlags 'cause gradle couldn't read the configuration file... After some digging I found out that they remove the += for assignation and now you have tu use .add and .addAll (as an object), and now gradle can find all the headers references. It's been a while since I compiled a C++ project and forget about the flags. – vicmns Jan 11 '16 at 17:42
  • @AlexCohn What exactly should go in **cppFlags** variable? – IgorGanapolsky Jul 15 '16 at 21:23
  • 1
    @IgorGanapolsky: all flags you want to use in C++ compilation goes to **cppFlags**. WIth ndk-build, we use APP_CPPFLAGS, LOCAL_ CPPFLAGS, LOCAL_C_INCLUDES - here all these should go to **cppFlags**. Note that += does not work, use **.add** instead – Alex Cohn Jul 16 '16 at 05:42
  • @AlexCohn I used the following: `cppFlags.addAll(['-std=c++11', '-fexceptions', '-frtti', '-Wno-error=format-security'])`. However, I'm still getting **undefined reference** errors in my static libraries. – IgorGanapolsky Jul 18 '16 at 13:05
  • To resolve libraries, you need **ldLibs** – Alex Cohn Jul 18 '16 at 20:23

1 Answers1

3

There are dependencies between the OpenCV static libraries. So you need to include them in exactly the right order. .a and .o includes are order dependent in GCC, files that have dependencies must be included before the files they depend on:

CV_LIBS = \
    $(CV_LIB_FOLDER)/libopencv_calib3d.a \
    $(CV_LIB_FOLDER)/libopencv_highgui.a \
    $(CV_LIB_FOLDER)/libopencv_video.a \
    $(CV_LIB_FOLDER)/libopencv_objdetect.a \
    $(CV_LIB_FOLDER)/libopencv_imgproc.a \
    $(CV_LIB_FOLDER)/libopencv_imgcodecs.a \
    $(CV_LIB_FOLDER)/libopencv_core.a \
    $(CV_LIB_FOLDER)/libopencv_hal.a
griffin2000
  • 709
  • 9
  • 26