[Android Studio] Using NDK to call OpenCV

NDK才是Android开发通向超高薪之路。(这句话,似乎四年前有云)

难点在于常用的non-free module (sift and surf)

 

unsw@unsw-UX303UB$ pwd
/home/unsw/Android/5-android/NDKTest/app/src/main

unsw@unsw-UX303UB$ javah -d jni -classpath ../../build/intermediates/classes/debug com.example.unsw.ndktest.NativeClass

 

生成了jni目录, 并且有了一个.h文件。


 

 jni目录下添加 

1. 通过.h文件复制粘贴出个.c文件,这样少输入这么长的名字。

 

2. 然后,把.h的内容改为.c需要的形式。

/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class com_example_unsw_ndktest_NativeClass */

#ifndef _Included_com_example_unsw_ndktest_NativeClass
#define _Included_com_example_unsw_ndktest_NativeClass
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     com_example_unsw_ndktest_NativeClass
 * Method:    getMessageFromJNI
 * Signature: ()Ljava/lang/String;
 */
JNIEXPORT jstring JNICALL Java_com_example_unsw_ndktest_NativeClass_getMessageFromJNI
  (JNIEnv *, jclass);
// 改为如下所示:(其他删除)
  #include <com_example_unsw_ndktest_NativeClass.h>
  JNIEXPORT jstring JNICALL Java_com_example_unsw_ndktest_NativeClass_getMessageFromJNI
    (JNIEnv *env, jclass obj) {
    return env->NewStringUTF("This is message from JNI");
    }

#ifdef __cplusplus
}
#endif
#endif

 

3. 添加到此处后,可以尝试编译下,应该不会报错。

gradle.properties添加:
  android.useDeprecatedNdk=true

 

4. 继续添加.mk文件:

Android.mk

LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)

LOCAL_SRC_FILES := com_example_unsw_ndktest_NativeClass.cpp

LOCAL_LDLIBS += -llog
LOCAL_MODULE := MyLib


include $(BUILD_SHARED_LIBRARY)

Application.mk

APP_STL := gnustl_static
APP_CPPFLAGS := -frtti -fexceptions
APP_ABI := armeabi-v7a  //如果是本地模拟器,则为x86
APP_PLATFORM := android-23

 

5. Compile, generate *.so

 

6. Just use your baby function.

public class MainActivity extends AppCompatActivity {

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

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        ((TextView) findViewById(R.id.textView)).setText(NativeClass.getMessageFromJNI());
    }
}

 

 

 

若c++调用的是OpenCV lib,如下示范 


  

/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
#include <stdio.h>
#include <opencv2/opencv.hpp>
#include "opencv2/nonfree/nonfree.hpp"

using namespace cv;
using namespace std;
/* Header for class com_example_unsw_jeff_OpencvNativeClass */

#ifndef _Included_com_example_unsw_jeff_OpencvNativeClass
#define _Included_com_example_unsw_jeff_OpencvNativeClass
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     com_example_unsw_jeff_OpencvNativeClass
 * Method:    convertGray
 * Signature: (JJ)I
 */
int toGray(Mat img, Mat& gray);

JNIEXPORT jint JNICALL Java_com_example_unsw_jeff_OpencvNativeClass_convertGray
  (JNIEnv *, jclass, jlong, jlong);

#ifdef __cplusplus
}
#endif
#endif

 

  

但要注意一个问题:Nonfree Module (SIFT, SURF) 


参见如下:

OpenCV & Android

Kornel的总结:(opencv 2.4.10)

http://stackoverflow.com/questions/28510045/undefined-reference-to-cvinitmodule-nonfree-in-android
直接使用现成lib的tutor: https://www.youtube.com/watch?v=cLK9CjQ-pNI
作者代码隐藏在了这里:https://dmigit.uqtr.ca/u/nguidjol

 

1. 工程下新建文件夹 libraries/,并拷贝过来 java文件夹 in opencv-for-android。

unsw@unsw-UX303UB$ cp ./OpenCV-2.4.9-android-sdk/sdk/
etc/    java/   native/

Copy "java" to libraries and change its name to "opencv"

unsw@unsw-UX303UB$ pwd
/home/unsw/Programmer/5-android/nonfree_model

unsw@unsw-UX303UB$ ls
app           gradle             gradlew.bat       nonfree_model.iml
build         gradle.properties  libraries         settings.gradle
build.gradle  gradlew            local.properties

unsw@unsw-UX303UB$ ls libraries/
libraries.iml  opencv

效果如下:

 

2. opencv文件夹下 create build.gradle.

apply plugin: 'com.android.library'

buildscript {
    repositories {
        mavenCentral()
    }
    dependencies {
        classpath 'com.android.tools.build:gradle:2.2.3'
    }
}

android {
    compileSdkVersion 23
    buildToolsVersion "23.0.2"

    defaultConfig {
        minSdkVersion 23
        targetSdkVersion 23
        versionCode 1
        versionName "1.0"
    }

    sourceSets {
        main {
            manifest.srcFile 'AndroidManifest.xml'
            java.srcDirs = ['src']
            resources.srcDirs = ['src']
            res.srcDirs = ['res']
            aidl.srcDirs = ['src']
        }
    }
}
build.gradle
 

3. settings.gradle添加库

include ':app'
include ':libraries:opencv'  //add.

之后,Project structure便可加载libraries:opencv 模块。

 

4. 添加sift操作java文件

package com.example.unsw.nonfree_model;

import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.media.FaceDetector;
import android.net.Uri;
import android.os.Bundle;
import android.provider.MediaStore;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.VideoView;

