Linux下编译、链接、加载运行C++ OpenCV的两种方式及常见问题的解决

Linux下编译、链接、加载运行C++ OpenCV的两种方式及常见问题的解决

在Linux下安装完OpenCV C++之后(还没有安装的读者请参考Ubuntu 18.04 安装OpenCV C++),本文将探索Linux下编译、链接C++ OpenCV的两种方式,并且给出笔者在初次尝试时遇到的一些问题的解决方法。一般来看,不只是OpenCV,其实本文已经囊括了大部分Linux下C++链接库文件、运行时链接动态库文件的常见问题。各位读者如果链接时遇到其他问题,也可留言讨论。

注意,我们这里并不采用简单的包含进opencv的头文件,然后再main函数中打印个hello world的测试方式。这种测试方式只能测试头文件的正常包含,即只能保证编译通过,我们需要实际地调用一些opencv中的图像处理接口,来保证链接和运行时的正确性。这里,我们调用Canny算子来提取图像的边缘信息。如果这个测试用例能够正常地编译、链接、运行的话,那整个opencv的运行测试才能算是完整通过了。

首先我们准备一个测试源码demo.cpp,和测试图像demo.jpg,把它们放到测试文件夹demo/下。即一开始,我们有目录树如下:

├── demo
│   ├── demo.cpp
│   └── demo.jpg

方法一:CMake

CMake链接的使用方法笔者已经在安装OpenCV的文章中介绍过,这里再复述一遍,这种方法基本不会遇到什么问题。我们先准备一个CMakeLists.txt

cmake_minimum_required(VERSION 2.8)
project( demo )
find_package( OpenCV REQUIRED )
include_directories( ${OpenCV_INCLUDE_DIRS} )
add_executable( demo demo.cpp )
target_link_libraries( demo ${OpenCV_LIBS} )

具体的含义笔者就不在这里赘述了,这需要一些cmake的相关知识,总之按照这个CMakeLists.txt是可以正常编译链接运行包含opencv库的源代码的。我们来试一下:

cmake .
make

过程输出:

$ cmake .
-- The C compiler identification is GNU 7.5.0
-- The CXX compiler identification is GNU 7.5.0
-- Check for working C compiler: /usr/bin/cc
-- Check for working C compiler: /usr/bin/cc -- works
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Detecting C compile features
-- Detecting C compile features - done
-- Check for working CXX compiler: /usr/bin/c++
-- Check for working CXX compiler: /usr/bin/c++ -- works
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Detecting CXX compile features
-- Detecting CXX compile features - done
-- Found OpenCV: /usr/local (found version "4.5.4")
-- Configuring done
-- Generating done
-- Build files have been written to: /home/song/hello/demo/demo
$ make
Scanning dependencies of target demo
[ 50%] Building CXX object CMakeFiles/demo.dir/demo.cpp.o
[100%] Linking CXX executable demo
[100%] Built target dem

经过这两步后,我们直接运行生成的可执行文件demo

./demo

会得到一张Canny算子提取的图像边缘信息Canny.jpg,整个过程没有报错的话,则测试成功。

方法二:通过g++直接命令行编译

笔者在使用这种方法时遇到了不少问题,会记录在后面的常见问题及解决一节,读者在编译过程中遇到问题可以去后面看一下,如果后面也没有你遇到的问题,欢迎留言讨论。

我们回到最初的测试目录的内容,只有demo.cppdemo.jpg

我们先尝试直接编译链接:

g++ demo.cpp -o demo

这样是肯定不行的,会报一大堆undefined reference,因为这样是没有链接我们的opencv库的。但是只这样编译其实是可以的,即:

g++ -c demo.cpp

除非你的头文件都找不到,否则上述-c选项的仅编译的命令是会通过的,会得到一个demo.o可重定向文件。头文件找不到保存请看下面的问题一。

