opencv4.1.0+contrib4.1.0+opencl在mac下编译so

前言

如题,需要编译最新版的opencv,同时需要使用face module,这个模块已被移到contrib中,同时为了在安卓下使用gpu加速,需要开启opencl。因此,这篇记录了整个编译过程

正文

1 编译环境

macos 10.14.1
安装python 3.6
安装安卓sdk,我是装了android studio自带的
下载安卓ndk,这个我因为编译ffmpeg下载了r17c版本,其实sdk中已经有一个了,在ndk-bundle中

下载 opencv4.1.0 source源码
https://opencv.org/releases/
下载 对应的opencv-contrib4.1.0
https://github.com/opencv/opencv_contrib/releases

2 配置修改

新版的opencv可以用python来执行编译,进入到
${opencv}/platforms/android, 可以看到一个build_sdk.py文件,待会就靠它了
在这里插入图片描述
可以到最后看看它的所有参数
在这里插入图片描述
1)写个脚本
为了方便在该文件夹内创建脚本文件build_android.sh
写入脚本内容


mkdir android_so
python3 build_sdk.py \
./android_so \
/Users/cd/Downloads/opencv-4.1.0 \
--sdk_path=/Users/cd/Library/Android/sdk \
--ndk_path=/Users/cd/Downloads/android-ndk-r17c \
--extra_modules_path=/Users/cd/Downloads/opencv_contrib-4.1.0/modules

创建了一个文件夹android_so
执行build_sdk.py
指定工作目录为当前文件夹的android_so
指定opencv目录为我的opencv目录${opencv}
指定sdk目录
指定ndk目录
指定额外模块目录

2)根据前人经验,把对应的工具链版本改成你android ndk toolchain的版本
opencv4.1.0的工具链指定也是在py中指定的,
在这里插入图片描述
可以在同文件夹下找到ndk-18-api-level-21.config.py,至于怎么改,看看
${ndk}->toolchains中是什么样的了
在这里插入图片描述
因此,我的改为
在这里插入图片描述
如果需要其他可以再加

还要修改build_library方法
一个方面是禁止对于Android工程/服务的编译,因为这涉及到外网连接,常常会失败;另一个方面是为了编译libopencv_java4.so,需要打开一些开关

# 原先
def build_library(self, abi, do_install):
    cmd = ["cmake", "-GNinja"]
    cmake_vars = dict(
        CMAKE_TOOLCHAIN_FILE=self.get_toolchain_file(),
        INSTALL_CREATE_DISTRIB="ON",
        WITH_OPENCL="OFF",
        WITH_IPP=("ON" if abi.haveIPP() else "OFF"),
        WITH_TBB="ON",
        BUILD_EXAMPLES="OFF",
        BUILD_TESTS="OFF",
        BUILD_PERF_TESTS="OFF",
        BUILD_DOCS="OFF",
        BUILD_ANDROID_EXAMPLES="OFF",
        INSTALL_ANDROID_EXAMPLES="OFF",
)
# 修改后
def build_library(self, abi, do_install):
    cmd = ["cmake", "-GNinja"]
    cmake_vars = dict(
        CMAKE_TOOLCHAIN_FILE=self.get_toolchain_file(),
        INSTALL_CREATE_DISTRIB="ON",
        WITH_OPENCL="OFF",
        WITH_IPP=("ON" if abi.haveIPP() else "OFF"),
        WITH_TBB="ON",
        BUILD_EXAMPLES="OFF",
        BUILD_TESTS="OFF",
        BUILD_PERF_TESTS="OFF",
        BUILD_DOCS="OFF",
        BUILD_ANDROID_EXAMPLES="OFF",
        INSTALL_ANDROID_EXAMPLES="OFF",
        BUILD_ANDROID_SERVICE="OFF",
        CMAKE_BUILD_TYPE="RELEASE",
        BUILD_ZLIB="ON"
    )

3)运行bash build_android.sh,
发现找不到cmake,其实在sdk目录下是有cmake的,也有一个版本,它的逻辑找不到。
我们手动验证下cd /Users/cd/Library/Android/sdk/cmake/3.10.2.4988404/bin,进到我的cmake目录的bin下,cmake --verison 这个是成功的
在这里插入图片描述
只能给它换种写法了,如下修改cmake_subdirs, 跳过执行cmake --version的验证步骤。
在这里插入图片描述
重新运行,发现找不到ninja,方法一样
验证
在这里插入图片描述
修改
在这里插入图片描述
4)发生了神奇的错误
在这里插入图片描述
发现我的gradle-4.6-all.zip只有14M大小,应该有100多m的才对,清空,下载一个丢进来http://services.gradle.org/distributions/

5)可以正常编译了,先停一下,让我们打开opencl
在这里插入图片描述
./modules/core/src/opencl/runtime/opencl_core.cpp
找到 #if defined(linux)这一项将其修改为:

#if defined(__linux__)&&!defined(__ANDROID__)
....
#endif  __linux__

并在endlif下方添加以下代码:



#if defined(__ANDROID__)

    #include <dlfcn.h>
    #include <sys/stat.h>


#if defined(__ARM_ARCH_8A__) || defined(_X64_)

    static const char *default_so_paths[] = {
                                            "/system/lib64/libOpenCL.so",
                                            "/system/vendor/lib64/libOpenCL.so",
                                            "/system/vendor/lib64/egl/libGLES_mali.so"
                                          };

#else

    static const char *default_so_paths[] = {
                                            "/system/lib/libOpenCL.so",
                                            "/system/vendor/lib/libOpenCL.so",
                                            "/system/vendor/lib/egl/libGLES_mali.so"
                                          };

#endif


static int access_file(const char *filename)
    {
        struct stat buffer;
        return (stat(filename, &buffer) == 0);
    }

    static void* GetProcAddress (const char* name)
    {
        static void* h = NULL;
        unsigned int i;
        if (!h)
        {
            const char* name;
            for(i=0; i<(sizeof(default_so_paths)/sizeof(char*)); i++)
            {
                if(access_file(default_so_paths[i])) {
                    name = (char *)default_so_paths[i];
                    h = dlopen(name, RTLD_LAZY);
                    if (h) break;
                }
            }
            if (!h)
                return NULL;
        }

        return dlsym(h, name);
    }
    #define CV_CL_GET_PROC_ADDRESS(name) GetProcAddress(name)

#endif


这段代码中的default_so_paths指明了当程序在目标平台运行时,会从目标平台的指定位置,读取opencl.so。所以为什么说目标设备一定要支持opencl,以及有它的驱动。

然后编译就行了。

小结

编译太难了有木有,这个熬到凌晨4点都没整出来,今天又花了半天才弄出来。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值