如何编译iOS版OpenCV动态库

OpenCV 的编译动态库单独拎出来写这篇博客,主要是因为根据官方的教程编译出来的动态库将可执行程序内部的 install Name 写死了,导致动态库无法链接到,报 dyld: Library not loaded: 错误。

GitHubOpenCV 库的 issues 上也有人提,但并没有得到解决。

而它打包用的是 cmake ,自己也不想去修改他的 Python脚本cmake文件 ,于是使用了 install_name_tool 工具修改 OpenCV 二进制文件中的链接路径,完美地解决了该问题。

于是将其记录下来,以便后期查阅。

使用脚本直接打包OpenCV动态库,添加到项目中会报下方错误:

dyld: Library not loaded: /Users/cntp/Documents/opencv-4.1.0/platforms/ios/ios/build/build-iphonesimulator/lib/Release/opencv2.framework/opencv2

  Referenced from: /private/var/containers/Bundle/Application/UUID/MyApp.app/Frameworks/MyApp.framework/MyApp
  Reason: image not found

使用otool工具查看,会发现二进制文件中将链接路径写成固定路径:

cntp@TPL-0000-161520deMacBook-Pro opencv2.framework % otool -L opencv2
opencv2:
	/Users/cntp/Documents/opencv-4.1.0/platforms/ios/ios/build/build-iphonesimulator/lib/Release/opencv2.framework/opencv2 (compatibility version 4.1.0, current version 4.1.0)

关于 Opencv

OpenCV 的全称是Open Source Computer Vision Library,是一个跨平台的计算机视觉库。OpenCV是由英特尔公司发起并参与开发,以BSD许可证授权发行,可以在商业和研究领域中免费使用。OpenCV可用于开发实时的图像处理、计算机视觉以及模式识别程序。该程序库也可以使用英特尔公司的IPP进行加速处理。

主要模块分为以下几种:

  • core:简洁核心模块,基本函数,基本数据结构;
  • imgproc:图像处理模块,线性和非线性图像滤波,几何图像转换,颜色空间转换,直方图等;
  • video:视频分析模块,运动估计,背景消除,物体跟踪算法;
  • calib3d:基本多视角几何算法,单体和立体相机的标定,对象姿势估计,双目立体匹配算法和元素的三维重建;
  • features2d:包含了显著特征检测算法,描述算子和算子匹配算法;
  • objdetect:物体检测和一些预定义的物体的检测(如人脸,眼睛,杯子,人,汽车等);
  • ml:多种机器学习算法,如K均值,支持向量机和神经网络;
  • highgui:简单易用接口,有视频捕捉,图像和视频编码功能,简单UI接口,iOS的是其中一个子集;
  • gpu:GPU加速算法,iOS不可用;
  • ocl:OpenCL通用算法,iOS不可用;
  • 其它,辅助、算法等。

OpenCV可用于解决如下领域问题:

关于 Cmake

OpenCV 使用 Cmake 工具进行编译的,所以在打包Framework之前我们也需要了解 Cmake

CMake 是一个跨平台的安装(编译)工具,可以用简单的语句来描述所有平台的安装(编译过程)。具体内容可以查看 百度百科关于Cmake的介绍

同时也可以查看 CMake官网

CMake是旨在构建,测试和打包软件的开源,跨平台工具系列。
CMake用于使用简单平台和独立于编译器的配置文件来控制软件编译过程,并生成可在您选择的编译器环境中使用的本机makefile和工作区。
CMake工具套件是由Kitware创建的,旨在满足ITK和VTK等开源项目对功能强大的跨平台构建环境的需求。

---

CMake is an open-source, cross-platform family of tools designed to build, test and package software. 
CMake is used to control the software compilation process using simple platform and compiler independent configuration files, and generate native makefiles and workspaces that can be used in the compiler environment of your choice.
The suite of CMake tools were created by Kitware in response to the need for a powerful, cross-platform build environment for open-source projects such as ITK and VTK.

第一步:查看是否安装 Cmake

如果想在Mac上编译OpenCV,需要检测Mac中是否安装过 Cmake

对于如何查看,我们可以直接在终端输入 cmake --version ,如果有相应版本会出现下方内容:

cntp@TPL-0000-161520deMacBook-Pro ~ % Cmake --version
cmake version 3.17.1

CMake suite maintained and supported by Kitware (kitware.com/cmake).

如果出现 -bash: cmake: command not found 则代表电脑中并没有安装 Cmake

如果没有安装 Cmake 可以通过 Homebrew 进行安装,具体命令如下:

brew install cmake

其中 Homebrew 是Mac OS平台下的软件包管理工具,大家应该都不陌生,具体的使用可以查看 Homebrew官网

安装完成后即可编译OpenCV动态库

第二步:编译 OpenCV 动态库

下载相应版本的 OpenCVcd~/platforms/ios 目录,我们可以看到其打包的脚本 build_framework.py ,语言是 Python 并不是常用的 Shell 脚本,不过语言很简单。

