• OpenCV


      我在http://www.cnblogs.com/fx-blog/p/8206737.html一文中提到了如何在Android Studio中Java层导入OpenCV(包含opencv_contrib部分),但是这仅仅是Java层的导入,随着学习的深入,我们可以渐渐的发现OpenCV库对Java的支持不是很给力,比如我使用SIFT算法时,一般提取出来的特征点有一万多个,这其中包含了大量的无效特征点,如果我想指定特征点的数目,比如说500个(经过测试,Java中利用OpenCV提取特征点默认为500),但是OpenCV Java库中并没有提供这样的方法(或者是我没有发现,如果有大神知晓,还望告知)。但是在C++中,是可以给SIFT指定特征点的,这里面就需要有一个在Java中调用C++的功能,Java中我们知道可以利用JNI或者JNA解决,那么在Androd中我们便可以利用NDK做到Java对C++的调用。

      在Android Studio 2.2之前,我们通常是通过Android.mk和Application.mk两个文件设置本地开发;但是在Android Studio 2.2之后的版本,加入了利用CMAKE配置编译NDK项目的方法,这无疑是一个很好的消息,我们终于可以抛弃之前那种繁琐的方法啦,本篇文章只要讲的就是在Android Studio中利用CMAKE进行OpenCV的NDK开发。

    准备工作:

      首先,我们需要在Android Studio中配置CMAKE、NDK工具,打开Android Studio 2.2,点击按钮打开SDK Manager,在SDK Platforms中选择你所需要的Android版本,这里我使用的是Android 7.0。

        

       在SDK Tools中选择红框标出的部分(这里推荐SDK Manager当中提供的NDK,NDK安装好后路径为<Android SDK Path> dk-bundle):

        

    正式开始:

      创建一个新项目,在创建的过程中,我们需要勾选Include C++ Support,之后的步骤默认即可,可以与http://www.cnblogs.com/fx-blog/p/8206737.html相应正,写的不是很好,大家见谅。

      项目创建成功之后,会自动在appsrcmain下建立一个名为cpp的文件夹,其中包含一个native-lib.cpp文件。同时,在app目录下会多出一个CMakeLists.txt文件,Android Studio调用CMAKE利用该文件来协调C++代码的编译(默认使用Clang编译),并将产生的.so文件提供给apk文件的打包过程。

      然后需要在Java层导入OpenCV,OpenCV 3.2 Android SDK可以选用之前编译好的库,下载地址:https://pan.baidu.com/s/1kVOejLt;在Android Studio中点击File -> New… -> Import Module,然后在Source Directory中选取<OpenCV 3.2 Android SDK>sdkjava目录,这时Module Name就会自动变成“openCVLibrary320”,之后的步骤采用默认设置即可。

      刚刚导入OpenCV包之后,Android Studio会尝试自动编译,由于其默认的build.gradle文件设置并不适合最新版本,所以会报错。修改openCVLibrary320uild.gradle为如下内容就会纠正这些错误(红框标出的部分需要与appuild.gradle一致)。

        

       点击File -> Project Structure,在左边的Modules中点击“app”,然后点击右边的加号,再选择Module Dependency,然后在弹出框中选择:openCVLibrary320。这样,就为我们的项目app在Java层上添加了OpenCV支持(其中library是我使用的另外一个库,不用理会)。

        

    接下来就是配置利用CMAKE配置OpenCV应用了:

      首先,需要将应用OpenCV C++所需要头文件和库文件全部复制到项目中,将<OpenCV 3.2 Android SDK>sdk ativejniinclude文件夹复制到appsrcmaincpp当中,把<OpenCV 3.2 Android SDK>sdk ativelibs文件夹复制到appsrcmain当中,并将文件夹重命名为jniLibs。

        

      然后将 appuild.gradle修改为(各位看官可以对比修改):

    apply plugin: 'com.android.application'
    
    android {
        compileSdkVersion 25
        buildToolsVersion "27.0.1"
        defaultConfig {
            applicationId "com.example.demo02"
            minSdkVersion 15
            targetSdkVersion 25
            versionCode 1
            versionName "1.0"
            testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
            externalNativeBuild {
                cmake {
                    cppFlags "-std=c++11", "-frtti", "-fexceptions"
                    abiFilters 'x86', 'x86_64', 'armeabi', 'armeabi-v7a', 'arm64-v8a', 'mips', 'mips64'
                }
            }
        }
        sourceSets {
            main {
                jniLibs.srcDirs = ['src/main/jniLibs']
            }
        }
        buildTypes {
            release {
                minifyEnabled false
                proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
            }
        }
        externalNativeBuild {
            cmake {
                path "CMakeLists.txt"
            }
        }
    }
    
    dependencies {
        compile 'com.android.support:design:25.3.1'
        compile fileTree(include: ['*.jar'], dir: 'libs')
        androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', {
            exclude group: 'com.android.support', module: 'support-annotations'
        })
        compile 'com.android.support:appcompat-v7:25.3.1'
        testCompile 'junit:junit:4.12'
        compile project(':openCVLibrary320')
    }

      appCMakeLists.txt修改为:

    cmake_minimum_required(VERSION 3.4.1)
    
    set(CMAKE_VERBOSE_MAKEFILE on)
    set(ocvlibs "${CMAKE_SOURCE_DIR}/src/main/jniLibs")
    include_directories(${CMAKE_SOURCE_DIR}/src/main/cpp/include)
    
    add_library(libopencv_java3 SHARED IMPORTED )
    set_target_properties(libopencv_java3 PROPERTIES
                          IMPORTED_LOCATION "${ocvlibs}/${ANDROID_ABI}/libopencv_java3.so")
    
    add_library( # Sets the name of the library.
                 native-lib
    
                 # Sets the library as a shared library.
                 SHARED
    
                 # Provides a relative path to your source file(s).
                 src/main/cpp/native-lib.cpp )
    
    find_library( # Sets the name of the path variable.
                  log-lib
    
                  # Specifies the name of the NDK library that
                  # you want CMake to locate.
                  log )
    
    target_link_libraries( # Specifies the target library.
                           native-lib android log libopencv_java3
    
                           # Links the target library to the log library
                           # included in the NDK.
                           ${log-lib} )

      配置部分就此完成了,贴一下关键部分的代码吧,NDK帮助类OpenCVNDKHelper:

    1 package com.example.ndk;
    2 
    3 public class OpenCVNDKHelper {
    4     static {
    5         System.loadLibrary("native-lib");
    6     }
    7     public native static void detectFeatures(long srcMatAddr, long dstMatAddr);
    8 }

      C++文件native-lib.cpp:

     1 #include <jni.h>
     2 #include <string>
     3 #include <opencv2/core/core.hpp>
     4 #include <opencv2/features2d/features2d.hpp>
     5 #include <opencv2/xfeatures2d/nonfree.hpp>
     6 
     7 using namespace std;
     8 using namespace cv;
     9 using namespace xfeatures2d;
    10 
    11 extern "C"
    12 {
    13     JNIEXPORT void JNICALL Java_com_example_ndk_OpenCVNDKHelper_detectFeatures
    14         (JNIEnv *, jclass, jlong srcMatAddr, jlong dstMatAddr) {
    15         Mat* srcMat = (Mat*)srcMatAddr;
    16         Mat* descriptors = (Mat*)dstMatAddr;
    17         vector<KeyPoint> Keypoints;
    18         Ptr<SIFT> detector = SIFT::create(1000);
    19         detector->detect(*srcMat, Keypoints);
    20         detector->compute(*srcMat, Keypoints, *descriptors);
    21     }
    22 }

      这段代码主要的作用就是提取Sift特征(这里我就不介绍生成.h文件的过程了,只要掌握.cpp文件中的函数命名的方法,这个过程是可以省略的啦)。

      补充一点:这个代码其实也解释了如何在Java中将Mat传递到C++中的方法,在java中的调用如下(src和srcMat为Mat对象):

    OpenCVNDKHelper.detectFeatures(src.getNativeObjAddr(), srcMat.getNativeObjAddr());

      PS:文章中使用的jniLibs是直接拷贝到目录中的,这个项目的存储空间就比较大了,大约1G左右,每次新建项目都需要重新拷贝,大家也可以用软链接或者绝对路径代替,这里就不多介绍啦。但是最终生成的APK文件大小是差不多的。

      over~~~

    跑下题:

      写博客的过程中,老爸忽然找我视频聊天,我们聊了蛮多,主要就是催我找个女朋友,难道95后也要被家长催找女朋友了吗,感觉从我6月份毕业以来(可能更早),这个话题就没有停过啊,脑补过年回家的场面。。。

  • 相关阅读:
    J2ME学习笔记之问题看法
    单词王(kingWord)
    TCP的粘包和拆包问题及解决办法(C#)
    vuex里mapState,mapGetters使用详解
    agelform formcreate 使 elementui form 组件更简单
    elementui 二次封装系列 button
    开源一套后台管理系统框架,远离996 记录
    客户端架构介绍
    简单的Unity学习项目,封装了一下简单、通用功能组件,适用于数据可视化展示
    Vue关于对象数组的双向数据绑定(props与watch)
  • 原文地址:https://www.cnblogs.com/fx-blog/p/8215331.html
Copyright © 2020-2023  润新知