最近的一个项目中,需要自己编译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. 就遇到编译问题。后来手动下载
- http://ant.apache.org 1.9版本。才解决问题。 但还是做了个软链接 /usr/ant
副编译机采用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) )
#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地址,
git clone 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
#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) )
#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_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_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'
../../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版本即可。