对于某些时候,我们希望能在python中调用c++代码,或许是为了追求速度,或许是为了调用现成的c++代码。
网上也有很多相关方面的教程,但他们的c++代码仅仅为一个函数或者一个类,情况比较简单。
我找到了一个不错的c++项目,但是我没有能力用python重写,所以我将c++中的main函数写成一个类,并希望导出为共享链接库(.so文件),在python中调用。
我的难点在于我希望导出的这个类,使用了第三方库OpenCV,以及这个类还使用了其他的类,情况一下就复杂了。
实际上这也符合真实情况,如果我只想调用c++实现的一个函数或者单纯的类,为什么不直接用python写呢?实际情况才是如同上面我讲的那样,情况复杂。
注意:opencv.cpp是作者自己写的一个类(类似于RPPG.cpp),而OpenCV是第三方库,不要混淆了
三个类都使用了OpenCV第三方库,同时HB使用了RPPG类和opencv类,RPPG使用了opencv类,而我要导出HB类,使其可以在python中调用,依赖关系复杂了。
所以,我们使用cmake来帮助编译so文件
首先我们在Ubuntu20.04中编译( (一)Ubuntu安装详细教程(从镜像制作到NVIDIA驱动安装全流程)——超详细的图文教程)
先查看是否安装gcc:
如果没有安装:
# 在终端中,依次执行
sudo apt-get update
sudo apt-get install build-essential gdb
先查看是否安装cmake:
如果没有安装,请参阅 Kitware APT存储库中适用于您的平台的说明
# .hpp 头文件,用于申明
# .cpp 实现头文件中申明的函数或类
-project
--HB.cpp
--HB.hpp
--RPPG.cpp
--RPPG.hpp
--opencv.cpp
--opencv.hpp
--......
首先我的结构目录如上,在节2中给出了之间的关系,我们的目的是导出HB.cpp为so文件
我们知道编译时会指定许多参数,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)
可以看出:
在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就是编译好的文件)
编译时成功不代表真的成功,我们需要检查一下。
执行命令$ 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
就可以查看到后面的一串到底代表哪个函数
直接给代码:
import ctypes
dll=ctypes.cdll.LoadLibrary
# 加载so链接库
lib=dll("./libHB.so")
# 这里是调用HB类中的load函数
lib.load()
可以看到C++中HB.load()函数执行成功会打印字符串:
验证一下,运行python代码,ok!