如果项目中需要静态库,可以直接到 opencv官网 下载,或者直接使用 python build_framework.py ios ,其中 ios 为编译文件的路径,最后会在 ios 下看到 opencv2.framework

如果需要编译动态库需要在后面添加 --dynamic ,或者到 python 脚本中将 parser.add_argument('--dynamic', default=False, action='store_true', help='build dynamic framework (default is "False" - builds static framework)') 中的 default 改为 true

打包动态库的命令如下:

cd /Users/cntp/Documents/opencv-4.1.0/platforms/ios 

python build_framework.py ios --dynamic

编译时间会比较长,结束后会在 /Users/cntp/Documents/opencv-4.1.0/platforms/ios/ios 目录下得到相应的 opencv2.framework

ps:不用吐槽 ios 不是 iOS ,它那边文档就是这样写的,也就这样用了。

第三步:修改 OpenCV 二进制文件中的链接地址

理论上到这一步就可以了,直接将 opencv2.framework 添加到外面工程中,修改 EmbedEmbed&Sign 就可以。

但当在运行时加载 opencv.framework 时,它会直接报错,由终端的打印日志可以看出app链接的路径为打包存放的默认路径,也就是 /Users/cntp/Documents/opencv-4.1.0/platforms/ios/ios

我们可以使用 otool 工具进行查看,命令如下:

cntp@TPL-0000-161520deMacBook-Pro opencv2.framework % otool -L opencv2
opencv2:
	/Users/cntp/Documents/opencv-4.1.0/platforms/ios/ios/build/build-iphonesimulator/lib/Release/opencv2.framework/opencv2 (compatibility version 4.1.0, current version 4.1.0)
	/System/Library/Frameworks/Accelerate.framework/Accelerate (compatibility version 1.0.0, current version 4.0.0)
	/System/Library/Frameworks/CoreGraphics.framework/CoreGraphics (compatibility version 64.0.0, current version 1251.12.0)
	/System/Library/Frameworks/QuartzCore.framework/QuartzCore (compatibility version 1.2.0, current version 1.11.0)
	/System/Library/Frameworks/AssetsLibrary.framework/AssetsLibrary (compatibility version 1.0.0, current version 1.0.0)
	/System/Library/Frameworks/UIKit.framework/UIKit (compatibility version 1.0.0, current version 61000.0.0)
	/System/Library/Frameworks/AVFoundation.framework/AVFoundation (compatibility version 1.0.0, current version 2.0.0)
	/System/Library/Frameworks/CoreImage.framework/CoreImage (compatibility version 1.0.0, current version 5.0.0)
	/System/Library/Frameworks/CoreMedia.framework/CoreMedia (compatibility version 1.0.0, current version 1.0.0)
	/System/Library/Frameworks/CoreVideo.framework/CoreVideo (compatibility version 1.2.0, current version 1.5.0)
	/System/Library/Frameworks/Foundation.framework/Foundation (compatibility version 300.0.0, current version 1570.15.0)
	/usr/lib/libobjc.A.dylib (compatibility version 1.0.0, current version 228.0.0)
	/usr/lib/libc++.1.dylib (compatibility version 1.0.0, current version 400.9.4)
	/usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 1252.250.1)
	/System/Library/Frameworks/CoreFoundation.framework/CoreFoundation (compatibility version 150.0.0, current version 1570.15.0)
	/usr/lib/libc++abi.dylib (compatibility version 1.0.0, current version 400.17.0)

我们可以看到链接 opencv2.framework/opencv2 的路径(install Name)是写死的。

而正确的动态库的链接路径(install Name)应该是 @rpath/opencv2.framework/opencv2

知道这一点后我们使用 install_name_tool 工具修改它的链接路径即可,我们可以将它写死的链接路径(install Name)修改成 @rpath/opencv2.framework/opencv2 ,具体操作如下:

