Ubuntu 16.04下为Android编译OpenCV 3.2.0 Manager

学向勤中得,萤窗万卷书。这篇文章主要讲述Ubuntu 16.04下为Android编译OpenCV 3.2.0 Manager相关的知识,希望能为你提供帮助。
http://johnhany.net/2016/07/build-opencv-manager-for-android-on-ubuntu/
    最近想在Android上尝试一下SIFT和SURF匹配算法,但考虑到这些算法都是专利保护的,并没有被包含在预编译库中,所以还需要自己来动手编译OpenCV Android SDK。在OpenCV 2.4.x版本中,这些算法被包含在nonfree模块中;从3.0版本开始,用于图像特征匹配的一些算法(比如SIFT,SURF,BRIEF,FREAK等)被转移到了opencv_contrib项目的xfeatures2d模块中。在PC环境下,为C++编译opencv+contrib库是很方便的,不过为Android平台的编译过程就有些麻烦了。
        我在编译的过程中也遇到了一些问题,在网络上搜索相应解决方案时也发现很多人都在尝试为OpenCV Android SDK添加contrib(nonfree)支持,某些遇到难以解决的问题最后只能绕路而行,比如这里和这里。我找到相应解决办法后回应了他们,但愿对别人会有所帮助吧!
        本文要介绍的内容就是如何在Ubuntu 16.04环境下利用最新的OpenCV和opencv_contrib为Android平台编译一个可用的OpenCV Android SDK,并生成相应的OpenCV Manager。
