Android Studio 2.3利用CMAKE进行OpenCV 3.2的NDK开发

        Android Studio 2.2之后的版本加入了利用CMAKE配置编译NDK项目的方法,这也使得之前沿用多年通过Android.mk和Application.mk两个文件设置本地开发的老方法逐渐被取代。

        本文正是介绍如何在Android Studio 2.3上通过CMAKE的方法配置OpenCV 3.2的NDK开发。本文所给样例代码托管在github.com/johnhany/OpenCV-NDK

        如果想了解旧方法,请参考《Android Studio 2上利用NDK进行OpenCV 3.1开发》,该文章也介绍了用Exerimental Plugin 0.7.0进行NDK开发的方法。

        如果你的Android Studio刚刚更新到2.3.3,第一次建立项目进行自动编译时可能会遇到卡在Building *** Gradle project info不动的情况。这是因为Android Studio更新后会根据项目需要选择比较新的Gradle版本(比如在Android Studio 2.3上建立CMAKE NDK项目需要Gradle 3.3),并默认从官网下载Gradle,网络状况不太好时就会卡主。解决方法是手动在Gradle官网下载软件包:https://services.gradle.org/distributions/gradle-3.3-all.zip,解压在一个名为gradle-3.3的文件夹中,并将这个文件夹复制到C:\Program Files\Android\Android Studio\gradle。然后打开Android Studio的File -> Settings -> Build, Execution, Deployment -> Gradle,点击Use local gradle distribution并选择刚刚复制过去的目录。尽量不要勾选下面的Offline work,否则无法自动下载所需的Gradle Android Plugin等工具。


开发环境

        Windows 10 x64专业版

        Android Studio 2.3.3

        JDK 8u141

        Android 7.0 (API Level 24)

        OpenCV 3.2 Android SDK

        Android NDK r15b


准备工作

        1. 打开Android Studio 2.3.3,点击图标打开SDK Manager。在SDK Platforms选项卡中选择你所需要的Android版本,我选择的是Android 7.0。如果使用模拟器开发,推荐安装带有Google API的Intel x86镜像,因为可以利用Intel的HAXM技术为模拟器加速。HAXM路径在<Android SDK Path>\extras\intel\Hardware_Accelerated_Execution_Manager下。如果在实机上调试,则不需要安装镜像,只安装SDK Platform本身即可。下图中红色框标注的是必要的安装项。

        目前Android SDK已经去掉了原本带有图形界面的独立SDK Manager,所有对SDK包的管理都要经过Android Studio内File -> Settings -> Appearance & Behaviour -> System Settings -> Android SDK来进行管理,或者直接点击上面所示的图标。独立的SDK Manager可以通过<Android SDK Path>\tools\bin\sdkmanager.bat以命令行的方式进行操作。

        NDK也可以单独从官网下载,然后在项目根目录的local.properties文件中加入ndk.dir=<NDK Path>来使用下载好的NDK。不过这样每次新建项目都需要重新设置local.properties文件,而且NDK不能自动更新,所以推荐直接安装SDK Manager当中提供的NDK。NDK安装好后路径为<Android SDK Path>\ndk-bundle

        2. 建立新项目。创建新项目的过程可以直接参考官方文档。如果无法打开android.com,只需要了解建立NDK项目的过程与建立一般性Android项目的过程十分类似,只是要注意勾选Include C++ Support。其后的步骤完全采用默认设置即可。我将项目名称设置为“OpenCV-NDK”。

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

        3. Java层导入OpenCV。

注:如果你要编写一个“纯粹的”NDK应用,即不需要在Java代码中调用OpenCV的对象和函数,则可跳过这一步。后文给出的灰度处理的例子就属于这种,所有有关OpenCV的处理都放在C++代码中,在Java层面上只负责向我们的自定义Native库传递与接收图像数据。在文末给出的另一个调用摄像头的例子就需要首先用下面的方法引入OpenCV Java API。

        在http://opencv.org/releases.html下载OpenCV 3.2 Android SDK,解压到一个不限制读写权限的目录下,比如E:\dev-lib\OpenCV-android-sdk

        在Android Studio中点击File -> New… -> Import Module,然后在Source Directory中选取E:\dev-lib\OpenCV-android-sdk\sdk\java目录,这时Module Name就会自动变成“openCVLibrary320”。之后的步骤采用默认设置即可。

        刚刚导入OpenCV包之后,Android Studio会尝试自动编译,由于其默认的build.gradle文件设置并不适合最新版本,所以会报错。修改openCVLibrary320\build.gradle为如下内容就会纠正这些错误。

        点击File -> Project Structure,在左边的Modules中点击“app”,然后点击右边的加号,再选择Module Dependency,然后在弹出框中选择:openCVLibrary320。这样,就为我们的项目app在Java层上添加了OpenCV支持。


