zoukankan      html  css  js  c++  java
  • OpenCV4Android编译

    http://blog.sina.com.cn/s/blog_602f87700102vdnw.html

     (2015-04-02 11:10:01)
     
     
    最近的一个项目中,需要自己编译OpenCV Android版本。(想要修改其中libopencv_java.so)。
    之前也做过OpenCV的完整编译,但当时必须使用OpenCV2.0版本(据说这个版本最快),想要在Android Native C层面使用OpenCV2.0 库,但OpenCV2.0并无Android版本,所以下载了OpenCV2.0 for Linux Source Code。自己写Android.mk Application.mk来编译之。将所有依赖第三方库和OpenCV本身的:cvaux,cv, cxcore, ml, highgui等全部成功编译。且highgui底层支持V4L2, libv4l, ffmpeg等。 (具体编译过程以后再讲) .  当时就体会了OpenCV编译的繁琐和复杂,以及配置选项之多。这次需要编译OpenCV4Android,有了打持久战的精神准备。
     
    (后续,之后随项目的升级,又编译了OpenCV3.1。于是添加在这里)
     
     
    1. 下载和准备工作:
    编译平台:Fedora17, Fedora20
    http://opencv.org/downloads.html
    选择了Version 2.4.10   OpenCV for Android Source下载。
    与OpenCV2.0使用autoconfig不同,OpenCV2.4 已经采用CMake管理Source。
     
    必须预先安装的软件有:
    a. jdk
    b. Android SDK (revision 14以上)
    c. Android NDK
    d. Apache Ant. (Version 1.9以上)
    e: Python.
    请注意版本限制,如果版本过低,编译中会遇到问题。
    Sam主编译机器时FC17, yum install ant 会被安装Apache Ant 1.8. 就遇到编译问题。后来手动下载 
    副编译机采用FC 20 .   yum install ant本身就是Apache Ant 1.9. 所以没有问题。
    如果运行ant,可能会发现找不到库,
    (Error: Could not find or load main class org.apache.tools.ant.launch.Launcher)
    则需要
    export CLASSPATH=.:$JAVA_HOME/bin/dt.jar:$JAVA_HOME/lib/tools.jar:/usr/share/java/ant-launcher.jar
     
     
    2. 设置环境变量:
    export ANDROID_NDK=/opt/android-ndk-r9d/
    export ANDROID_SDK=/home/sam/android-sdks/
    export ANDROID_ABI=armeabi-v7a
     
    export ANT_HOME=/usr/local/apache-ant-1.9.4
    export PATH=${PATH}:${ANT_HOME}/bin
     
     
     
    3. cmake产生Makefile:
    #cd platforms/
    #sh scripts/cmake_android_arm.sh 
     
    这个shell脚步会创建build_android_arm目录,并将Makefile文件放置在其中。
     
    Sam需要确保Java modules包含在其中。否则不会产生libopencv_java.so
     
    --   OpenCV modules:
    --     To be built:                 core androidcamera flann imgproc highgui features2d calib3d ml video legacy objdetect photo gpu ocl nonfree contrib java stitching superres ts videostab
     
    如果发现Cmake 产生的这个列表中,有些设置需要修改。可以尝试察看:
    opencv-2.4.10/CMakeLists.txt, 它同时也include很多 .mk文件(opencv-2.4.10/cmake目录内)。 看看具体是什么原因。
     
    例1:刚开始无论如何无法使Java 加入编译模块。
    Unavailable:                 dynamicuda Java python viz
    后来只好跟踪 CMakeLists.txt, 直到 OpenCVDetectApacheAnt.cmake中,才发现:
    execute_process(COMMAND ${ANT_EXECUTABLE} -version
    后出错。 这里ANT_EXECUTABLE就是ant. 
    才意识到ant未安装造成问题。
     
    例2:
    Sam想把所有Example都编译出来,但 C/C++ Examples: 显示NO。
    察看CMakeLists.txt, 看到其和BUILD_EXAMPLES 相关, 于是作如下修改:
    opencv-2.4.10/platforms/scripts/cmake_android_arm.sh 中添加:
    -DBUILD_EXAMPLES=1
     
    则C++ Example也被加入编译了。
     
     
     
      4. 编译:
    #cd opencv-2.4.10/platforms/build_android_arm
    #make
     
     
     
    5.OpenCV一些附加模块支持: 
    OpenCV在不断更新中,添加了很多模块,但这些模块并未添加入代码树中。例如:Eigen,TBB,OpenCL等。需要自己处理。
     
    5.1: 对Eigen的支持
    platforms/scripts/cmake_android_arm.sh 中添加:
    -DHAVE_EIGEN=1
    此时 调用: sh scripts/cmake_android_arm.sh 
    关于Eigen的结果有点异常:
    --   Other third-party libraries:
    --     Use Eigen:                   YES (ver ..)
    没有得到版本号,则一定没有找到对应头文件等。查看CMakeLists.txt。以下几项内容缺失。
    ${EIGEN_WORLD_VERSION}.${EIGEN_MAJOR_VERSION}.${EIGEN_MINOR_VERSION}
     
    跟踪检查:cmake/OpenCVFindLibsPerf.cmake
    其中与此相关的是:
    if(WITH_EIGEN)
      find_path(EIGEN_INCLUDE_PATH "Eigen/Core"
                PATHS /usr/local /opt /usr  ENV ProgramFiles ENV ProgramW6432
                PATH_SUFFIXES include/eigen3 include/eigen2 Eigen/include/eigen3 Eigen/include/eigen2
                DOC "The path to Eigen3/Eigen2 headers"
                CMAKE_FIND_ROOT_PATH_BOTH)
     
      if(EIGEN_INCLUDE_PATH)
        ocv_include_directories(${EIGEN_INCLUDE_PATH})
        ocv_parse_header("${EIGEN_INCLUDE_PATH}/Eigen/src/Core/util/Macros.h" EIGEN_VERSION_LINES EIGEN_WORLD_VERSION EIGEN_MAJOR_VERSIO
    N EIGEN_MINOR_VERSION)
        set(HAVE_EIGEN 1)
      endif()
    endif(WITH_EIGEN)
     
     find_path()含义是:在PATHS 后面的目录内查找Eigen/Core目录。查到了,则目录存放在EIGEN_INCLUDE_PATH中。否则写入NOFound。
    然后在Eigen/src/Core/util/Macros.h找到版本号并存储。
     
    所以当前是因为没有Eigen Source Tree造成问题。Sam下载(https://bitbucket.org/erublee/eigen-android/get/2e2c8da72443.zip)并把它放在3rdparty/eigen中。
    并修改cmake/OpenCVFindLibsPerf.cmake 相关内容为:
     
    if(WITH_EIGEN)
      find_path(EIGEN_INCLUDE_PATH "Eigen/Core"
                PATHS /usr/local /opt /usr /home/sam/work/current/Research/OpenCV/opencv-2.4.10/3rdparty/eigen  ENV ProgramFiles ENV Pro
    gramW6432
                PATH_SUFFIXES include/eigen3 include/eigen2 Eigen/include/eigen3 Eigen/include/eigen2
                DOC "The path to Eigen3/Eigen2 headers"
                CMAKE_FIND_ROOT_PATH_BOTH)
            message(SamInfo)
     
            message(${EIGEN_INCLUDE_PATH})
     
      if(EIGEN_INCLUDE_PATH)
        ocv_include_directories(${EIGEN_INCLUDE_PATH})
     
            message(${EIGEN_INCLUDE_PATH})
        ocv_parse_header("${EIGEN_INCLUDE_PATH}/Eigen/src/Core/util/Macros.h" EIGEN_VERSION_LINES EIGEN_WORLD_VERSION EIGEN_MAJOR_VERSIO
    N EIGEN_MINOR_VERSION)
        set(HAVE_EIGEN 1)
      endif()
    endif(WITH_EIGEN)
     
    此时调用: sh scripts/cmake_android_arm.sh 
    --   Other third-party libraries:
    --     Use Eigen:                   YES (ver 2.92.0)
     
    5.2: 关于OpenCL:
    因为OpenCL依赖于芯片支持,而Sam发现当前几个芯片(ARM)均不支持OpenCL。所以哪怕将OpenCL编译入OpenCV,其实没有使用到。
     
     
     
    5.3: TBB支持:
    platforms/scripts/cmake_android_arm.sh 中添加:
    -DBUILD_TBB=ON -DWITH_TBB=ON
    此时 调用: sh scripts/cmake_android_arm.sh 
    则会自动下载TBB for ARM来使用。并编译入OpenCV库。
     
     
     
    6. 附加讲解:
    编译过程非常明晰简单,即:
    sh scripts/cmake_android_arm.sh
    此时,可以在cmake_android_arm.sh中添加一些配置如下:
    cmake -DCMAKE_BUILD_WITH_INSTALL_RPATH=ON -DHAVE_EIGEN=1 -DWITH_V4L=1 -DHAVE_CAMV4L2=ON -DBUILD_TBB=ON -DWITH_TBB=ON  -DBUILD_EXAMPLES=1 -DANDROID_ABI="armeabi-v7a"  -DCMAKE_TOOLCHAIN_FILE=../android/android.toolchain.cmake $@ ../..
    但这些配置均会起作用么?不一定。
     
    当前我们要编译Android ARM版本,那OpenCV HighGUI中如何使用何种接口控制Camera呢?在Linux时代,有libv4l, V4L2等接口。在Android时代,还可以使用它们么? Sam为了测试这一点,添加了:
    -DWITH_V4L=1 -DWITH_LIBV4L=1 -DHAVE_CAMV4L2=ON
    下面就看看是否有效.
     
    察看CMakeLists.txt
    可以看到以下几类:
    A: 
    OCV_OPTION(WITH_V4L            "Include Video 4 Linux support"               ON   IF (UNIX AND NOT ANDROID) )
    OCV_OPTION(WITH_LIBV4L         "Use libv4l for Video 4 Linux support"        ON   IF (UNIX AND NOT ANDROID) )
    在这里,如果不是Android版本,WITH_V4L, WITH_LIBV4L才会被设置为ON。
     
    B: 
    # ----------------------------------------------------------------------------
    #  Detect 3rd-party libraries
    # ----------------------------------------------------------------------------
     
    include(cmake/OpenCVFindLibsGrfmt.cmake)
    include(cmake/OpenCVFindLibsGUI.cmake)
    include(cmake/OpenCVFindLibsVideo.cmake)
    include(cmake/OpenCVFindLibsPerf.cmake)
    其中,OpenCVFindLibsVideo.cmake中有相关设置:
    ocv_clear_vars(HAVE_LIBV4L HAVE_CAMV4L HAVE_CAMV4L2 HAVE_VIDEOIO)
    #Sam info:先把HAVE_XXXX信息先clean掉。再根据WITH_XXX设置之。
     
    if(WITH_V4L)
      if(WITH_LIBV4L)
        CHECK_MODULE(libv4l1 HAVE_LIBV4L1)
        CHECK_MODULE(libv4l2 HAVE_LIBV4L2)
        if(HAVE_LIBV4L1 AND HAVE_LIBV4L2)
          set(HAVE_LIBV4L YES)
        else()
          set(HAVE_LIBV4L NO)
        endif()
      endif()
      CHECK_INCLUDE_FILE(linux/videodev.h HAVE_CAMV4L)
      CHECK_INCLUDE_FILE(linux/videodev2.h HAVE_CAMV4L2)
      CHECK_INCLUDE_FILE(sys/videoio.h HAVE_VIDEOIO)
    endif(WITH_V4L)
     
     
     
     
    所以,在cmake_android_arm.sh中,
    1:修改HAVE_CAMV4L这样的设置,并无多大用户,因为后面会被WITH_LIBV4L设置所修改。所以只设置WITH_XXXX就好。
    2. 哪怕设置WITH_XXXXX。但也会被各种具体细节所修改。例如,是否Android,是否Win等。
     
     
     
    7. Android NativeC版本编译:
    Sam需要编译一个纯NativeC 程序,它使用到OpenCV2.4.10. 按照之前的认识,直接编译一个Android版本,使用libopencv_java.so即可。但在实际使用中,发现使用NativeC程序,无法正常访问Camera。后来重新编译OpenCV,并引导它改用V4L2,解决了此问题。现将过程记录如下:
    7.1: 问题发现:
    运行程序:发现和Camera相关处全都会抱错,提示要求重新编译OpenCV。
    Sam从Camera相关第一个function查起:cvCaptureFromCAM()
     
    在modules/highgui/include/opencv2/highgui/highgui_c.h中,有
    #define cvCaptureFromCAM cvCreateCameraCapture
     
    在modules/highgui/src/cap.cpp中,
    有其实现:CV_IMPL CvCapture * cvCreateCameraCapture (int index)
    它其实就是根据配置来选择调用何种接口,Sam在OpenCV2.0编译时,利用编译选项使它指向libv4l和v4l2俩接口。
     
    7.2: 尝试修改:
    既然之前使用libv4l和v4l2接口能够成功。那咱们就继续来。
    在platforms/scripts/cmake_android_arm.sh中,增加以下选项
    -DWITH_V4L=1 -DWITH_LIBV4L=1 -DHAVE_CAMV4L2=ON
     
    编译后,发现并未被指向libv4l 或者v4l2。找原因:
    CMakeLists.txt看到以下语句:
    OCV_OPTION(WITH_V4L            "Include Video 4 Linux support"               ON   IF (UNIX AND NOT ANDROID) )
    OCV_OPTION(WITH_LIBV4L         "Use libv4l for Video 4 Linux support"        ON   IF (UNIX AND NOT ANDROID) )
    只有非Android平台时,才会把WITH_V4L和WITH_LIBV4L置ON
     
    所以修改之:
    #Sam modify it
    #OCV_OPTION(WITH_V4L            "Include Video 4 Linux support"               ON   IF (UNIX AND NOT ANDROID) )
    OCV_OPTION(WITH_V4L            "Include Video 4 Linux support"               ON   IF (UNIX) )
    把非Android版本才允许WITH_V4l 的要求去掉。
     

     
    在cmake/OpenCVFindLibsVideo.cmake中:
    ocv_clear_vars(HAVE_LIBV4L HAVE_CAMV4L HAVE_CAMV4L2 HAVE_VIDEOIO)
    if(WITH_V4L)
      if(WITH_LIBV4L)
        CHECK_MODULE(libv4l1 HAVE_LIBV4L1)
        CHECK_MODULE(libv4l2 HAVE_LIBV4L2)
        if(HAVE_LIBV4L1 AND HAVE_LIBV4L2)
          set(HAVE_LIBV4L YES)
        else()
          set(HAVE_LIBV4L NO)
        endif()
      endif()
      CHECK_INCLUDE_FILE(linux/videodev.h HAVE_CAMV4L)
      CHECK_INCLUDE_FILE(linux/videodev2.h HAVE_CAMV4L2)
      CHECK_INCLUDE_FILE(sys/videoio.h HAVE_VIDEOIO)
    endif(WITH_V4L)
     
    此处,会查找linux/videodev2.h,来决定HAVE_CAMV4L2为ON。
    但不知为何,它总找不到。不知道CHECK_INCLUDE_FILE()找的是哪个目录。
    于是强行修改:
     
    # --- V4L ---
    status("  WITH_V4L:" ${WITH_V4L})
    status("  HAVE_LIBV4L:" ${HAVE_LIBV4L})
    status("  HAVE_CAMV4L:" ${HAVE_CAMV4L})
    status("  HAVE_CAMV4L2:" ${HAVE_CAMV4L2})
    status("  HAVE_VIDEOIO:" ${HAVE_VIDEOIO})
    ocv_clear_vars(HAVE_LIBV4L HAVE_CAMV4L HAVE_CAMV4L2 HAVE_VIDEOIO)
    if(WITH_V4L)
      if(WITH_LIBV4L)
        CHECK_MODULE(libv4l1 HAVE_LIBV4L1)
        CHECK_MODULE(libv4l2 HAVE_LIBV4L2)
        if(HAVE_LIBV4L1 AND HAVE_LIBV4L2)
          set(HAVE_LIBV4L YES)
        else()
          set(HAVE_LIBV4L NO)
        endif()
      endif()
      CHECK_INCLUDE_FILE(linux/videodev.h HAVE_CAMV4L)
      CHECK_INCLUDE_FILE(linux/videodev2.h HAVE_CAMV4L2)
      CHECK_INCLUDE_FILE(sys/videoio.h HAVE_VIDEOIO)
     
    #sam add it
    set(HAVE_CAMV4L YES)
    set(HAVE_CAMV4L2 YES)
    status("  HAVE_LIBV4L:" ${HAVE_LIBV4L})
    status("  HAVE_CAMV4L:" ${HAVE_CAMV4L})
    status("  HAVE_CAMV4L2:" ${HAVE_CAMV4L2})
    status("  HAVE_VIDEOIO:" ${HAVE_VIDEOIO})
     
    endif(WITH_V4L)
    此时,V4L2被作为底层编译进去了。
     
    7.3: 察看原来路径:
    那之前没加入V4L2 之前呢?看看OpenCV是怎么做的。
    因为:HAVE_ANDROID_NATIVE_CAMERA被设置,所以走了:
    cvCreateCameraCapture_Android()这路。它最终调用Camera_activity.cpp中的class CameraWrapperConnector去处理Camera。这里需要用到:
    libnative_camera_r2.2.0.so
    libnative_camera_r2.3.3.so
    .....
    libnative_camera_r4.4.0.so
    但具体选择哪一个,不知道用什么规则来确定。
    看起来这条路是通的阿,其实不然,这条路相信是给JNI-C中使用Camera准备的,它密切依赖于OpenCV-Manager. 对Native-C程序来说并不适用。如果强行编译使用,会被锁死。
    这条路径,Sam称之为:Native-Camera 路径。
     
    7.4:堵住原生Native-Camera路径:
    Sam 在cmake/templates/opencvconfig.cmake.in中,删除:
    set(OpenCV_HAVE_ANDROID_CAMERA @HAVE_opencv_androidcamera@)
     
    在modules/highgui/CMakeList.txt中,删除:
    add_definitions(-DHAVE_ANDROID_NATIVE_CAMERA)
    即可堵住Native-Camera。
     
     
    7.5: 局限:
    此方法编译出的库,有非常大的局限性,除非必须使用NativeC方式使用OpenCV,否则不要用此方法,因为它破坏了原生JNI-C方式使用Camera的路径。不建议使用。
     
     
     
     
    8. OpenCV3.1的编译
    随着项目的升级,又需要在NativeC下使用OpenCV3.1.  继续采用OpenCV for Android 为基础编译,修改其Camera路径为V4L2或者libv4l. 使之不再依赖于OpenCVManager而独立在NativeC层存在。
     
    8.1: 下载OpenCV3.1 Source Code:
    需要使用git下载。在https://github.com/itseez/opencv 页面,得到git地址,
     
    8.2:确认版本:
    modules/core/include/opencv2/core/version.hpp:
    #define CV_VERSION_MAJOR    3
    #define CV_VERSION_MINOR    1
    #define CV_VERSION_REVISION 0
     
    8.3: 增加对V4L,V4L2等的支持:
    A:在platforms/scripts/cmake_android_arm.sh中,增加以下选项
    -DWITH_V4L=1 -DWITH_LIBV4L=1 -DHAVE_CAMV4L2=ON
     
    B:CMakeLists.txt修改以下语句:
    #Sam modify it
    #OCV_OPTION(WITH_V4L            "Include Video 4 Linux support"               ON   IF (UNIX AND NOT ANDROID) )
    OCV_OPTION(WITH_V4L            "Include Video 4 Linux support"               ON   IF (UNIX) )
    把非Android版本才允许WITH_V4l 的要求去掉。
     
    C:在cmake/OpenCVFindLibsVideo.cmake中
    if(WITH_V4L)
      if(WITH_LIBV4L)
        CHECK_MODULE(libv4l1 HAVE_LIBV4L1)
        CHECK_MODULE(libv4l2 HAVE_LIBV4L2)
        if(HAVE_LIBV4L1 AND HAVE_LIBV4L2)
          set(HAVE_LIBV4L YES)
        else()
          set(HAVE_LIBV4L NO)
        endif()
      endif()
      CHECK_INCLUDE_FILE(linux/videodev.h HAVE_CAMV4L)
      CHECK_INCLUDE_FILE(linux/videodev2.h HAVE_CAMV4L2)
      CHECK_INCLUDE_FILE(sys/videoio.h HAVE_VIDEOIO)
     
    #sam add it
    set(HAVE_CAMV4L YES)
    set(HAVE_CAMV4L2 YES)
    status("  HAVE_LIBV4L:" ${HAVE_LIBV4L})
    status("  HAVE_CAMV4L:" ${HAVE_CAMV4L})
    status("  HAVE_CAMV4L2:" ${HAVE_CAMV4L2})
    status("  HAVE_VIDEOIO:" ${HAVE_VIDEOIO})
     
    endif(WITH_V4L)
     
    8.4:处理编译问题:
    根据以上设置,会在调用时cvCreateCameraCapture()调用cvCreateCameraCapture_V4L().
    且modules/videoio/cap_v4l.cpp会被编译进来。
    但NDK中的v4l2的头文件比较老,会导致一些错误(未定义错误)
    Sam直接在cap_v4l.cpp中添加如下语句解决之:
    //sam add it
    #define V4L2_PIX_FMT_SGBRG8  v4l2_fourcc('G', 'B', 'R', 'G')
    #define V4L2_CTRL_CLASS_CAMERA            0x009a0000
    #define V4L2_CID_CAMERA_CLASS_BASE        (V4L2_CTRL_CLASS_CAMERA | 0x900)
    #define V4L2_CID_FOCUS_ABSOLUTE                   (V4L2_CID_CAMERA_CLASS_BASE+10)
    #define V4L2_CID_FOCUS_AUTO                       (V4L2_CID_CAMERA_CLASS_BASE+12)
     
     8.5: 处理FFMPEG库添加问题:
    Sam并不是用正规做法加入FFMPEG支持,所以,在OpenCV编译时,常会发现找不到ffmpeg 符号的问题:
     
    例如:
    Linking CXX executable ../../bin/opencv_perf_videoio
    ../../lib/armeabi-v7a/libopencv_videoio.a(cap_ffmpeg.cpp.o):cap_ffmpeg.cpp:function InternalFFMpegRegister::~InternalFFMpegRegister(): error: undefined reference to 'av_lockmgr_register'
     
    Sam的解决方法是:
    在类似以下文件:
    modules/videoio/CMakeFiles/opencv_perf_videoio.dir/link.txt
    加入以下语句:
    -lffmpeg -L../../3rdparty/ffmpeg/android/armv7-a/
    这个方法并不正规。但先这么处理了。(Sam把单独编译的ffmpeg放在了platforms/build_android_arm/3rdparty/ffmpeg/android)
     

     
     
    附录:
    错误记录:
    [ 73%] Building OpenCV Android library project
    /usr/bin/ant -q -noinput -k debug
       [subant] No sub-builds to iterate on
    Target '-compile' failed with message 'The following error occurred while executing this line:
    /home/sam/android-sdks/tools/ant/build.xml:734: Class not found: javac1.8'.
    Cannot execute '-dex' - '-compile' failed or was not executed.
    Cannot execute '-package' - '-dex' failed or was not executed.
    Cannot execute '-do-debug' - '-package' failed or was not executed.
    Cannot execute 'debug' - '-do-debug' failed or was not executed.
     
    BUILD FAILED
    /home/sam/android-sdks/tools/ant/build.xml:720: The following error occurred while executing this line:
    /home/sam/android-sdks/tools/ant/build.xml:734: Class not found: javac1.8
     
    Total time: 3 seconds
    make[2]: *** [bin/classes.jar] Error 1
    make[2]: Leaving directory `/home/sam/work/current/Research/OpenCV/opencv-2.4.10/platforms/build_android_arm'
    make[1]: *** [modules/java/CMakeFiles/opencv_java.dir/all] Error 2
    make[1]: Leaving directory `/home/sam/work/current/Research/OpenCV/opencv-2.4.10/platforms/build_android_arm'
    make: *** [all] Error 2
    [sam@KingOfLinux build_android_arm]$ ant -version
    Apache Ant(TM) version 1.8.3 compiled on February 29 2012
    [sam@KingOfLinux build_android_arm]$ javac -version
    javac 1.8.0_20
     
    这个错误是因为ANT 版本太低造成的。
    Sam升级到1.9版本即可。
  • 相关阅读:
    matplotlib 学习总结
    数据获取,解析,存储等知识的学习总结
    python学习总结
    Mybatis Plus各种查询方法
    centos启动Nginx提示nginx: [emerg] still could not bind()
    vue只能本地跨域,线上跨域要后端弄
    小程序图片懒加载
    html直接引入vue.js
    vue监听浏览器关闭
    【算法】归并排序算法的编码和优化
  • 原文地址:https://www.cnblogs.com/leoking01/p/6625360.html
Copyright © 2011-2022 走看看