2017.08.03更新:
          更新到OpenCV 3.2,Android SDK API 24,Android NDK r15b。
          我原本挣扎了三天尝试在Windows平台通过MinGW来编译最新版本的OpenCV Android SDK,但在生成Manager的apk时遇到很棘手的问题,只好作罢转回Linux进行编译。不过读者放心,在Linux下编译得到的OpenCV Android SDK也可在Windows下直接使用,另外我在文章结尾添加了我编译好的SDK下载链接。
        我的开发环境如下:
                Ubuntu 16.04 LTS (x64)
                GCC 5.4.0
                CMAKE 3.9.0
                OpenCV 3.2.0
                Android SDK API 24(Android 7.0)
                Android SDK Tools r25.2.2
                Androird NDK r15b
                python 3.5
          虽然目前Android SDK Tools已经更新到r26,但由于其相对原来的工具变化较大,为了稳妥起见,建议手动降级到r25版本(下载地址:https://dl.google.com/android/repository/tools_r25.2.2-linux.zip)
        当然在编译过程中还会用到很多其他工具和软件包,比如Ninja,Ant等,我会在文中需要的时候给出它们的版本和安装方法。
        关于GCC,CMAKE和Python的安装过程本文就不详述了,一是因为这些工具的安装过程很容易,网络上的介绍都很详细,二是因为之前刚刚配置好TensorFlow,安装了一大堆软件包,我也不清楚本文的内容究竟会涉及哪些依赖库,我会尽量把我在编译过程中所需的工具和库标记出来。
        同样,Android SDK和NDK的安装方法这里也不会作过多介绍。我是通过Android Studio 2.1.2提供的SDK Manager下载的Android SDK和Android NDK,在Android Studio中下载Android SDK和Android NDK的方法可以参考《Android Studio 2上利用NDK进行OpenCV 3.1开发》,虽然开发平台不同,但在软件界面中的操作过程是相似的。我的Android SDK路径是/home/john/Android/Sdk,Android NDK的路径是/home/john/Android/Sdk/ndk-bundle。
下载源码
        在opencv的master分支Release页面下载opencv 3.2源码,解压到一个有读写权限的目录下,比如/home/john/Downloads/opencv-master。在这里下载对应3.2的opencv-contrib源码,解压到某个目录下,个人习惯是解压到OpenCV的源码根目录下,使得opencv-contrib/modules目录的路径如/home/john/Downloads/opencv-master/opencv_contrib/modules,这个路径后面会用到。

个人推荐在编译其他OpenCV版本甚至编译其他软件包时要下载有版本号的Release版,而不是直接git下来最新的master版。毕竟master一般是正在开发修缮的版本,即便是整合了功能强大的CI(比如OpenCV在用的buildbot,或者其他开源库喜欢用的travis-ci)也不能保证其测试过程考虑到了大多数应用环境。
        如果你的机器上已经有了一份opencv源码,但不想花时间去下载最新版本,至少要保证你的opencv源码是在16年5月份之后下载的(具体来讲,至少要包含该Pull Request:Added –extra_modules_path to build_sdk.py  #6460,这样你的build_sdk.py才会包含后面要用到的extra_modules_path)。
        仅仅下载好源码还不够,还要对源码做一些修改,具体如下:
          1.把/home/john/Downloads/opencv-master/opencv_contrib/modules/xfeatures2d/include/opencv2目录下的xfeatures2d.hpp文件和xfeatures2d文件夹复制到/home/john/Downloads/opencv-master/modules/features2d/include/opencv2目录下。此时,该目录包含的文件应该如下图所示:
Ubuntu 16.04下为Android编译OpenCV 3.2.0 Manager

文章图片
          2.找到/home/john/Downloads/opencv-master/modules/features2d/misc/java/src/cpp目录,打开features2d_manual.hpp文件,做以下修改:
        在第8行增加一个头文件opencv2/xfeatures2d.hpp,即把
 
   
1 2 3 #ifdef HAVE_OPENCV_FEATURES2D #include "opencv2/features2d.hpp" #include "features2d_converters.hpp"
 
改为
 
   
1 2 3 4 #ifdef HAVE_OPENCV_FEATURES2D #include "opencv2/features2d.hpp" #include "opencv2/xfeatures2d.hpp" #include "features2d_converters.hpp"
 
        把第120-125行的
 
   
1 2 3 4 5 6                 //case SIFT:                 //        name = name + "SIFT";                 //        break;                 //case SURF:                 //        name = name + "SURF";                 //        break;
 
改为
 
   
1 2 3 4 5 6                 case SIFT:                         fd = xfeatures2d::SIFT::create();                         break;                 case SURF:                         fd = xfeatures2d::SURF::create();                         break;
 
【Ubuntu 16.04下为Android编译OpenCV 3.2.0 Manager】        把第239-245行的
 
   
1 2 3 4 5 6                 //case SIFT:                 //        name = name + "SIFT";                 //        break;                 //case SURF:                 //        name = name + "SURF";                 //        break;
 
改为
 
   
1 2 3 4 5 6                 case SIFT:                         de = xfeatures2d::SIFT::create();                         break;                 case SURF:                         de = xfeatures2d::SURF::create();                         break;
 
        把第254-256行的
 
   
1 2 3                 //case FREAK:                 //        name = name + "FREAK";                 //        break;
 
改为
 
   
1 2 3                 case FREAK:                         de = xfeatures2d::FREAK::create();                         break;
 
编译OpenCV Android SDK
        找到/home/john/Downloads/opencv-master/platforms/android目录,在该目录下会发现build_sdk.py文件,这个文件就是用来编译OpenCV Android SDK的Python脚本文件。
        首先做一些检查工作:
          1.在第78行的ABIs定义了需要编译的ABI目标以及相应的NDK工具链。保险起见,可以先在/home/john/Android/Sdk/ndk-bundle/toolchains中查看一下你的NDK工具链版本,如果版本号有所不同,就要修改build_sdk.py中对应ABI工具链的版本,如下图所示:
Ubuntu 16.04下为Android编译OpenCV 3.2.0 Manager

文章图片
          mips和mips64两行被注释掉的原因是,在我的机器上编译时,由于依赖库之一Google Protobuf的编译错误,无法为这两个架构成功编译OpenCV Android SDK。不过我手边并没有采用该架构的Android设备,而且我暂时还不需要考虑短期内编写的代码有太好的兼容性,所以干脆放弃对这两种架构进行编译。如果感兴趣,你可以试着为mips编译一下:)目前在编译OpenCV 3.2版本时,所有ABI都能够成功编译(包括:arm64-v8a,armeabi,armeabi-v7a,mips,mips64,x86,x86_64)。上图并没有相应更新,还请见谅!
          2.安装Ninja。打开终端,输入:
 
   
1 sudo apt-get install ninja-build
 
        另外,我在编译之前参考这里又安装了几个用于交叉编译的库,不确定是否必要,但还是贴出来吧:
 
   
1 sudo apt-get install gcc-4.9-arm-linux-gnueabihf
 
        其中版本号4.9根据你的NDK工具链的版本来修改(工具链路径在android-sdk\ndk-bundle\toolchains当中)。
          3.安装Ant。在终端输入:
 
   
1 sudo apt-get install ant
 
        如果不安装Ant,在编译过程中CMAKE关于Java的输出信息全是NO,而且后面还会出现unknown target ‘opencv_engine‘的错误。
          4.在build_sdk.py里还有很多可以根据需要来修改的地方,比如-DANDROID_NATIVE_API_LEVEL可以适当地提高,可以选择开启-DBUILD_EXAMPLES和-DBUILD_TESTS等等。这里为了演示的目的,除了前文提到的,其他地方全部保持默认,不做修改。
          5.在终端中输入:
 
   
1 python /home/john/Downloads/opencv-master/platforms/android/build_sdk.py /home/john/Downloads/opencv-master/build /home/john/Downloads/opencv-master --sdk_path=/home/john/Android/Sdk --ndk_path=/home/john/Android/Sdk/ndk-bundle --extra_modules_path=/home/john/Downloads/opencv-master/opencv_contrib/modules
 
        注意命令是一行的,中间没有换行符。
        根据build_sdk.py文件中第252行的说明,其采用的参数按照以下格式:
 
   
1 python build_sdk.py < output-dir> < opencv-src-dir> --sdk_path=< android-sdk-path> --ndk_path=< ndk-path> --extra_modules_path=< opencv-contrib-path>
 
        其中,< output-dir> 是保存编译中间结果和最终结果的目录(也叫工作目录),< android-sdk-path> 指向OpenCV源码目录,< android-sdk-path> 为Android SDK目录,< ndk-path> 为Android NDK目录,< opencv-contrib-path> 为opencv_contrib/modules所在的路径。
结果
        最终,在/home/john/Downloads/opencv-master/build目录中会得到一个名为OpenCV-android-sdk的文件夹,这就是你编译得到的OpenCV Android SDK。和官方预编译库一样,在apk子文件夹中是OpenCV Manager的安装包,在sdk/java文件夹中是OpenCV的Java接口,sdk/native是OpenCV的Native接口。
        在新项目中使用自己编译的OpenCV Android SDK时,要注意需要在设备上重新安装OpenCV Manager,还要修改Android.mk中指向OpenCV.mk的路径。
Ubuntu 16.04下为Android编译OpenCV 3.2.0 Manager

文章图片

        如果需要调用SIFT,SURF等匹配算法,还要把相应的源码文件(位于/home/john/Downloads/opencv-master/opencv_contrib/modules/xfeatures2d/src)拷贝到项目jni文件夹中,然后在Android.mk中为LOCAL_SRC_FILES添加对应的源码文件。
        在编译的过程中如果发现有错误或异常出现,可以首先比较一下OpenCV官方BuildBot的输出信息,便于分析自己的问题原因。最新编译结果的链接为:http://pullrequest.opencv.org/buildbot/one_line_per_build。一次编译成功的输出结果为precommit_pack_android Build Log #116。如果你的网络比较好,甚至可以把网站上生成的压缩包下载下来(比如这里)!
          我编译产生的OpenCV Android SDK可以在这里下载:http://pan.baidu.com/s/1kVOejLt。因为考虑到在编译过程中需要从网络下载一些文件,我已经把Linux与Windows编译所需的文件全部下载好,并和3.2的源码一同放在sources文件夹中。感兴趣的读者可以直接用这个源码包进行编译。
        另外附上一篇在Android上利用SIFT,SURF和FREAK进行目标匹配的教程:《深入OpenCV Android应用开发 中文版 – 第三章代码更新》。
参考资料
[1]  http://stackoverflow.com/questions/30657774/surf-and-sift-algorithms-doesnt-work-in-opencv-3-0-java
[2]  https://github.com/opencv/opencv/issues/5561
[3]  https://github.com/opencv/opencv/issues/6215
[4]  http://answers.opencv.org/question/73863/how-to-run-build_sdkpy-for-android-sdk/
[5]  https://zami0xzami.wordpress.com/2016/03/17/building-opencv-for-android-from-source/
[6]  http://eevee.cc/2017/05/05/build-ocv-for-android-with-cuda/

    推荐阅读