對於某些時候,我們希望能在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!