Skip to content

Android Studio JNI层调用OpenCV进行图像处理

iPanda edited this page Mar 15, 2019 · 1 revision

This article was written on 2018-03-28,may have been out of date ,just for reference.


Android调用OpenCV的Android SDK进行计算机视觉相关的开发,可以调用JAVA层的函数,也可以直接调用JNI层的方法。前者需要在手机上另外安装Android Opencv Manager,并且apk体积也会增大,所以不推荐,后者可以只编译出需要用到的函数的.so库,apk只需要引用这个.so库,所以apk体积不会太大。所以推荐使用opencv的native方法进行jni编程并编译.so库。

本机环境

  • Win10_x64
  • Android Studio 3.1
  • OpenCV3.4.1

准备工作

  • 下载opencv3.4.1

相关配置

最开始,先配置添加外部工具,因为JNI编程需要经常用到,每次cd到目录然后手打javah等命令是一件很没有效率的事情,在文件->设置->工具->外部工具中点击+号新建External Tools

我们需要添加3个工具

  • Name: javah(SDK23)
  • Program: JDKPath\bin\javah.exe
  • Arguments: -d ModuleFileDir\src\main\jni -classpath D:\ProgramData\Android\SDK\platforms\android-23\android.jar;. FileClass
  • Working directory: ModuleFileDir\src\main\java
  • Name: ndk-build(module)
  • Program: D:\ProgramData\Android\SDK\ndk-bundle\build\ndk-build.cmd
  • Working directory: ProjectFileDir\ModuleName\src\main
  • Name: ndk-build clean(module) 在ndk-build(module)基础上增加一个clean参数即可
  • Program: D:\ProgramData\Android\SDK\ndk-bundle\build\ndk-build.cmd
  • Arguments: clean
  • Working directory: ProjectFileDir\ModuleName\src\main

以后只要在mudule目录中右键然后找到External Tools,点击即可使用这些命令。


一个示例

  • 为了便于扩展,我们创建一个新的module,你可以取名为opencv3,我自己原先取的ImageProcLib举例就不改了。
  • 首先在工程中创建ImageProcLib module,里面包含一个工具类ImageProcUtils,这样,我们在app module中的Activity中就可以调用ImageProcLib module中的工具类中的本地方法。 ImageProcUtils示例:
package cn.gloud.imageproclib;
import android.graphics.Bitmap;
import android.util.Log;

public class ImageProcUtils {
    private static final String TAG = "ImageProcUtils";
    static {
        //导入生成的链接库文件
        System.loadLibrary("ImageProcUtils");
    }

    public boolean ImageMatchTempl(int x, int y, Bitmap imageBitmap, Bitmap templBitmap, int threshold) {
        return ImageMatchTemplate(x,y,imageBitmap,templBitmap,threshold);
    }
    //本地方法,模板匹配
    private native boolean ImageMatchTemplate(int x, int y, Bitmap imageBitmap, Bitmap templBitmap, int threshold);
}
  • 在module的 src\main下创建一个文件夹命名为jni
  • 因为我们不需要java层函数,所以只复制...\OpenCV-android-sdk\sdk下的native文件夹到刚才创建的jni目录下即可
  • 配置build.gradle,下面是module的build.gradle文件的模板
apply plugin: 'com.android.library'
android {
    ...
    defaultConfig {
        ...
        //配置NDK信息
        ndk {
            moduleName "ImageProcUtils"
            ldLibs "log", "jnigraphics", "m", "z"
            abiFilters "armeabi-v7a","x86" //,"x86"等架构
        }
        //配置SO文件存放路径
        sourceSets {
            main {
                jni.srcDirs = []
                jniLibs.srcDirs = ['src/main/libs']
            }
        }
    }
    //设置构建脚本路径,会自动索引同目录下的Application.mk
    externalNativeBuild {
        ndkBuild {
            path 'src/main/jni/Android.mk'
        }
    }
    //如果报错More than one file was found with OS independent path时添加
    packagingOptions {
        pickFirst 'lib/armeabi-v7a/libImageProcUtils.so'
    }
    ...
}
dependencies {
    implementation fileTree(dir: 'libs', include: ['*.jar'])
    implementation 'com.android.support:appcompat-v7:23.4.0'
    ...
}
  • 利用javah命令编译ImageProcUtils的头文件于jni目录下,例如cn_gloud_imageproclib_ImageProcUtils.h,如果你配置好了外部工具,只需要右键ImageProcUtils,在External Tools中点击javah工具即可,
  • 复制cn_gloud_imageproclib_ImageProcUtils.h到同目录下修改后缀名为.cpp,例如cn_gloud_imageproclib_ImageProcUtils.cpp .cpp需要进一步修改,示例:
#include <cn_gloud_imageproclib_ImageProcUtils.h>
#include <android/bitmap.h>
#include <android/log.h>
#include <opencv2/opencv.hpp>

#ifndef LOG
#define TAG "ImageMatchTemplate"
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO,TAG ,__VA_ARGS__) // 定义LOGI类型
#endif

/*
 * Class:     cn_gloud_imageproclib_ImageProcUtils
 * Method:    ImageMatchTemplate
 * Signature: (IILandroid/graphics/Bitmap;Landroid/graphics/Bitmap;I)Z
 */
JNIEXPORT jboolean JNICALL Java_cn_gloud_imageproclib_ImageProcUtils_ImageMatchTemplate
        (JNIEnv *env, jobject obj, jint x, jint y, jobject imageBitmap, jobject templBitmap,jint threshold){
}
  • jni目录下创建Android.mk和Application.mk文件 Android.mk:
LOCAL_PATH:=(call my-dir)  include (CLEAR_VARS)
OPENCV_LIB_TYPE :=STATIC #利用静态库编译

ifeq ("(wildcard (OPENCV_MK_PATH))","")
include (LOCAL_PATH)/native/jni/OpenCV.mk else include (OPENCV_MK_PATH)
endif

LOCAL_MODULE := ImageProcUtils #和System.loadLibrary("ImageProcUtils");中指定的一样
LOCAL_LDLIBS += -llog -ljnigraphics #相关依赖库
LOCAL_SRC_FILES := cn_gloud_imageproclib_ImageProcUtils.cpp #源文件
include (BUILD_SHARED_LIBRARY) #编译动态库  #LOCAL_SRC_FILES := libImageProcUtils.so #预编译则指定so库 #include (PREBUILT_SHARED_LIBRARY) #预编译动态库

Application.mk:

APP_STL := gnustl_static #STLAPP_CPPFLAGS := -frtti -fexceptions #编译参数
APP_PLATFORM := android-21 #android平台
APP_ABI += armeabi-v7a x86 #cpu架构
  • 右键jni目录点击外部工具的ndk-build(module)就可以编译生成工具类需要的.so库了,.so库会生成在jni目录下的libs目录中,目录在build.gradle中指定
  • app module引用ImageProcLib module,修改app的build.gradle
dependencies {
    ...
    implementation project(path: ':ImageProcLib')
}
  • 修改工程的settings.gradle,
include ':app', ':ImageProcLib'