C++調用python(一)



一、基本使用方法
二、調用簡單語句
三、調用函數
四、調用類
五、調用SSD目標檢測算法
六、遇到的錯誤


最近訓練一個3D分割的模型,需要將其結合到項目中,由於項目是C++開發,而這邊python訓練好的模型嘗試了ONNX、libtorch等轉換C++也沒有成功,因此考慮采用C++直接調用python代碼,這里對里面用到的一些方法做一個總結,方便以后查看。

一、基本使用方法

1.1 調用步驟

  • 將數據值從C/C++轉換為Python
  • 使用轉換后的值對Python接口例程執行函數調用
  • 將Python調用中的數據值轉換為C/C++

1.2 編譯鏈接

使用python提供的C/C++接口,需要包含python安裝目錄下的頭文件Python.h 編譯、鏈接時需要指定頭文件、python庫的地址。
示例:

include: /home/zjh/anaconda3/envs/learn/include/python3.6m   Python.h
lib: /home/zjh/anaconda3/envs/learn/lib/  libpython3.6m.a/libpython3.6m.so

注:如果需要Numpy庫的話需要找到numpy的地址,一般存放於第三方庫路徑下,如:

/home/zjh/anaconda3/envs/learn/lib/python3.6/site-packages/numpy/core/include/numpy   arrayobject.h

1.3 基本接口

  • 解釋器
    在C/C++調用python之前必須為其指定解釋器環境。
void Py_Initialize():       
    初始化python解釋器.C/C++中調用Python之前必須先初始化解釋器
int Py_IsInitialized():
    返回python解析器的是否已經初始化完成,如果已完成,返回大於0,否則返回0
void Py_Finalize() :
    撤銷Py_Initialize()和隨后使用Python/C API函數進行的所有初始化,
    並銷毀自上次調用Py_Initialize()以來創建並未被銷毀的所有子解釋器。
  • 格式轉換
    在C/C++中,所有的Python類型都被聲明為PyObject類型,為了讓C/C++能夠操作python的數據,python提供了C語言數據類型到PyObject類型的轉換接口。

    • 數字/字符串
    PyObject* Py_BuildValue( const char *format, ...)
      Py_BuildValue()提供了類似c語言printf的參數構造方法,format是要構造的參數的類型列表,函數中剩余的參數即要轉換的C語言中的整型、浮點型或者字符串等。
    其返回值為PyObject型的指針。
    

    format對應的類型參見官網, 如:

    s(str或None)[char *]
    使用'utf-8'編碼將以null結尾的C字符串轉換為Python str對象。如果C字符串指針為NULL,則表示None。
    
    i(int)[int]
    將普通的C int轉換為Python整數對象。
    ...
    
    • 列表
    PyObject* PyList_New( Py_ssize_t len)
      創建一個新的Python列表,len為所創建列表的長度
    
    int PyList_SetItem( PyObject *list, Py_ssize_t index, PyObject *item)
      向列表中添加項。當列表創建以后,可以使用PyList_SetItem()函數向列表中添加項。 list:要添加項的列表。 index:所添加項的位置索引。 item:所添加項的值。
    
    PyObject* PyList_GetItem( PyObject *list, Py_ssize_t index)
      獲取列表中某項的值。list:要進行操作的列表。index:項的位置索引。
    
    Py_ssize_t PyList_Size(PyObject * list)
      返回列表中列表對象的長度;這相當於列表對象上的 len(list) 。
    
    int PyList_Append( PyObject *list, PyObject *item)
    int PyList_Sort( PyObject *list)
    int PyList_Reverse( PyObject *list)
      Python/C API中提供了與Python中列表操作相對應的函數。例如
    列表的append方法對應於PyList_Append()函數。
    列表的sort方法對應於PyList_Sort()函數。
    列表的reverse方法對應於PyList_Reverse()函數。
    
    • 元組
    PyObject* PyTuple_New( Py_ssize_t len) 
      PyTuple_New()函數返回所創建的元組。其函數原型如下所示。len:所創建元組的長度。 
    
    int PyTuple_SetItem( PyObject *p, Py_ssize_t pos, PyObject *o) 
      當元組創建以后,可以使用PyTuple_SetItem()函數向元組中添加項。p:所進行操作的元組,pos:所添加項的位置索引,o:所添加的項值。
    
    PyObject* PyTuple_GetItem( PyObject *p, Py_ssize_t pos)
      可以使用Python/C API中PyTuple_GetItem()函數來獲取元組中某項的值。p:要進行操作的元組,pos:項的位置索引
    
    Py_ssize_t PyTuple_Size(PyObject * p)
      獲取指向元組對象的指針,並返回該元組的大小。
    
    int _PyTuple_Resize( PyObject **p, Py_ssize_t newsize) 
      當元組創建以后可以使用_PyTuple_Resize()函數重新調整元組的大小。其函數原型如下所示。p:指向要進行操作的元組的指針,newsize:新元組的大小
    
    • 字典
    PyObject* PyDict_New() 
      PyDict_New()函數返回所創建的字典。
    
    
    int PyDict_SetItem( PyObject *p, PyObject *key, PyObject *val) 
    int PyDict_SetItemString( PyObject *p, const char *key, PyObject *val) 
      當字典創建后,可以使用PyDict_SetItem()函數和PyDict_SetItemString()函數向字典中添加項。 其參數含義如下。
    p:要進行操作的字典。key:添加項的關鍵字,
    對於PyDict_SetItem()函數其為PyObject型,
    對於PyDict_SetItemString()函數其為char型,val:添加項的值。 
    
    
    PyObject* PyDict_GetItem( PyObject *p, PyObject *key) 
    PyObject* PyDict_GetItemString( PyObject *p, const char *key) 
      使用Python/C API中的PyDict_GetItem()函數和PyDict_GetItemString()函數來獲取字典中某項的值。它們都返回項的值。
    其參數含義如下。p:要進行操作的字典,key:添加項的關鍵字,
    對於PyDict_GetItem()函數其為PyObject型
    對於PyDict_GetItemString()函數其為char型。 
    
    PyObject* PyDict_Items( PyObject *p) 
    PyObject* PyDict_Keys( PyObject *p) 
    PyObject* PyDict_Values( PyObject *p) 
      在Python/C API中提供了與Python中字典操作相對應的函數。例如
      字典的item方法對應於PyDict_Items()函數。
      字典的keys方法對應於PyDict_Keys()函數。
      字典的values方法對應於PyDict_Values()函數。
    其參數p:要進行操作的字典。
    
  • 返回值解析
    python執行完返回的結果也是PyObject類型,因此需要將PyObject類型轉換為C/C++類型。

