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版本便可。
相關文章
相關標籤/搜索