使用opencv源码自己编制android so库的过程

工作需要,在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

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了~


你可能感兴趣的:(使用opencv源码自己编制android so库的过程)