import org.opencv.android.Utils;
import org.opencv.core.Mat;
import org.opencv.core.MatOfKeyPoint;
import org.opencv.features2d.FeatureDetector;
import org.opencv.features2d.Features2d;
import org.opencv.imgproc.Imgproc;

public class MainActivity extends AppCompatActivity {

    static {
        System.loadLibrary("opencv_java");
        System.loadLibrary("nonfree");
    }

    private ImageView imageView;
    private Bitmap inputImage; // make bitmap from image resource
    private FeatureDetector detector = FeatureDetector.create(FeatureDetector.SIFT);

    @Override
    protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState); inputImage = BitmapFactory.decodeResource(getResources(), R.drawable.test); setContentView(R.layout.activity_main); imageView = (ImageView) this.findViewById(R.id.imageView); sift(); } public void onResume(){ super.onResume(); } public void sift(){ Mat rgba = new Mat(); Utils.bitmapToMat(inputImage,rgba); MatOfKeyPoint keyPoints = new MatOfKeyPoint(); Imgproc.cvtColor(rgba, rgba, Imgproc.COLOR_RGBA2GRAY); detector.detect(rgba, keyPoints); Features2d.drawKeypoints(rgba, keyPoints, rgba); Utils.matToBitmap(rgba, inputImage); imageView.setImageBitmap(inputImage); } }

以及相对应的 layout。

 

5. 添加现成的 .so 

新建jniLibs,给jniLibs各个目录内添加.so。

 

效果图:(可见sift的特征点)

 

  


 -- 如何生成自己的 .so --

 

  (1)为使OpenCV-2.4.9-android-sdk完整,添加了OpenCV-2.4.9的nonfree文件夹。

unsw@unsw-UX303UB$ ls opencv-2.4.9/modules/nonfree/include/opencv2/nonfree/
features2d.hpp  gpu.hpp         nonfree.hpp     ocl.hpp
unsw@unsw-UX303UB$ ls OpenCV-2.4.9-android-sdk/sdk/native/jni/include/opencv2/nonfree/
features2d.hpp  nonfree.hpp

  

(2)新工程:MyApplication下创建文件夹:nonfree_opencv_android/jni, 其下也添加OpenCV-2.4.9modules/nonfree/src/* 的内容。

unsw@unsw-UX303UB$ pwd
/home/unsw/Programmer/5-android/MyApplication/nonfree_opencv_android

unsw@unsw-UX303UB$ ls
jni  libs  obj    //ndk-build后生成的。

// jni 添加以下内容: unsw@unsw-UX303UB$ ls ~/Android/opencv-2.4.9/modules/nonfree/src/
cuda/ opencl/ sift.cpp surf_gpu.cpp
nonfree_init.cpp precomp.hpp surf.cpp surf_ocl.cpp

修改文件里的内容,按照报错提示去掉ocl相关的部分即可。

unsw@unsw-UX303UB$ ~/Android/Sdk/ndk-bundle/ndk-build 
[x86] Compile++      : nonfree <= nonfree_init.cpp
In file included from jni/nonfree_init.cpp:43:
jni/precomp.hpp:46:10: fatal error: 'cvconfig.h' file not found
#include "cvconfig.h"
         ^
1 error generated.
make: *** [obj/local/x86/objs/nonfree/nonfree_init.o] Error 1

unsw@unsw-UX303UB$ ~/Android/Sdk/ndk-bundle/ndk-build 
[x86] Compile++      : nonfree <= nonfree_init.cpp
jni/nonfree_init.cpp:69:19: error: unknown type name 'SURF_OCL'
CV_INIT_ALGORITHM(SURF_OCL, "Feature2D.SURF_OCL",
                  ^
jni/nonfree_init.cpp:69:19: error: use of undeclared identifier 'SURF_OCL'
2 errors generated.
make: *** [obj/local/x86/objs/nonfree/nonfree_init.o] Error 1
Error处理

 

(3)再添加Android.mk和Application.mk,编译即可。

APP_ABI := x86
APP_STL := gnustl_static
APP_CPPFLAGS := -frtti -fexceptions
APP_PLATFORM := android-23
Application.mk
LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
OPENCV_INSTALL_MODULES:=on
OPENCV_CAMERA_MODULES:=off
include /home/unsw/Android/OpenCV-2.4.9-android-sdk/sdk/native/jni/OpenCV.mk

LOCAL_C_INCLUDES:= /home/unsw/Android/OpenCV-2.4.9-android-sdk/sdk/native/jni/include
LOCAL_MODULE    := nonfree
LOCAL_CFLAGS    := -Werror -O3 -ffast-math
LOCAL_LDLIBS    += -llog

LOCAL_SRC_FILES := nonfree_init.cpp \
 sift.cpp \
 surf.cpp
include $(BUILD_SHARED_LIBRARY)
Android.mk

Compile:

unsw@unsw-UX303UB$ ~/Android/Sdk/ndk-bundle/ndk-build 
[x86] Compile++      : nonfree <= nonfree_init.cpp
[x86] Compile++      : nonfree <= sift.cpp
[x86] Compile++      : nonfree <= surf.cpp
[x86] Prebuilt       : libopencv_java.so <= /home/unsw/Android/OpenCV-2.4.9-android-sdk/sdk/native/jni/../libs/x86/
[x86] SharedLibrary  : libnonfree.so
[x86] Install        : libnonfree.so => libs/x86/libnonfree.so
[x86] Install        : libopencv_java.so => libs/x86/libopencv_java.so

  Success! 

posted @ 2017-02-02 18:05  郝壹贰叁  阅读(1691)  评论(1编辑  收藏  举报