基本的OpenCV NDK应用

        即便是使用CMAKE来执行C++代码的编译,仍然存在若干变数。比如,如果需要在C++代码中include第三方库的头文件,那么这些头文件可以存在于项目路径之中,也可能存在于项目目录之外;在调用更复杂的库——如OpenCV——时,就需要直接调用第三方的预编译库,这些预编译库得调用又有三种方法:可以直接复制到项目目录下,或者采用软链接的方式避免复制,再或者通过修改配置文件直接调用位于项目目录之外的库文件。针对不同的情况,也许这些方法都有价值,所以在这一部分我会详细介绍头文件和库文件的几种不同调用方式。

        1. 与配置方法无关的文件。首先来修改一下基本的代码,这些文件的内容不受第三方库的不同调用方法所影响,所以先贴出来。

        app\src\main\java\net\johnhany\opencv_ndk\MainActivity.java的内容修改为:

        app\src\main\cpp\native_lib.cpp改为:

        app\src\main\res\layout\activity_main.xml修改为:

        app\src\main\res\values\strings.xml改为:

        检查一下app\src\main\AndroidManifest.xml是否为:

        还有项目根目录下的build.gralde是否为:

        另外,把一副尺寸不太大的图片复制到app\src\main\res\drawable中,并命名为“testpic1.jpg”,比如下图

        2. 与配置方法有关的文件。其实只有两个文件(确切地说,是这两个文件中的三行代码)稍有差异。

        (1) 头文件和库文件全部复制到项目中。

        把E:\dev-lib\OpenCV-android-sdk\sdk\native\jni\include文件夹复制到app\src\main\cpp当中,把E:\dev-lib\OpenCV-android-sdk\sdk\native\libs文件夹复制到app\src\main当中,并将文件夹重命名为jniLibs(也可以是其他名称,后文会有解释)。由于文件比较多,复制过程需要花些时间。

        app\build.gradle修改为:

        请注意第22行jniLibs.srcDirs = [‘src/main/jniLibs’]代表在src/main/jniLibs目录下的所需的库文件会被调用,并被一同打包进apk文件。如果在复制OpenCV的libs文件夹后没有重命名或者修改为其他名称,只需要把“jniLibs”改为正确的文件夹名称即可。

        app\CMakeLists.txt修改为:

        需要注意第5行include_directories(${CMAKE_SOURCE_DIR}/src/main/cpp/include)表示我们的C++代码是从src/main/cpp/include路径下调用头文件。第4行的${CMAKE_SOURCE_DIR}/src/main/jniLibs含义与上文相同。这里并不是重复定义库文件目录,而是为了另外定义一个变量ocvlibs,便于后面set_target_properties()当中定位到具体的库文件本身。

        这种方法由于需要将所有第三方库拷贝到项目目录下,使得一个很简单的OpenCV项目占据接近1G的硬盘空间,而且每个新项目都要再复制一遍,资源的利用率很低。不过其优点在于使用相对路径定义库目录,把项目拷贝到另一台机器上可以直接编译。

        (2) 用软链接代替文件的复制。

        为了避免每个项目都要进行复制,我们采用在要复制的目标文件夹创建软链接的方法,使得不需要修改前一种方法的任何文件,同时也不需要额外复制任何文件。

        这里以Windows系统为例。以管理员身份打开命令行提示符,输入:

        创建软链接(symlink或symbolic link)的格式为mklink /D [目标文件夹] [原始文件夹]。在创建之前要确认目标文件夹所在的目录没有同名的文件夹。

        app\build.gradleapp\CMakeLists.txt的内容与(1)相同。

        这种方法可以避免大量复制文件,而且也采用相对路径,不过需要额外创建软链接的步骤。

        (3) 用绝对路径代替相对路径。

        在(1)中的app\build.gradle文件,我们把第22行改为:

        在(1)中的app\CMakeLists.txt文件,把第4行改为:

        把第5行改为:

        此外,并不需要复制任何文件或者建立软链接。我个人倾向采用这种方法,虽然在更换开发环境时需要修改上面三个绝对路径,但还是比前两种方法方便多了!后两种方法的项目大小要小于第一种方法,接近700MB,但三种方法产生的apk文件是相同的,要60MB左右(因为同时包含了7种ABI的.so文件)。

        3. 运行效果


摄像头的例子

        我在github.com/leadrien/opencv_native_androidstudio的基础之上,针对目前的版本稍作修改,修改后的代码托管在github.com/johnhany/opencv_native_androidstudio。该程序调用OpenCV的Java层摄像头接口(需要在项目中导入OpenCV Java库),加入随机噪声并显示出来。运行效果如下:


参考

1. https://developer.android.com/studio/projects/add-native-code.html?hl=zh-cn

2. http://blog.csdn.net/martin20150405/article/details/53284442

3. http://www.jianshu.com/p/4e3c0c20c244

4. https://stackoverflow.com/q/42979633/3829845

5. https://stackoverflow.com/a/36040177/3829845

共有11条评论

  1. 您好!我配置了很多遍,但虽然运行成功,但opencv还是有error,这是什么原因吗?您有遇到这种情况吗?

  2. 您好!我配置了很多遍,但虽然运行成功,但opencv还是有error,这是什么原因吗?您有遇到这种情况吗?

  3. 博主,您好!我按照您这样配置方法做,虽然第一个例子成功,也没有报错。但它这个openCVLibrary320\scr\main\java\org\opencv\android\   里面的文件还是有error,这是什么原因。您自己平时的做项目有没有出现这种情况?我根据您的配置方法已经做了很多遍,还是有这个问题?

    1. 用压缩软件直接打开apk文件,看一下是缺少libnative_lib.so还是libopencv_java3.so呢?如果是前者说明C++代码编译失败,如果是后者就可能是app\build.gradle和app\CMakeLists.txt文件里的库目录没有设置正确。

发表评论

电子邮件地址不会被公开。 必填项已用*标注