文章目录
- 前言
- 正文
- 1 编译环境
- 2 配置修改
- 小结
前言
如题,需要编译最新版的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点都没整出来,今天又花了半天才弄出来。