原文地址
前言
最近在做嵌入式下计算机视觉相关功能的实现,那么交叉编译和opencv
库基本上就是必要条件了,这里记录下相关内容的构建以及猜的一些坑
实现
交叉编译环境的构建
首先我们的构建环境是CentOS Stream release 8 x86_64 GNU/Linux
以及Ubuntu 18.04.6 LTS x86_64 GNU/Linux
,运行环境为armv7l GNU/Linux
ubuntu
Ubuntu
的交叉编译环境构建比较简单,直接使用apt-get
下载即可,参考opencv
的Cross compilation for ARM based Linux systems,构建环境中可选项根据需求安装,但是如果不是特别熟悉opencv
建议全装
centos
Centos
的交叉编译工具链相对稍微麻烦点,首先我们去arm.com
下载相关工具链,Downloads,我们可以看到分别为不同编译环境下的不同运行环境需求提供了工具了,根据我们的构建和运行,需要选择X86_64
下的AArch32 target with hard float
下载后解压tar -xf /usr/local/arm/*.tar.xz
,然后再添加环境变量,export PATH=$PATH:/usr/local/arm/arm-none-linux-gnueabihf/bin
,如果需要永久添加的话将命令写入/etc/profile
或/etc/environment
再source
即可
至此最基本的交叉编译工具链就构建完成了
为运行环境编译opencv库
如果我希望在嵌入式环境中使用opencv
,理所当然需要将opencv
编译成他的形状,那么首先下载源码
mkdir /usr/local/opencv && cd /usr/local/opencv
git clone https://github.com/opencv/opencv.git
然后找到提供的cmake
示例,我们这里是为Linux
平台构建
cd opencv-*/platforms/linux/
根据我们编译以及运行平台,这里选择arm-gnueabi.toolchain.cmake
,但是需要稍微做一些改变适应我们的需求
cp arm-gnueabi.toolchain.cmake arm-gnueabi.toolchain.cmake.bak && vim arm-gnueabi.toolchain.cmake
小伙伴们可以自行增加减少,更多CMake
选项参考CMake Introduction
set(GCC_COMPILER_VERSION "" CACHE STRING "GCC Compiler version")
set(GNU_MACHINE "arm-linux-gnueabi" CACHE STRING "GNU compiler triple")
#指定工具链
set(CMAKE_C_COMPILER /usr/local/arm/arm-none-linux-gnueabihf/bin/arm-none-linux-gnueabihf-gcc)
set(CMAKE_CXX_COMPILER /usr/local/arm/arm-none-linux-gnueabihf/bin/arm-none-linux-gnueabihf-g++)
#生成静态库而不是动态库
set(BUILD_SHARED_LIBS OFF)
#构建详细输出
set(CMAKE_VERBOSE_MAKEFILE ON)
#opencv生成pkconfig便于编译
set(OPENCV_GENERATE_PKGCONFIG ON)
#生成Release版本
set(CMAKE_BUILD_TYPE Release)
#补充构建和链接
set(BUILD_ZLIB ON)
set(BUILD_OPENEXR ON)
set(BUILD_ILMIMF ON)
set(BUILD_TBB ON)
set(BUILD_JASPER ON)
set(BUILD_PNG ON)
set(BUILD_JPEG ON)
set(BUILD_TIFF ON)
set(CMAKE_EXE_LINKER_FLAGS "-pthread -ldl -lrt")
include("${CMAKE_CURRENT_LIST_DIR}/arm.toolchain.cmake")
然后生成Makefile
,同时指定工具链和库安装目录
mkdir build && cd build && cmake -DCMAKE_TOOLCHAIN_FILE=../arm-gnueabi.toolchain.cmake -DCMAKE_INSTALL_PREFIX=/usr/local/opencv/install/ ../../..
最后构建以及安装
make && make install
测试
现在我们以及在编译机器上构建了运行机所需要的opencv
相关库,根据上面的编译命令,opencv
相关头文件可以在/usr/local/opencv/install/include
中获得,而静态库可以在/usr/local/opencv/install/lib
中获得,但是我们如何知道在编译的时候要引用哪些库参加编译呢。这个时候就需要pkg-config
以及opencv
生成的.pc
文件来协助插入正确的编译器选项,定位需要的库
当然,如果不想用pkg-config
,那么直接查看/usr/local/opencv/install/lib/pkgconfig/*.pc
,让根据自己的需求将所需库拷贝到编译命令中也一样可以就是了
如果希望使用pkg-config
,我们需要添加环境变量,让他可以查找到opencv
的.pc
文件,同样,如果需要永久添加的话将命令写入/etc/profile
或/etc/environment
再source
即可
export PKG_CONFIG_PATH=/usr/local/opencv/install/lib/pkgconfig:$PKG_CONFIG_PATH
使用命令pkg-config --cflags --libs --static opencv4
检查是否以及成功被检测到
-I/usr/local/opencv/install/include/opencv4 -L/usr/local/opencv/install/lib -lopencv_gapi -lopencv_highgui -lopencv_ml -lopencv_objdetect -lopencv_photo -lopencv_stitching -lopencv_video -lopencv_calib3d -lopencv_features2d -lopencv_dnn -lopencv_flann -lopencv_videoio -lopencv_imgcodecs -lopencv_imgproc -lopencv_core -L/usr/local/opencv/install/lib/opencv4/3rdparty -llibprotobuf -lade -littnotify -llibjpeg-turbo -llibwebp -llibpng -llibtiff -llibopenjp2 -lIlmImf -lzlib -lquirc -ltegra_hal -ldl -lm -lpthread -lrt
然后我们编写一个简单的测试程序,main.cpp
#include <opencv2/opencv.hpp>
#include <opencv2/core.hpp>
#include <opencv2/highgui.hpp>
#include <opencv2/imgproc.hpp>
#include <opencv2/imgcodecs.hpp>
#include <iostream>
int main(int argc, char* argv[]) {
std::cout << "start test" << std::endl;
const char* diskImg = "/test/test.jpg";
cv::Mat img = cv::imread(diskImg, 1);
if (img.empty()) {
std::cout << "img not found" << std::endl;
return -1;
} else {
std::cout << "loaded img" << std::endl;
}
cv::resize(img, img, cv::Size(100, 100));
cv::imwrite("/test/new.jpg", img);
std::cout << "convert finish" << std::endl;
return 0;
}
然后编译
arm-none-linux-gnueabihf-g++ main.cpp -o testcv -std=c++11 -static $(pkg-config --cflags --libs --static opencv4)
生成可执行文件后,将文件传到运行机器上,直接运行
即可获得模糊后的图片
坑
细心的小伙伴可能发现了,我们在编译的时候使用了-static
来强制让g++
使用静态库,而不是默认的优先使用动态库。这样做好处是基本动态库,比如libstdc++.so
,不会因为运行环境和编译环境gcc
版本不同而报错,因为有些项目是需要相对高版本的gcc
编译的。但坏处是可执行文件过大,不同程序引用相同库没有共享库导致占用运行环境磁盘空间,以及即使是静态引用也不能保证你这个程序就是独立的,因为你静态引用的库仍然可能动态引用其他库,这个就是当我们使用-static
编译时候,会报出
/usr/local/arm/arm-none-linux-gnueabihf/bin/../lib/gcc/arm-none-linux-gnueabihf/10.3.1/../../../../arm-none-linux-gnueabihf/bin/ld: /usr/local/opencv/install/lib/libopencv_core.a(opencl_core.cpp.o): in function `opencl_check_fn(int)':
opencl_core.cpp:(.text._ZL15opencl_check_fni+0x10c): warning: Using 'dlopen' in statically linked applications requires at runtime the shared libraries from the glibc version used for linking
警告的的原因,但是如果我们使用默认的优先动态链接的方式去构建,就需要关注运行环境的基本库和构建环境的基本库版本是否相同的问题了,否则就会报出
/lib/libm.so.6: version `GLIBC_2.29' not found
/lib/libstdc++.so.6: no version information available
这样的错误