转载---- 使用opencv源码自己编制android so库的过程
http://blog.csdn.net/lantishua/article/details/21182965
工作需要,在Android上使用OpenCV。opencv当前的版本(2.4.8)已经有了opencv4android,但是一方面这个SDK将所有opencv的功能打到了一个so包里,所以so的体积较大,从而造成使用该so的apk也大,上层对此不太满意;另一方面,使用opencv4android必须在手机上安装openv manager,上层对此也感觉有点别扭。所以我尝试用opencv的源码自己编译应用于android平台的opencv动态库以供底层开发用。首先介绍一下相关环境:
opencv: opencv2.4.8 (Linux)
系统:ubuntu12.04
开发环境:eclipse juno(with CDT等), android ndk r9c
针对android开发可用的so库需要用ndk build编译,我详细查看了下opencv.org上的文档,上面只有opencv在linux,win等上的编译方法,当然还有opencv4android的使用方法。网上的文档找到一个使用老版本opencv来自己编译的帖子,但版本较老就没采用,所以只能自己分析下opencv的源码写mk文件进行编译。
为了压缩动态库的体积,也根据自己的实际需求,暂时只编译了三个库:core,imgproc,highgui. 幸运的是,新版本的opencv模块化比较好,所以不存在太多源码拆分的问题。所以,将源码中module文件夹下的三个模块源码放到jni/opencv目录下就可以了。之后在opencv文件夹下编写Android.mk文件,如下:
LOCAL_PATH:= $(call my-dir)
ZLIB_PATH:= /home/yxh/eclipse/android-ndk-r9c/platforms/android-14/arch-arm/usr
#opencv_core module
include $(CLEAR_VARS)
LOCAL_MODULE := libopencv_core
LOCAL_C_INCLUDES := \
$(LOCAL_PATH)/core/include \
$(ZLIB_PATH)/include
LOCAL_CPPFLAGS := -frtti -fexceptions
LOCAL_LDLIBS += -llog -lz
LOCAL_SRC_FILES := \
core/src/precomp.cpp \
core/src/algorithm.cpp \
core/src/alloc.cpp \
core/src/arithm.cpp \
core/src/array.cpp \
core/src/cmdparser.cpp \
core/src/convert.cpp \
core/src/copy.cpp \
core/src/datastructs.cpp \
core/src/drawing.cpp \
core/src/dxt.cpp \
core/src/gpumat.cpp \
core/src/lapack.cpp \
core/src/mathfuncs.cpp \
core/src/matmul.cpp \
core/src/matop.cpp \
core/src/matrix.cpp \
core/src/opengl_interop_deprecated.cpp \
core/src/opengl_interop.cpp \
core/src/persistence.cpp \
core/src/parallel.cpp \
core/src/rand.cpp \
core/src/stat.cpp \
core/src/system.cpp \
core/src/tables.cpp
include $(BUILD_SHARED_LIBRARY)
#opencv_imgproc module
include $(CLEAR_VARS)
LOCAL_MODULE := libopencv_imgproc
LOCAL_C_INCLUDES := \
$(LOCAL_PATH)/core/include \
$(LOCAL_PATH)/imgproc/include \
$(ZLIB_PATH)/include
LOCAL_CPPFLAGS := -frtti -fexceptions
LOCAL_LDLIBS += -llog -lz
LOCAL_SHARED_LIBRARIES := libopencv_core
LOCAL_SRC_FILES := \
imgproc/src/accum.cpp \
imgproc/src/approx.cpp \
imgproc/src/canny.cpp \
imgproc/src/clahe.cpp \
imgproc/src/color.cpp \
imgproc/src/contours.cpp \
imgproc/src/convhull.cpp \
imgproc/src/corner.cpp \
imgproc/src/cornersubpix.cpp \
imgproc/src/deriv.cpp \
imgproc/src/distransform.cpp \
imgproc/src/emd.cpp \
imgproc/src/featureselect.cpp \
imgproc/src/filter.cpp \
imgproc/src/floodfill.cpp \
imgproc/src/gabor.cpp \
imgproc/src/generalized_hough.cpp \
imgproc/src/geometry.cpp \
imgproc/src/grabcut.cpp \
imgproc/src/histogram.cpp \
imgproc/src/hough.cpp \
imgproc/src/imgwarp.cpp \
imgproc/src/linefit.cpp \
imgproc/src/matchcontours.cpp \
imgproc/src/moments.cpp \
imgproc/src/morph.cpp \
imgproc/src/phasecorr.cpp \
imgproc/src/pyramids.cpp \
imgproc/src/rotcalipers.cpp \
imgproc/src/samplers.cpp \
imgproc/src/segmentation.cpp \
imgproc/src/shapedescr.cpp \
imgproc/src/smooth.cpp \
imgproc/src/subdivision2d.cpp \
imgproc/src/sumpixels.cpp \
imgproc/src/tables.cpp \
imgproc/src/templmatch.cpp \
imgproc/src/thresh.cpp \
imgproc/src/undistort.cpp \
imgproc/src/utils.cpp
include $(BUILD_SHARED_LIBRARY)
#opencv_highgui module
include $(CLEAR_VARS)
LOCAL_MODULE := libopencv_highgui
LOCAL_C_INCLUDES := \
$(LOCAL_PATH)/core/include \
$(LOCAL_PATH)/imgproc/include \
$(LOCAL_PATH)/highgui/include \
$(ZLIB_PATH)/include \
/usr/include \
/usr/include/x86_64-linux-gnu
LOCAL_CPPFLAGS := -frtti -fexceptions
LOCAL_LDLIBS := -llog -lz
LOCAL_SHARED_LIBRARIES := libopencv_core
LOCAL_SHARED_LIBRARIES += libjpeg libpng
LOCAL_SRC_FILES := \
highgui/src/bitstrm.cpp \
highgui/src/cap.cpp \
highgui/src/cap_ffmpeg.cpp \
highgui/src/cap_images.cpp \
highgui/src/cap_v4l.cpp \
highgui/src/grfmt_base.cpp \
highgui/src/grfmt_bmp.cpp \
highgui/src/grfmt_imageio.cpp \
highgui/src/grfmt_jpeg.cpp \
highgui/src/grfmt_png.cpp \
highgui/src/grfmt_pxm.cpp \
highgui/src/grfmt_sunras.cpp \
highgui/src/loadsave.cpp \
highgui/src/utils.cpp \
highgui/src/window.cpp \
highgui/src/window_gtk.cpp \
highgui/src/grfmt_exr.cpp \
highgui/src/grfmt_tiff.cpp
include $(BUILD_SHARED_LIBRARY)
说几点mk文件中需要注意的地方。
1、opencv依赖于zlib库,该库在NDK中已经提供了,但要在include的时候加入进去,所以定义了ZLIB_PATH变量,将其指向zlib.h的位置,并在之后三个模块的编译中加入到include中。
2、LOCAL_CPPFLAGS := -frtti -fexceptions,是编译opencv必须的编译标志,不加会提示exception等错误。如果是在工作中某个系统里(类ANDROID)中增加opencv的功能,但系统的编译选项中没开-frtti,则会在链接的时候报错。
3、前两个模块比较好弄,因为相对独立,需要注意的是core/include下需要将源码中生成的cvconfig.h文件拷贝过去,这个文件包含很多宏定义来控制opencv编译中哪些功能打开哪些功能关闭。实际上,比较麻烦的是highgui库的编译,以为设计的依赖比较多。首先声明的是,因为功能划分,本人在编译中没有打开gtk+等界面工具,只进行数据的处理。同时,经过查询比较,本人认为现版本Opencv在NDK-BUILD下编译时也不支持ffmpeg的相关功能。我曾经尝试打开ffmpeg相关开关并进行编译调试,调试到后来在源文件中发现某线程功能的条件编译不支持android ndk下的编译(供出源码,摘自cap_ffmpeg_impl.hpp)
#if defined WIN32 || defined _WIN32 || defined WINCE
struct ImplMutex::Impl
{
void init()
{
#if (_WIN32_WINNT >= 0x0600)
::InitializeCriticalSectionEx(&cs, 1000, 0);
#else
::InitializeCriticalSection(&cs);
#endif
refcount = 1;
}
void destroy() { DeleteCriticalSection(&cs); }
void lock() { EnterCriticalSection(&cs); }
bool trylock() { return TryEnterCriticalSection(&cs) != 0; }
void unlock() { LeaveCriticalSection(&cs); }
CRITICAL_SECTION cs;
int refcount;
};
#ifndef __GNUC__
static int _interlockedExchangeAdd(int* addr, int delta)
{
#if defined _MSC_VER && _MSC_VER >= 1500
return (int)_InterlockedExchangeAdd((long volatile*)addr, delta);
#else
return (int)InterlockedExchangeAdd((long volatile*)addr, delta);
#endif
}
#endif // __GNUC__
#elif defined __APPLE__
#include <libkern/OSAtomic.h>
struct ImplMutex::Impl
{
void init() { sl = OS_SPINLOCK_INIT; refcount = 1; }
void destroy() { }
void lock() { OSSpinLockLock(&sl); }
bool trylock() { return OSSpinLockTry(&sl); }
void unlock() { OSSpinLockUnlock(&sl); }
OSSpinLock sl;
int refcount;
};
#elif defined __linux__ && !defined ANDROID
struct ImplMutex::Impl
{
void init() { pthread_spin_init(&sl, 0); refcount = 1; }
void destroy() { pthread_spin_destroy(&sl); }
void lock() { pthread_spin_lock(&sl); }
bool trylock() { return pthread_spin_trylock(&sl) == 0; }
void unlock() { pthread_spin_unlock(&sl); }
pthread_spinlock_t sl;
int refcount;
};
#else
struct ImplMutex::Impl
{
void init() { pthread_mutex_init(&sl, 0); refcount = 1; }
void destroy() { pthread_mutex_destroy(&sl); }
void lock() { pthread_mutex_lock(&sl); }
bool trylock() { return pthread_mutex_trylock(&sl) == 0; }
void unlock() { pthread_mutex_unlock(&sl); }
pthread_mutex_t sl;
int refcount;
};
#endif
上述条件编译最后在NDK下走到了#else,而pthread_mutex_init出自wp32threads.h,该头文件中第一依赖于windows.h。。。android下玩不转了。。。
另外,分析了下opencv4android的config发现也没打开ffmpeg,同时从论坛里看到某外说道现版本opencv在android上确不支持ffmpeg。所以本人认为现版本opencv从源码上对android下的ffmpeg调用就不支持,这一点如果各位有别的见解,欢迎讨论。
接上文,所以本人在编译highgui模块时也关闭了ffmpeg相关的依赖,但为了能够支持图片的存取操作,打开了jpeg,png的依赖,所以在mk文件中有
LOCAL_C_INCLUDES := \
/usr/include \
/usr/include/x86_64-linux-gnu
LOCAL_SHARED_LIBRARIES += libjpeg libpng
这两个库的编译也是从相关官网上下载源码,编写mk文件,这两个库的mk文件是从网上找来的,粘到jpeg和png源码目录下,ndk-build异常顺利 :-). 下面也贴上两个mk的代码(转载):
jpeg下Android.mk:
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := libjpeg
LOCAL_SRC_FILES := \
jaricom.c jcapimin.c jcapistd.c jcarith.c jccoefct.c jccolor.c \
jcdctmgr.c jchuff.c jcinit.c jcmainct.c jcmarker.c jcmaster.c \
jcomapi.c jcparam.c jcprepct.c jcsample.c jctrans.c jdapimin.c \
jdapistd.c jdarith.c jdatadst.c jdatasrc.c jdcoefct.c jdcolor.c \
jddctmgr.c jdhuff.c jdinput.c jdmainct.c jdmarker.c jdmaster.c \
jdmerge.c jdpostct.c jdsample.c jdtrans.c jerror.c jfdctflt.c \
jfdctfst.c jfdctint.c jidctflt.c jidctfst.c jidctint.c jquant1.c \
jquant2.c jutils.c jmemmgr.c jmemnobs.c
include $(BUILD_SHARED_LIBRARY)
png下Android.mk:
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := libpng
LOCAL_SRC_FILES := png.c pngerror.c pngget.c pngmem.c pngpread.c pngread.c pngrio.c \
pngrtran.c pngrutil.c pngset.c pngtrans.c pngwio.c pngwrite.c pngwtran.c \
pngwutil.c
LOCAL_LDLIBS := -lz
include $(BUILD_SHARED_LIBRARY)
PS:难怪异常顺利,真真没啥依赖。。。
这样,opencv编译中mk上就没啥可啰嗦的了。但opencv有另外一些重要的依赖本人是在eclipse中设置的,主要是标准C++的支持。在这里提醒大家,编译opencv中一定要使用标准C++,这个在NDK中有带的,具体在sources/cxx-stl/gnu-libstdc++下。如果使用stlport等其他一些C++库,会出现一些问题。本人最初就是使用的stlport,出现了一些问题,为了fix自己改stlport改来改去头大了很麻烦。
下面给出eclipse中的相关设置:(C/C++ general下paths and symbols中添加)
${NDKROOT}/toolchains/arm-linux-androideabi-4.6/prebuilt/linux-x86_64/lib/gcc/arm-linux-androideabi/4.6/include
${NDKROOT}/sources/cxx-stl/gnu-libstdc++/4.6/include
${NDKROOT}/sources/cxx-stl/gnu-libstdc++/4.6/libs/armeabi-v7a/include
${NDKROOT}/platforms/android-9/arch-arm/usr/include
/home/yxh/OpenCV-2.4.8-android-sdk/sdk/native/jni/include
/usr/include
${JAVA_HOME}/include
${NDKROOT}/platforms/android-12/arch-arm/usr/include/
PS:有些可以去掉,NDK几个应该是必须的,请自行根据实际情况实验。
设置完以上的东西,最后给出jni下Application.mk:
APP_ABI := armeabi-v7a
APP_STL := gnustl_static
APP_CPPFLAGS := -frtti -fexceptions
APP_PLATFORM := android-8
上面APP_ABI应该设置成相应处理器的,好像有个$ABI的东西可以自适应
这样,在eclipse下ndk-build就可以生成libopencv_core,libopencv_imgproc,libopencv_highgui三个so库了。这样就可以在自己jni开发中使用Opencv了~
每一个不曾起舞的日子,都是对生命的辜负。
But it is the same with man as with the tree. The more he seeks to rise into the height and light, the more vigorously do his roots struggle earthward, downward, into the dark, the deep - into evil.
其实人跟树是一样的,越是向往高处的阳光,它的根就越要伸向黑暗的地底。----尼采