cntp@TPL-0000-161520deMacBook-Pro opencv2.framework % install_name_tool h
Usage: /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/install_name_tool [-change old new] ... [-rpath old new] ... [-add_rpath new] ... [-delete_rpath old] ... [-id name] input
cntp@TPL-0000-161520deMacBook-Pro opencv2.framework % install_name_tool -id @rpath/opencv2.framework/opencv2 opencv2
cntp@TPL-0000-161520deMacBook-Pro opencv2.framework % otool -L opencv2
opencv2:
	@rpath/opencv2.framework/opencv2 (compatibility version 4.1.0, current version 4.1.0)
	/System/Library/Frameworks/Accelerate.framework/Accelerate (compatibility version 1.0.0, current version 4.0.0)
	/System/Library/Frameworks/CoreGraphics.framework/CoreGraphics (compatibility version 64.0.0, current version 1251.12.0)
	/System/Library/Frameworks/QuartzCore.framework/QuartzCore (compatibility version 1.2.0, current version 1.11.0)
	/System/Library/Frameworks/AssetsLibrary.framework/AssetsLibrary (compatibility version 1.0.0, current version 1.0.0)
	/System/Library/Frameworks/UIKit.framework/UIKit (compatibility version 1.0.0, current version 61000.0.0)
	/System/Library/Frameworks/AVFoundation.framework/AVFoundation (compatibility version 1.0.0, current version 2.0.0)
	/System/Library/Frameworks/CoreImage.framework/CoreImage (compatibility version 1.0.0, current version 5.0.0)
	/System/Library/Frameworks/CoreMedia.framework/CoreMedia (compatibility version 1.0.0, current version 1.0.0)
	/System/Library/Frameworks/CoreVideo.framework/CoreVideo (compatibility version 1.2.0, current version 1.5.0)
	/System/Library/Frameworks/Foundation.framework/Foundation (compatibility version 300.0.0, current version 1570.15.0)
	/usr/lib/libobjc.A.dylib (compatibility version 1.0.0, current version 228.0.0)
	/usr/lib/libc++.1.dylib (compatibility version 1.0.0, current version 400.9.4)
	/usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 1252.250.1)
	/System/Library/Frameworks/CoreFoundation.framework/CoreFoundation (compatibility version 150.0.0, current version 1570.15.0)
	/usr/lib/libc++abi.dylib (compatibility version 1.0.0, current version 400.17.0)
cntp@TPL-0000-161520deMacBook-Pro opencv2.framework %

使用 install_name_tool h 查看所有指令,根据提示使用 install_name_tool -id @rpath/opencv2.framework/opencv2 opencv2 修改其链接路径(install Name)。

可以再使用 otool 工具验证一下,发现没有问题,然后再将其添加到项目中,因为是动态库,修改 EmbedEmbed&Sign 运行、使用同样没有问题。

至此问题完美解决。

写在最后

关于@rpath、@loader_path、@executable_path

Framework 工程的 TARGETS -> Build Settings 中的 Linking 可以看到 Dynamic Library Install NameDynamic Library Install Name BaseRunpath Search Paths 等配置,根据其名称可以大致了解其含义。

这里先梳理下几个单词的概念。

install Name

install Name 可以理解为安装名称,本质上是一个相对路径,主要是告诉链接器(linker synthesized)在运行时从哪调用需要的库。

就比如刚刚编译的动态库 opencv2.framework ,在编译的时候会被拷贝到应用程序(**.app)下的 Frameworks 目录下,其二进制文件的绝对路径地址大致为 ~/-.app/Frameworks/opencv2.framework/opencv2 ,其中 install Name 就是 @rpath/opencv2.framework/opencv2

当动态链接器需要 opencv2.framework 的时候,它就会从应用程序中根据 install Name 找到 opencv2.framework

@rpath

在维基百科中有关于 rpath 的介绍:

In computing, rpath designates the run-time search path hard-coded in an executable file or library. Dynamic linking loaders use the rpath to find required libraries.

Specifically, it encodes a path to shared libraries into the header of an executable (or another shared library). This RPATH header value (so named in the Executable and Linkable Format header standards) may either override or supplement the system default dynamic linking search paths.

翻译过来就是:

在计算中 rpath指定在可执行文件或库中硬编码的运行时 搜索路径。动态链接加载程序时使用rpath查找所需的库。

具体来说,它将共享库的路径编码为可执行文件(或另一个共享库)的标头。此RPATH标头值(在“ 可执行文件和链接格式”标头标准中如此命名)可以替代或补充系统默认的动态链接搜索路径。

其解释已经十分详细了,我们可以理解为 @rpath 就是一个相对路径。

其中 @rpath/opencv2.framework/opencv2 就相当于我们项目下动态库所在的位置 ~/**.app/Frameworks/opencv2.framework/opencv2

@loader_path

@loader_pathFramework 工程中 TARGETS -> Build Settings -> Linking -> Runpath Search Paths 的一项配置,字面上的意思是加载路径。

其实也就是 Framework 加载的路径,比如 app 下的动态库 opencv2.framework@loader_path 则相当于 ~/**.app/Frameworks/opencv2.framework

@executable_path

@executable_pathFramework 工程中 TARGETS -> Build Settings -> Linking -> Runpath Search Paths 的一项配置,字面上的意思是可执行文件路径。

@loader_path 不同的是 @executable_path 代表的是可执行文件(mach-o)的路径。还以 app 下加载动态库 opencv2.framework 为例,其 @executable_path 则相当于 ~/**.app/Frameworks/opencv2.framework/opencv2

ps:写完 关于@rpath、@loader_path、@executable_path 本来还想将程序如何链接动态库、静态库写一下,最后还是决定单独拎出来写。

同时准备将动、静态库相互依赖;编译打包动、静态库;等注意事项整理下。

参考资料

我来评几句
登录后评论

已发表评论数()

相关站点

热门文章