opencv交叉编译生成静态库,编译demo

因工作需要,需在marvell平台上交叉编译出一个静态链接opencv库的可执行文件,只调用了其中的imread,resize等少数几个函数。所以我最终只交叉编译了一部分的链接库,video相关的基本都没有编译,这个后续也会描述到。因为是初次接触opencv,之前交叉编译的经验也都没记录下来,特此留存,便于后续查阅。
我将最终要实现交叉编译的过程分成四个阶段来执行,因工作中没有整片时间,拆成小目标容易分阶段完成任务。
  • 在linux上安装opencv。
  • 编译源文件,并生成静态链接到opencv和其他库文件的linux端的可执行文件。
  • 交叉编译opencv生成Marvell端的可执行文件,以及opencv之外的依赖库和部分opencv依赖的库
  • 编译源文件,并生成静态链接到交叉编译的opencv库和其他交叉编译的库的Marvell端的可执行文件
  • 阶段一、在linux上安装opencv。
    • 我的安装环境是 virtualbox虚拟机,ubuntu 14.04- 64bit
    • 这步操作简单,我是参考如下官方链接:https://docs.opencv.org/master/d7/d9f/tutorial_linux_install.html
      • 下载解压源代码到本地后,创建一个build目录,到该目录下执行cmake的操作。
        • cmake -D CMAKE_BUILD_TYPE=Release -D CMAKE_INSTALL_PREFIX=/usr/local ..
        • 注意结尾有 .. ; 即上级目录
      • 然后直接按照要求make即可。
      • 如果按照说明操作,默认可执行文件安装在/usr/local/bin , 链接库在 /usr/local/lib,但默认应该是生成 .so
      • 如果要直接编译静态库,建议安装cmake-gui,指定了源代码目录和build目录后,把configure之后生成的 BUILD_SHARED_LIBS选项去掉,然后才能在/usr/local/lib 下 生成libopencv_xxx.a,这也是为下一步做准备
      • opencv交叉编译生成静态库,编译demo
      • 关于静态编译这篇文章也可参考:http://blog.****.net/xdonx/article/details/38871659
        • 在 cmake时 使用 -DBUILD_SHARE_LIBS=OFF选项应该也是可以的,但我没尝试过。
    • 我下载的是3.3.0版本的源代码,在如下的github链接里,****的资料里应该也是可以搜到的。
  • 阶段二、编译源文件,并生成静态链接到opencv和其他库文件的linux端的可执行文件。
    • 首次完成opencv的安装建议执行 ldconfig 
    • 这步常规是比较简单的,但就是需要注意编译选项,包括  1、要包含的头文件,-I。2、要链接的库所在的文件夹,-L。3、要链接的库-l。
      • 使用pkg-config opencv --cflags --libs 命令,可以提供编译opencv时,目前opencv所有的上述所需的编译选项。我本地执行的效果如下
        • -I/usr/local/include/opencv -I/usr/local/include -L/usr/local/lib -lopencv_dnn -lopencv_ml -lopencv_objdetect -lopencv_shape -lopencv_stitching -lopencv_superres -lopencv_videostab -lopencv_calib3d -lopencv_features2d -lopencv_highgui -lopencv_videoio -lopencv_imgcodecs -lopencv_video -lopencv_photo -lopencv_imgproc -lopencv_flann -lopencv_core
    • 但是直接使用
      • g++ test.cpp -static `pkg-config opencv --cflags --libs`
      • 会出现非常多的链接未定义错误,原因是 opencv库其实还依赖了很多的其他库,要引用opencv的常规库,必须也链接其对应的依赖,而这些需要手动添加。最常见的就是 -lpthread
    • 直接使用寻找依赖库,然后一个一个手动加上显然不是合理的做法。我在网上找了下资料。
      • opencv-3.3.0/build/unix-install/opencv.pc的文件,其中包含了要链接的内容。
        • Libs.private: -L${exec_prefix}/share/OpenCV/3rdparty/lib -littnotify -llibprotobuf -llibwebp -lIlmImf -lgtk-x11-2.0 -lgdk-x11-2.0 -latk-1.0 -lgio-2.0 -lpangoft2-1.0 -lpangocairo-1.0 -lgdk_pixbuf-2.0 -lcairo -lpango-1.0 -lfontconfig -lgobject-2.0 -lglib-2.0 -lfreetype -lgthread-2.0 -L/usr/lib/x86_64-linux-gnu -lpng -lz -ltiff -ljasper -ljpeg -ldc1394 -lavcodec -lavformat -lavutil -lswscale -lstdc++ -ldl -lm -lpthread -lrt
      • 后半段内容应该能满足我的需求。但需要注意对 -L目录进行调整,同时可以去掉一些你不需要的 链接选项
    • 添加到上述g++ test.cpp -static `pkg-config opencv --cflags --libs`后面,果然把需要的依赖库都加进来了,但是此时仍然可能在你的环境下出现错误,因为你可能在本地没有安装过上述的依赖库。上述的依赖库一部分是集成在opencv里面,在安装时就编译了的,另一部分则需要依赖本地。这里我就不多介绍,可自行上网搜索对应的链接库需要如何安装。
    • 介绍一个过程中让我碰到较多难题的链接库 ljbig
      • 我用 sudo aptitude search jbig 是可以找到jbig的,但是应该只有动态链接库,想静态链接到 .a时在默认路径下找不到。
      • 从https://github.com/mvanderkolff/jbigkit-packaging 下载对应package到本地.
      • 编译后(make即可),把生成的 libjbig.a 拷贝到 /usr/lib/x86_64-linux-gnu 目录,让静态编译的时候可以链接到。
      • 拷贝后 sudo ldconfig,将新安装的 库文件导入ld.so.cache.
    • 我最终执行编译源文件的命令如下,参考了opencv中编一个demo时的编译选项和上面的链接选项,因只有单个文件,就没引入Makefile
      • g++test.cpp -static -L/usr/local/lib-L/usr/local/share/OpenCV/3rdparty/lib -lopencv_dnn -lopencv_ml-lopencv_objdetect -lopencv_shape -lopencv_stitching -lopencv_superres-lopencv_videostab -lopencv_calib3d -lopencv_features2d -lopencv_highgui-lopencv_videoio -lopencv_imgcodecs -lopencv_video -lopencv_photo-lopencv_imgproc -lopencv_flann -lopencv_core -littnotify -llibprotobuf-llibwebp -lIlmImf -lgtk-x11-2.0 -lgdk-x11-2.0 -lgio-2.0 -lpangoft2-1.0 -lpangocairo-1.0  -lcairo -lpango-1.0 -lfontconfig-lgobject-2.0 -lglib-2.0 -lfreetype -lgthread-2.0 -lpng -lz -ltiff -ljasper-ljpeg -ldc1394 -lavcodec -lavformat -lavutil -lswscale -lstdc++ -ldl -lm-lpthread -lrt -lz  -llzma-ljbig

    • 这样就完成了第二步,静态编译源文件。
    • 会出现一个warning
      •  warning: Using 'dlopen' in statically linked applications requires at runtime the shared libraries from the glibc version used for linking
      • 但不影响可执行文件执行,故而暂时忽略。
  • 阶段三、交叉编译opencv生成Marvell端的可执行文件,以及opencv之外的依赖库和部分opencv依赖的库
    • 该阶段主要包含两块,一是opencv的交叉编译,二是其他依赖库的交叉编译。
    • 1、我先做了一步简化,就是因为我其实只用到opencv里面的imread,resize等少数几个函数,故而显然不需要将上述所有内容作为编译选项。有一部分opencv的库可以裁剪,这些库对应的依赖库也就可以裁剪。我尝试逐步去掉链接选项,看是否会出现未定义的问题,裁剪最终得到的命令如下
      • g++ test.cpp -static -L/usr/local/lib-L/usr/local/share/OpenCV/3rdparty/lib -lopencv_imgcodecs -lopencv_photo -lopencv_imgproc -lopencv_core-littnotify -llibwebp -lIlmImf  -lpng-ltiff -ljasper -ljpeg  -ldl-lpthread  -lz  -llzma -ljbig
      • 实际只需要链接上面这些库即可。这样后续对依赖库的交叉编译工作其实就减轻了一大半。,然后在对opencv进行交叉编译时,也可以额把上述没有涉及到库不进行编译,也可缩短编译时间和出问题时的排查时间。
    • 2、交叉编译opencv过程我主要参考了下面这篇博文,基本覆盖了我遇到的问题
      • http://blog.****.net/gatieme/article/details/49080355
      • 对博客中提到的内容,我在这里也做一下简述。主要分为以下6个步骤
      • 1)使用Cmake-gui,选择源码目录和build目录,这点与安装opencv时相同。
      • 2)configure生成Makefile时不能选择默认项,要选择第四项“ specify options for cross-compiling” 以配置交叉编译的选项
      • 3)选择交叉编译工具链里的C编译工具,C++编译工具,并选择工具链所在的路径。程序模式注意选择默认项,部分博客说需要修改为只是 Search in Target Root,实测会出现如下错误打印,即找不到部分编译工具,因为 交叉编译工具链目录下只包含了一部分交叉编译工具,一些默认工具,比如Make,还是使用系统的,如果不把系统路径纳入编译工具查找路径,自然会出现查找失败的情况。
        • CMakeError: CMake was unable to find a build program corresponding to"xxx".
      • opencv交叉编译生成静态库,编译demo
      • 4)点击Configure,即可生成配置内容。1)我去掉了一些不相关的video的库的编译。2、注意同样需要去掉BUILD_SHARED_LIBS才能生成静态库。3)注意修改默认安装路径,因为默认会生成到 /usr/local 这样就跟我们安装在linux上的opencv重叠了,可能会出现问题,故而我在这里另外指定了一个路径,用于存放生成产物。但我这步的操作好像实际并没有生效。但不影响,后续产物还是会生成在build目录下,-L链接进来即可。
        • 注意Build选项中需要用到的依赖库都必须要勾选上,包括下表中的BUILD_JASPER,BUILD_OPENEXR等等
      • opencv交叉编译生成静态库,编译demo
      • 5)Generate就会生成Makefile,然后执行Make即可。
      • 6)到对应目录下执行make最后会出现pthread和dl相关的库函数未定义的问题,因为编译时没有加上这些库函数的链接参数,需要在build目录下的CMakeCache.txt文件中配置CMAKE_EXE_LINKER_FLAGS的参数,加上 -lpthread -ldl。就可以解决未定义错误的问题。
      • 博客中最后的问题我未遇到。但我遇到了下面这个问题。
        • libopencv_imgcodecs.so:undefined reference to `png_init_filter_functions_neon'
        • 是说libpng里面的一个函数未定义,怀疑是开源库的问题。如下网页解决了该问题。
        • 我根据其patch,修改了我这边的pngpriv.h文件,重新Make即解决。在如下目录opencv-3.3.0/3rdparty/libpng。find一下即可。
      • 最终生成opencv_traincascade的可执行文件在/build/bin目录下。
    • 3、交叉编译opencv的依赖库
      • 简单了解一下opencv的源码目录框架即可发现,3rdparty目录下就已经包含了opencv自身需要的第三方的库,经对比,-littnotify -llibwebp -lIlmImf  -lpng-ltiff -ljasper -ljpeg 都是opencv源码包里面已经包含了的库。(p.s. 其实opencv用到的这些库都可以了解一下,作为拓展视野,比如ittnotify 我发现就是一个检测内存泄露的库,可能会在其他就会有用到
      • libz,libpthread的库因为比较通用,在交叉编译工具链的默认工具库里就有,后续编译时,链接进来即可。
      • lzma库后来发现其实并未用到。
      • jbig库在上面已经编译过。将能找到的编译工具,gcc,ar,等等都改为我所希望的aarch64-marvell-linux-gnu-系列即可。
  • 阶段四、编译源文件,并生成静态链接到交叉编译的opencv库和其他交叉编译的库的Marvell端的可执行文件
    • 因之前对编译的核心过程理解不深刻(其实现在理解也不深刻 - -!),在这步上我出了不少问题。
    • 首先的问题就是我在交叉产物的目录(就是上面cmake-gui中指定的)下找不到生成的头文件,最终我使用了原来在linux上安装opencv时,在/usr/local/include目录下的头文件,因为我猜想这块头文件应该是一致的,事实证明猜想正确。
    • 然后我使用-L选项把所有用到的Marvell平台的依赖库的位置都包含进来
    • 最终版本的编译执行命令如下。
      • aarch64-marvell-linux-gnu-g++ test.cpp -static -I/usr/local/include  -L/opt/Marvell/marvell_i686_gnu/aarch64-linux-gnu/libc/lib -L/home/xxxx/package/build_dir/lib -L/home/xxxx/package/build_dir/3rdparty/lib -lopencv_imgcodecs -lopencv_photo -lopencv_imgproc -lopencv_core -llibwebp -lIlmImf -lpng -ltiff -ljasper -ljpeg -ldl -lpthread -lz -ljbig
      • -L/opt/Marvell/marvell_i686_gnu/aarch64-linux-gnu/libc/lib 是我的交叉编译工具链的已经经过交叉编译的库所在的目录
      • /home/xxxx/package/build_dir就是我指定的编译产物目录
    • 头文件和链接库的逻辑关系其实还需要再捋顺一些。
  • 如上便是交叉编译opencv生成静态库后,我尝试编译一个最基本的应用的一个过程。