int PyArg_Parse( PyObject *args, char *format, ...)
    根據format把args的值轉換成c類型的值,[format](https://docs.python.org/3/c-api/arg.html)接受的類型和上述Py_BuildValue()的是一樣的,
  • 釋放資源
    Python使用引用計數機制對內存進行管理,實現自動垃圾回收。在Python/C API中提供了Py_CLEAR()、Py_DECREF()等宏來對引用計數進行操作。
    當使用Python/C API中的函數創建列表、元組、字典等后,就在內存中生成了這些對象的引用計數,在對其完成操作后應該使用Py_CLEAR()、Py_DECREF()等宏來銷毀這些對象。
void Py_CLEAR(PyObject *o)
void Py_DECREF(PyObject *o)
其中,o的含義是要進行操作的對象。
對於Py_CLEAR()其參數可以為NULL指針,此時,Py_CLEAR()不進行任何操作。而對於Py_DECREF()其參數不能為NULL指針,否則將導致錯誤。

二、調用簡單語句

首先編寫CMakeLists.txt文件,引入python所需頭文件和庫文件,python版本是3.6.5

  • CMakeLists.txt
cmake_minimum_required(VERSION 3.9)

project(helloworld)

set(SDK_VERSION 0_0_1)
# >>> build type 
set(CMAKE_BUILD_TYPE "Release")				# 指定生成的版本
set(CMAKE_CXX_FLAGS_DEBUG "$ENV{CXXFLAGS} -O0 -Wall -g2 -ggdb")
set(CMAKE_CXX_FLAGS_RELEASE "$ENV{CXXFLAGS} -O3 -Wall")
# <<<


# >>> CXX11 
set(CMAKE_CXX_STANDARD 11)				# C++ 11 編譯器
SET(CMAKE_CXX_STANDARD_REQUIRED TRUE)
# <<< 


# >>> Python3 
set(PYTHON_ROOT "/home/zjh/anaconda3/envs/learn")
message("python root: " ${PYTHON_ROOT})
include_directories(${PYTHON_ROOT}/include/)
link_directories(${PYTHON_ROOT}/lib/)
# <<<

# --- generate ---
add_executable(helloworld helloworld.cpp)
target_link_libraries(helloworld -lpython3.6m)
  • helloworld.cpp
#include <python3.6m/Python.h>

int main(int argc, char *argv[])
{
    wchar_t *program = Py_DecodeLocale(argv[0], nullptr);
    if ( program == nullptr ){
        std::cout << "Fatal Error: cannot decode argv[0]!" << std::endl;
        return -1;
    }
    Py_SetProgramName(program);
    Py_Initialize();    ## 初始化

    PyRun_SimpleString("print('hello world!')");

    Py_Finalize();      ## 釋放資源
    PyMem_RawFree(program);

    return 0;
}

CMakeLists.txthelloworld.cpp兩個文件放在同一文件夾下,在新建build文件夾,進行編譯構建,構建成功后目錄下會生成helloworld可執行文件。

$ cmake..
$ make

參考鏈接:

https://docs.python.org/2/extending/embedding.html
https://zhuanlan.zhihu.com/p/79896193
https://blog.csdn.net/ziweipolaris/article/details/83689597
https://blog.csdn.net/u011681952/article/details/92765549
https://blog.csdn.net/hnlylyb/article/details/89498651


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM