- 1、前言
- 2、介绍
- 3、环境安装
- 3.1 gcc安装
- 3.2 cmake安装
- 4、编译
- 4.1 CMakeLists.txt
- 4.2 编译
- 4.3 检查
- 5、python中调用
- 6、补充:一个更简单的方法编译共享链接库
该系列文章:
(一)python调用c++代码《从C++共享链接库编译到python调用指南》
(二)ndarray(Python)与Mat(C++)数据的传输
(三)C++结构体与python的传输
1、前言
对于某些时候,我们希望能在python中调用c++代码,或许是为了追求速度,或许是为了调用现成的c++代码。
网上也有很多相关方面的教程,但他们的c++代码仅仅为一个函数或者一个类,情况比较简单。
我找到了一个不错的c++项目,但是我没有能力用python重写,所以我将c++中的main函数写成一个类,并希望导出为共享链接库(.so文件),在python中调用。
我的难点在于我希望导出的这个类,使用了第三方库OpenCV,以及这个类还使用了其他的类,情况一下就复杂了。
实际上这也符合真实情况,如果我只想调用c++实现的一个函数或者单纯的类,为什么不直接用python写呢?实际情况才是如同上面我讲的那样,情况复杂。
2、介绍
注意:opencv.cpp是作者自己写的一个类(类似于RPPG.cpp),而OpenCV是第三方库,不要混淆了
三个类都使用了OpenCV第三方库,同时HB使用了RPPG类和opencv类,RPPG使用了opencv类,而我要导出HB类,使其可以在python中调用,依赖关系复杂了。
所以,我们使用cmake来帮助编译so文件
3、环境安装
首先我们在Ubuntu20.04中编译( (一)Ubuntu安装详细教程(从镜像制作到NVIDIA驱动安装全流程)——超详细的图文教程)
3.1 gcc安装
先查看是否安装gcc:
如果没有安装:
# 在终端中,依次执行
sudo apt-get update
sudo apt-get install build-essential gdb
3.2 cmake安装
先查看是否安装cmake:
如果没有安装,请参阅 Kitware APT存储库中适用于您的平台的说明
4、编译
# .hpp 头文件,用于申明
# .cpp 实现头文件中申明的函数或类
-project
--HB.cpp
--HB.hpp
--RPPG.cpp
--RPPG.hpp
--opencv.cpp
--opencv.hpp
--......
首先我的结构目录如上,在节2中给出了之间的关系,我们的目的是导出HB.cpp为so文件
4.1 CMakeLists.txt
我们知道编译时会指定许多参数,CMakeLists.txt就是告诉cmake我们编译时的参数设定。
我们在project文件夹下新建CMakeLists.txt文件,内容如下:
cmake_minimum_required(VERSION 3.0.0) # 最小版本
project(hbp VERSION 0.1.0) # 项目名称
set(CMAKE_CXX_FLAGS "-std=c++11") # 添加c++11标准
find_package(OpenCV REQUIRED) # 添加OpenCV库
include_directories(${OpenCV_INCLUDE_DIRS})
add_library(opencv SHARED opencv.cpp) # 把opencv.cpp导出为链接库,SHARED指定为共享链接库
target_link_libraries(opencv ${OpenCV_LIBS}) #因为opencv.cpp使用了OpenCV,所以将OpenCV链接到opencv中,相当于告诉opencv去哪儿找OpenCV
add_library(RPPG SHARED RPPG.cpp)
target_link_libraries(RPPG ${OpenCV_LIBS}) # RPPG也使用了OpenCV库,也要链接
add_library(HB SHARED HB.cpp)
target_link_libraries(HB ${OpenCV_LIBS}) # HB也使用了OpenCV库
target_link_libraries(RPPG opencv) # RPPG还使用了opencv类
target_link_libraries(HB RPPG) # HB使用了RPPG(同时RPPG链接了opencv,相当于HB间接链接了opencv)
可以看出:
- 第三方库是通过target_link_libraries直接链接
- 自定义类要先通过add_library定义为共享链接库,后面再通过target_link_libraries链接
4.2 编译
在project中新建build文件夹:
-project
--build/
--CMakeLists.txt
--HB.cpp
--HB.hpp
--RPPG.cpp
--RPPG.hpp
--opencv.cpp
--opencv.hpp
--......
再终端中进入build/,执行命令:$ cmake ..
:
再执行:$ make
:
然后就得到了想要的HB.so
文件:
(会自动加lib-前缀,所以libHB.so就是编译好的文件)
4.3 检查
编译时成功不代表真的成功,我们需要检查一下。
执行命令$ ldd -r libHB.so
:
这代表成功了。
失败了是什么样的?
如果我直接按照python调用C++中的函数【最简明教程】编译so文件:$ g++ -o HB.so -shared -fPIC HB.cpp
得到HB.so文件,现在检查一下这个有没有问题$ ldd -r HB.so
:
可以看到出现大量的"undefined symbol:“,从后面的_ZN2cv
8fastFreeEPv可以看出是缺少OpenCV的链接,导致使用的OpenCV函数为"undefined symbol:”,同理还可以看到“RPPG”等。如果你想查看是具体什么函数,你可以执行命令:
c++filt _ZN2cv8fastFreeEPv
就可以查看到后面的一串到底代表哪个函数
5、python中调用
直接给代码:
import ctypes
dll=ctypes.cdll.LoadLibrary
# 加载so链接库
lib=dll("./libHB.so")
# 这里是调用HB类中的load函数
lib.load()
可以看到C++中HB.load()函数执行成功会打印字符串:
验证一下,运行python代码,ok!
6、补充:一个更简单的方法编译共享链接库
实际上我们可以使用一下命令将HB.cpp编译为共享链接库:
g++ -std=c++11 \
-o HB.so \
-shared \
-fPIC HB.cpp opencv.cpp RPPG.cpp \
`pkg-config --cflags --libs opencv`
如此便成功将HB.cpp编译并保存在HB.so文件
下面简单解释一下
g++ -std=c++11 \ # 指定了c++11标准
-o HB.so \ # output
-shared \ # 创建共享链接库
#链接使用到的cpp,整个项目使用到了这三个cpp,所以全写上
-fPIC HB.cpp opencv.cpp RPPG.cpp \ #
# 链接使用的第三方库opencv,你可以通过pkg-config opencv --modversion看看是否正确安装了opencv,如果是则会显示版本号比如3.4.11
`pkg-config --cflags --libs opencv`
注意:pkg-config --cflags --libs opencv
前后的“撇”不是单引号,是左上角1旁边的按键符号