下面我们来解决链接时undefined reference的问题,未定义引用,究其原因无非就是opencv的库文件没有链接进来。对于动态链接库,我们通常使用-l[lib]的选项来链接,但是opencv中要包含的库文件太多了,我们难道要一个一个写吗?

当然不用!我们通常会借助pkg-config工具来生成链接时的库文件选项。如果还没有安装请参考问题二。我们要在pkgconfig的目录下添加opencv.pc来让它帮助生成我们的opencv链接选项。opencv.pc应该是在opencv安装时直接生成的。

但默认是不生成的,笔者当时安装的时候不知道要用到这个,就没有配置,笔者就自己从Stack Overflow抄了个opencv.pc文件,但是弄了大半天都不行,最后才发现是版本太老了,不适合现在的opencv版本了。最终无奈自己写了个脚本把lib目录下opencv的动态链接库名称都提取出来,放到opencv.pc里才成功。(不然这篇博客应该会早几小时出来,本段小吐槽勿怪

笔者把自己提取到的opencv.pc放到问题三了,可能与官网安装时生成的不同,但是亲测使用正常,有需要的小伙伴自取,版本是4.5.4。

将适合自己版本的opencv.pc放到/usr/local/lib/pkgconfig目录下。然后命令行执行pkg-config opencv --libs --cflags,应当会得到如下链接选项:

-I/usr/include/opencv -I/usr/include/opencv2 -L/usr/local/lib -lopencv_aruco -lopencv_barcode -lopencv_bgsegm -lopencv_bioinspired -lopencv_calib3d -lopencv_ccalib -lopencv_core -lopencv_datasets -lopencv_dnn_objdetect -lopencv_dnn -lopencv_dnn_superres -lopencv_dpm -lopencv_face -lopencv_features2d -lopencv_flann -lopencv_freetype -lopencv_fuzzy -lopencv_gapi -lopencv_hfs -lopencv_highgui -lopencv_imgcodecs -lopencv_img_hash -lopencv_imgproc -lopencv_intensity_transform -lopencv_line_descriptor -lopencv_mcc -lopencv_ml -lopencv_objdetect -lopencv_optflow -lopencv_phase_unwrapping -lopencv_photo -lopencv_plot -lopencv_quality -lopencv_rapid -lopencv_reg -lopencv_rgbd -lopencv_saliency -lopencv_shape -lopencv_stereo -lopencv_stitching -lopencv_structured_light -lopencv_superres -lopencv_surface_matching -lopencv_text -lopencv_tracking -lopencv_videoio -lopencv_video -lopencv_videostab -lopencv_wechat_qrcode -lopencv_xfeatures2d -lopencv_ximgproc -lopencv_xobjdetect -lopencv_xphoto

这时我们就可以正常地链接测试源码demo.cpp了:

g++ demo.cpp `pkg-config opencv --libs --cflags` -o demo

注意是反引号:`。

关于以上链接选项的稍微详细一些的介绍,可以参考:gcc参数 -i, -L, -l, -include。

这时我们就应该得到可执行文件demo了,运行它,我们可以得到一张Canny算子提取的图像边缘信息Canny.jpg,整个过程没有报错的话,则测试成功。

如果已经得到了可执行文件demo,但是运行时报错找不到共享库,请参考问题四。

常见问题及解决

问题一:默认安装的头文件路径与搜索的头文件路径不匹配

找不到头文件报错,通常是因为默认安装的头文件路径与搜索的头文件路径不匹配。opencv默认安装的头文件路径是/usr/local/include,而我们搜索的默认路径在/usr/include

遇到这种找不到头文件的报错,请先到/usr/local/include/opencv4/opencv2路径下确认有自己的头文件,然后直接软链接就行了:

sudo ln -s /usr/local/include/opencv4/opencv2 /usr/include/

关于软链接,用法:Linux软链接的使用,详解:Linux中的硬链接和软链接。

或者通过gcc的-I参数来指定头文件的搜索路径:

g++ -c demo.cpp -I/usr/local/include

问题二:pkg-config的安装

下载、解压、安装、验证一气呵成:

# 下载
wget https://pkg-config.freedesktop.org/releases/pkg-config-0.29.2.tar.gz
# 解压
tar -zxvf pkg-config-0.29.2.tar.gz 
# 安装
cd pkg-config-0.29.2/
./configure 
make
make check
sudo make install
# 验证
pkg-config --version
# 输出:0.29.2 安装成功

问题三:OpenCV 4.5.4 可行的opencv.pc文件

这个是笔者自己写脚本提取的opencv.pc文件,可能与官网安装时生成的不同,但是亲测使用正常,放到/usr/local/lib/pkgconfig目录下即可。

prefix=/usr
exec_prefix=${prefix}/local
includedir=${prefix}/include
libdir=${exec_prefix}/lib

Name: opencv
Description: The opencv library
Version: 2.x.x
Cflags: -I${includedir}/opencv -I${includedir}/opencv2
Libs: -L${libdir} -lopencv_aruco -lopencv_barcode -lopencv_bgsegm -lopencv_bioinspired -lopencv_calib3d -lopencv_ccalib -lopencv_core -lopencv_datasets -lopencv_dnn_objdetect -lopencv_dnn -lopencv_dnn_superres -lopencv_dpm -lopencv_face -lopencv_features2d -lopencv_flann -lopencv_freetype -lopencv_fuzzy -lopencv_gapi -lopencv_hfs -lopencv_highgui -lopencv_imgcodecs -lopencv_img_hash -lopencv_imgproc -lopencv_intensity_transform -lopencv_line_descriptor -lopencv_mcc -lopencv_ml -lopencv_objdetect -lopencv_optflow -lopencv_phase_unwrapping -lopencv_photo -lopencv_plot -lopencv_quality -lopencv_rapid -lopencv_reg -lopencv_rgbd -lopencv_saliency -lopencv_shape -lopencv_stereo -lopencv_stitching -lopencv_structured_light -lopencv_superres -lopencv_surface_matching -lopencv_text -lopencv_tracking -lopencv_videoio -lopencv_video -lopencv_videostab -lopencv_wechat_qrcode -lopencv_xfeatures2d -lopencv_ximgproc -lopencv_xobjdetect -lopencv_xphoto

问题四:可执行文件运行时找不到共享库

由于我们Linux默认是动态链接的,即有些共享库是在可执行文件运行时才链接进来的(关于链接、装载与库,可参考:Linux下的ELF文件、链接、加载与库(含大量图文解析及例程)。因此我们正确地链接生成可执行文件demo之后并不能保证正确地运行,可能会找不到共享库报错:

./demo: error while loading shared libraries: libopencv_core.so.4.5: cannot open shared object file: No such file or directory

我们还可以通过ldd命令来查看可执行文件所需要的共享库:

$ ldd demo
	linux-vdso.so.1 (0x00007ffdd25b5000)
	libopencv_core.so.4.5 => not found
	libopencv_imgcodecs.so.4.5 => not found
	libopencv_imgproc.so.4.5 => not found
	libstdc++.so.6 => /usr/lib/x86_64-linux-gnu/libstdc++.so.6 (0x00007fdf87e92000)
	libgcc_s.so.1 => /lib/x86_64-linux-gnu/libgcc_s.so.1 (0x00007fdf87c7a000)
	libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007fdf87889000)
	libm.so.6 => /lib/x86_64-linux-gnu/libm.so.6 (0x00007fdf874eb000)
	/lib64/ld-linux-x86-64.so.2 (0x00007fdf8841f000

确实opencv相关的库都找不到,但是这时我们到共享库目录/usr/local/lib/下去查看,其实是有这个共享库的:

$ ls -l /usr/local/lib/ | grep "core"
lrwxrwxrwx 1 root root         21 107 19:55 libopencv_core.so -> libopencv_core.so.4.5
lrwxrwxrwx 1 root root         23 107 19:55 libopencv_core.so.4.5 -> libopencv_core.so.4.5.4
-rw-r--r-- 1 root root    5247960 107 11:42 libopencv_core.so.4.5.4

既然有这个库还报找不到文件的错误,那这就提醒我们,是不是系统不知道要到这个目录下去找共享库。这时,我们就应该通过指定环境变量LD_LIBRARY_PATH来告诉系统我们想要搜索的共享库目录。即通过以下命令将/usr/local/lib添加到共享库搜索目录的环境变量中:

export LD_LIBRARY_PATH=/usr/local/lib:$LD_LIBRARY_PATH

添加之后,我们可以直接先ldd来查看一下是否已经能够找到共享库:

$ ldd demo
	linux-vdso.so.1 (0x00007ffc15975000)
	libopencv_core.so.4.5 => /usr/local/lib/libopencv_core.so.4.5 (0x00007fb271fde000)
	libopencv_imgcodecs.so.4.5 => /usr/local/lib/libopencv_imgcodecs.so.4.5 (0x00007fb271b1f000)
	libopencv_imgproc.so.4.5 => /usr/local/lib/libopencv_imgproc.so.4.5 (0x00007fb271283000)
	libstdc++.so.6 => /usr/lib/x86_64-linux-gnu/libstdc++.so.6 (0x00007fb270efa000)
	libgcc_s.so.1 => /lib/x86_64-linux-gnu/libgcc_s.so.1 (0x00007fb270ce2000)
	libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007fb2708f1000)
	libdl.so.2 => /lib/x86_64-linux-gnu/libdl.so.2 (0x00007fb2706ed000)
	libpthread.so.0 => /lib/x86_64-linux-gnu/libpthread.so.0 (0x00007fb2704ce000)
	librt.so.1 => /lib/x86_64-linux-gnu/librt.so.1 (0x00007fb2702c6000)
	libz.so.1 => /lib/x86_64-linux-gnu/libz.so.1 (0x00007fb2700a9000)
	libm.so.6 => /lib/x86_64-linux-gnu/libm.so.6 (0x00007fb26fd0b000)
	libjpeg.so.8 => /usr/lib/x86_64-linux-gnu/libjpeg.so.8 (0x00007fb26faa3000)
	libpng16.so.16 => /usr/lib/x86_64-linux-gnu/libpng16.so.16 (0x00007fb26f871000)
	libtiff.so.5 => /usr/lib/x86_64-linux-gnu/libtiff.so.5 (0x00007fb26f5fa000)
	/lib64/ld-linux-x86-64.so.2 (0x00007fb27282d000)
	liblzma.so.5 => /lib/x86_64-linux-gnu/liblzma.so.5 (0x00007fb26f3d4000)
	libjbig.so.0 => /usr/lib/x86_64-linux-gnu/libjbig.so.0 (0x00007fb26f1c6000)

我们看到,所需的共享库已经全部能够找到了。这时我们再运行,即可得到正确结果。

其实总的来说,这里的问题一对应的是编译时期头文件包含的问题,问题二三对应的是链接时期使用pkg-conifg生成对应库文件的链接选项的问题,而问题四对应的则是运行时期共享库搜索的环境变量配置的问题。所以说本文的这一节将一个动态链接的C++库在Linux下编译、链接、加载运行各个阶段会出现的常见问题都涵盖到了。

总之以上就是笔者在编译、链接、加载运行含有OpenCV库的源代码时遇到的一些问题及解决方案了,若有错误遗漏,或读者如果有其他问题不能解决,欢迎留言讨论。

Ref

https://blog.csdn.net/Charliewolf/article/details/101273248

你可能感兴趣的:(OpenCV,C++,Linux,opencv,linux,c++)