C++調用Python(混合編程)函數整理總結

 

 

C++調用python概述

python是一種非常強大的膠水語言,可以靈活的嵌入到c++和java等主流語言中。python提供了一套C的API庫,使得開發者能夠很方便的從C、C++的程序中調用python中的各個功能模塊。

c++ 調用 python ,本質上是在 c++ 中啓動了一個 python 解釋器,由解釋器對 python 相關的代碼進行執行,執行完畢後釋放資源,達到調用目的。

從操作步驟上看,C++調用Python低層接口可以分爲幾個階段:

  • 初始化Python解釋器
  • 從C++到Python轉換數據
  • 用轉換後的數據做參數調用Python函數
  • 把函數返回值轉換爲C++數據結構

說白了,即寫一個C文件,執行【Python解釋器初始化、導入模塊,導入函數,構造輸入參數,調用函數,解析返回值,終止Python解釋器】。

相關官方文檔

官方文檔python和C相互調用

相關函數

1.初始化python解釋器環境

和環境相關的接口如下:

void Py_Initialize():       
    初始化python解釋器.C/C++中調用Python之前必須先初始化解釋器
int Py_IsInitialized():
    返回python解析器的是否已經初始化完成,如果已完成,返回大於0,否則返回0
void Py_Finalize() :
    撤銷Py_Initialize()和隨後使用Python/C API函數進行的所有初始化,
    並銷燬自上次調用Py_Initialize()以來創建併爲被銷燬的所有子解釋器。

2.調用python腳本的靜態簡單方式

int PyRun_SimpleString(const char*) :
    執行一個簡單的執行python腳本命令的函數

int PyRun_SimpleFile(FILE *fp, const char *filename):
    從fp中把python腳本的內容讀取到內容中並執行,filename應該爲fp對應的文件名

方法示例:

#include "Python.h"

int main()
{
    Py_Initialize();    // 初始化

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

    Py_Finalize();      //釋放資源
}

這種方法存在的問題:

  • 把python代碼當作一個字符串傳給解釋器來執行。
  • 不管是字符串還是文件,都只能用於c++不需要像python傳參,同時python不會向c++返回值的情況,只執行固定腳本的場景。
  • 但是,實際的場景中是必然存在C++向python傳參,python返回結果的,這個需求下我們要怎樣做呢?

3.動態加載python模塊並執行函數

python api提供了動態加載模塊並且執行函數的能力,具體會涉及到下面幾個api。

//加載模塊
PyObject* PyImport_ImportModule(char *name)

PyObject* PyImport_Import(PyObject *name)
PyObject* PyString_FromString(const char*)
    上面兩個api都是用來動態加載python模塊的。區別在於前者一個使用的是C的字符串,而後者的name是一個python對象,
這個python對象需要通過PyString_FromString(const char*)來生成,其值爲要導入的模塊名


//導入函數相關
PyObject* PyModule_GetDict( PyObject *module)
    PyModule_GetDict()函數可以獲得Python模塊中的函數列表。PyModule_GetDict()函數返回一個字典。字典中的關鍵字爲函數名,值爲函數的調用地址。
字典裏面的值可以通過PyDict_GetItemString()函數來獲取,其中p是PyModule_GetDict()的字典,而key則是對應的函數名

PyObject* PyObject_GetAttrString(PyObject *o, char *attr_name)
     PyObject_GetAttrString()返回模塊對象中的attr_name屬性或函數,相當於Python中表達式語句:o.attr_name

//調用函數相關
PyObject* PyObject_CallObject( PyObject *callable_object, PyObject *args)
PyObject* PyObject_CallFunction( PyObject *callable_object, char *format, ...)
    使用上面兩個函數可以在C程序中調用Python中的函數。callable_object爲要調用的函數對象,也就是通過上述導入函數得到的函數對象,
而區別在於前者使用python的tuple來傳參,後者則使用類似c語言printf的風格進行傳參。
如果不需要參數,那麼args可能爲NULL。返回成功時調用的結果,或失敗時返回NULL。
這相當於Python表達式 apply(callable_object, args) 或 callable_object(*args)
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24

3.1不帶參數和返回值的舉例說明

python腳本

#cat script/sayHello.py
def say():
	print("hello")

C++代碼

#include <Python.h> //C++和python混合編程的頭文件
#include <iostream>
using namespace std;

int main() {
	//初始化python解釋器
	Py_Initialize();
	if (!Py_IsInitialized()) {
		cout << "python init fail" << endl;
		return 0;
	}
	//初始化python系統文件路徑,保證可以訪問到 .py文件
	//PyRun_SimpleString:把python代碼當作一個字符串傳給解釋器來執行。
	PyRun_SimpleString("import sys");
	/*把python腳本文件放入當前目錄下的script文件夾下
	sys.path是一個列表 list, 它裏面包含了已經添加到系統的環境變量路徑。
	當我們要添加自己的引用模塊搜索目錄時,可以通過列表 list 的 append()方法;*/
	PyRun_SimpleString("sys.path.append('./script')");

	//PyImport_ImportModule:動態加載python模塊,相當於導入python腳本文件
	//調用python文件名。當前的測試python文件名是test.py。在使用這個函數的時候,只需要寫文件的名稱就可以了。不用寫後綴。
	PyObject* pModule = PyImport_ImportModule("sayHello");
	if (pModule == NULL) {
		cout << "module not found" << endl;
		return 1;
	}

	/*PyObject* PyObject_GetAttrString(PyObject *o, char *attr_name)
     PyObject_GetAttrString()返回模塊對象中的attr_name屬性或函數,
	 相當於Python中表達式語句:o.attr_name
	 相當於找到導入的python腳本文件裏邊的某個函數*/
	PyObject* pFunc = PyObject_GetAttrString(pModule, "say");
	if (!pFunc || !PyCallable_Check(pFunc)) {
		cout << "not found function add_num" << endl;
		return 0;
	}

	/*PyObject_CallObject:在C程序中調用python函數
	參數1:通過導入函數獲得的函數對象
	參數2:被調用函數所需的參數*/
	PyObject_CallObject(pFunc, NULL);

	/* 撤銷Py_Initialize()和隨後使用Python/C API函數進行的所有初始化,
    並銷燬自上次調用Py_Initialize()以來創建併爲被銷燬的所有子解釋器。*/
	Py_Finalize();

	return 0;
}

3.2帶參數和返回值的舉例說明

注意
要在release下進行否則會報錯。
C++調用python程序時,出現如下問題:
無法解析的外部符號 __imp___Py_RefTotal
無法解析的外部符號 __imp___Py_NegativeRefcount

參數
在C/C++中,所有的Python類型都被聲明爲PyObject型,爲了能夠讓C++能夠操作python的數據,python提供了python各種數據類型和C語言數據類型的轉換操作,具體的使用方法見參考鏈接。
在Python/C API中提供了Py_BuildValue()函數對數字和字符串進行轉換處理,使之變成Python中相應的數據類型。其函數原型如下所示:

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

返回值
python函數的返回值也是PyObject類型,因此,在python腳本返回到C/C++之後,需要解構Python數據爲C的類型,這樣C/C++程序中才可以使用Python裏的數據。但是,由於python的返回值有多種數據結構類型,因此,我們需要爲每個類型進行轉換。
總體思路都是根據類型逐個從值從PyObject中提取。python提供了下面函數來完成這個功能

int PyArg_Parse( PyObject *args, char *format, ...)
     根據format把args的值轉換成c類型的值,format接受的類型和上述Py_BuildValue()的是一樣的

 

釋放資源
Python使用引用計數機制對內存進行管理,實現自動垃圾回收。在C/C++中使用Python對象時,應正確地處理引用計數,否則容易導致內存泄漏。在Python/C API中提供了Py_CLEAR()、Py_DECREF()等宏來對引用計數進行操作。
每個PyObject對象都有一個引用計數,用於垃圾回收,如果不能在恰當的時候增加(Py_INCREF)或減少(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指針,否則將導致錯誤。
#include <Python.h> //C++和python混合編程的頭文件
#include <iostream>
using namespace std;

int main() {
	//初始化python解釋器
	Py_Initialize();
	if (!Py_IsInitialized()) {
		cout << "python init fail" << endl;
		return 0;
	}
	//PyRun_SimpleString:把python代碼當作一個字符串傳給解釋器來執行。
	PyRun_SimpleString("import sys");
	/*把python腳本文件放入當前目錄下的script文件夾下
	sys.path是一個列表 list, 它裏面包含了已經添加到系統的環境變量路徑。
	當我們要添加自己的引用模塊搜索目錄時,可以通過列表 list 的 append()方法;*/
	PyRun_SimpleString("sys.path.append('./script')");

	//PyImport_ImportModule:動態加載python模塊,相當於導入python腳本文件
	PyObject* pModule = PyImport_ImportModule("sayHello");
	if (pModule == NULL) {
		cout << "module not found" << endl;
		return 1;
	}

	/*PyObject* PyObject_GetAttrString(PyObject *o, char *attr_name)
     PyObject_GetAttrString()返回模塊對象中的attr_name屬性或函數,
	 相當於Python中表達式語句:o.attr_name
	 相當於找到導入的python腳本文件裏邊的某個函數*/
	PyObject* pFunc = PyObject_GetAttrString(pModule, "add_num");
	if (!pFunc || !PyCallable_Check(pFunc)) {
		cout << "not found function add_num" << endl;
		return 0;
	}

	/*將參數轉換爲PyObject類型*/
	PyObject* args = Py_BuildValue("(ii)", 5, 2);

	/*PyObject_CallObject:在C程序中調用python函數
	參數1:通過導入函數獲得的函數對象
	參數2:被調用函數所需的參數*/
	PyObject* pRet = PyObject_CallObject(pFunc, args);

	//釋放參數內存
	Py_DECREF(args);

	int res = 0;
	//把參數返回值轉換爲C類型
	PyArg_Parse(pRet, "i", &res);
	//釋放返回值內存
	Py_DECREF(pRet);
	cout << res << endl;

	Py_DECREF(pModule);
	Py_DECREF(pFunc);
	/* 撤銷Py_Initialize()和隨後使用Python/C API函數進行的所有初始化,
    並銷燬自上次調用Py_Initialize()以來創建併爲被銷燬的所有子解釋器。*/
	Py_Finalize();

	return 0;
}

4. c++調用numpy和OpenCV

前言
首先要查看python的版本是release版本還是debug版本,一般安裝的python都是Release版本。VS編寫C++時,改爲Release模式,這要與python的版本一致,否則會報錯:

無法解析的外部符號 __imp___Py_RefTotal

如果將版本調整爲相同的Release,則不會存在此問題。

後續見參考鏈接,感覺不是很全面。

5.C++調用python顯示圖片(在python中顯示)

第一種方法:圖片在python中顯示,無返回值。
python代碼

# opencv顯示一張圖片
import cv2


def show_image(img_path):
    img = cv2.imread(img_path)
    cv2.imshow("img-show", img)
    cv2.waitKey(0)
    cv2.destroyAllWindows()


if __name__ == '__main__':
    show_image('E:/green_screen_keying/test_set/test1.png')

C++代碼

#include<iostream>
#include<Python.h>

using namespace std;

int main() {
	Py_Initialize();
	if (!Py_IsInitialized()) {
		cout << "python initialize failed!" << endl;
		return 0;
	}
	PyRun_SimpleString("import sys");
	PyRun_SimpleString("sys.path.append('./script')");
	
	PyObject* pModule = PyImport_ImportModule("show_img");
	if (pModule == NULL) {
		cout << "module not found!" << endl;
		return 0;
	}
	PyObject* pFunc = PyObject_GetAttrString(pModule, "show_image");
	if (pFunc == NULL || PyCallable_Check(pFunc) == NULL) {
		cout << "function not found!" << endl;
		return 0;
	}

	PyObject* args = Py_BuildValue("(s)", "E:/green_screen_keying/test_set/test1.png");

	PyObject_CallObject(pFunc, args);

	Py_DECREF(pModule);
	Py_DECREF(pFunc);
	Py_DECREF(args);

	Py_Finalize();

	return 0;
}

 

6.C++調用python顯示圖片(在C++中顯示)

C++使用numpy返回值的前提
環境:

  • Visual Studio頭文件目錄:D:\Program Files\Python36\Lib\site-packages\numpy\core\include,並在代碼中#include <numpy/arrayobject.h>
  • 關鍵代碼:在Py_Initialize();之後必須調用import_array();以加載所有numpy函數(C API),與加載dll類似。

C++使用opencv顯示圖片的前提

  • Visual Studio配置包含目錄,D:\Program Files\opencv3\build\include
  • Visual Studio配置庫目錄,D:\Program Files\opencv3\build\x64\vc15\lib
  • Visual Studio配置鏈接器輸入:opencv_world341.lib
  • 追加Path環境變量:Path=Path;D:\Program Files\opencv3\build\x64\vc15\bin,改完環境變量一定要重啓Visual Studio才能生效。

python代碼

# opencv顯示一張圖片
import cv2


def show_image(img_path):
    img = cv2.imread(img_path)
    cv2.imshow("img-show", img)
    cv2.waitKey(0)
    cv2.destroyAllWindows()


def show_image_return_numpy(img_path):
    img = cv2.imread(img_path)
    img = cv2.resize(img, (400, 400))  # numpy類型
    return img


if __name__ == '__main__':
    show_image('E:/green_screen_keying/test_set/test1.png')

C++代碼

#include<iostream>
#include<Python.h>
#include <numpy/arrayobject.h>//numpy的頭文件
#include<opencv/cv.hpp>//opencv的頭文件
using namespace cv;
using namespace std;

int main() {
	Py_Initialize();
	if (!Py_IsInitialized()) {
		cout << "python initialize failed!" << endl;
		return 0;
	}
	import_array();//加載numpy相關的庫

	PyRun_SimpleString("import sys");
	PyRun_SimpleString("sys.path.append('./script')");
	
	PyObject* pModule = PyImport_ImportModule("show_img");
	if (pModule == NULL) {
		cout << "module not found!" << endl;
		return 0;
	}
	PyObject* pFunc = PyObject_GetAttrString(pModule, "show_image_return_numpy");
	if (pFunc == NULL || PyCallable_Check(pFunc) == NULL) {
		cout << "function not found!" << endl;
		return 0;
	}

	PyObject* args = Py_BuildValue("(s)", "E:/green_screen_keying/test_set/test1.png");

	//PyObject_CallObject(pFunc, args);
	PyObject* pRetValue = PyObject_CallObject(pFunc, args);

	/* 解析返回結果 */
	PyArrayObject* ret_array;
	PyArray_OutputConverter(pRetValue, &ret_array);
	//npy_intp代表數組的維度,指向數組的尺寸/形狀的指針。
	npy_intp* shape = PyArray_SHAPE(ret_array);
	Mat imgReturn(shape[0], shape[1], CV_8UC3, PyArray_DATA(ret_array));
	
	cv::imshow("res", imgReturn);
	cv::waitKey(0);

	Py_DECREF(pModule);
	Py_DECREF(pFunc);
	Py_DECREF(args);
	Py_DECREF(pRetValue);
	Py_DECREF(ret_array);

	Py_Finalize();

	return 0;
}

7.C++調用python深度學習顯示結果圖片(在C++中顯示)

注意:
深度學習得到的結果轉換成numpy類型是0~1之間的小數,即使*255也是float32類型,而C++生成Mat需要的是uint8的numpy類型。

python測試代碼

import torch
import os
from torchvision import transforms as T
from torchvision.transforms.functional import to_pil_image
from threading import Thread
from torch.nn import functional as F
from model.model import MattingDGF
from PIL import Image
import cv2
import numpy as np


def show_image_return_numpy(img_path):
    img = cv2.imread(img_path)
    img = cv2.resize(img, (400, 400))  # numpy類型
    return img


def test_image_c(images_src):
    output_dir = "E:/VSProjects/out/"
    output_types = ('pha', 'com')

    device = torch.device('cuda')
    # Load model
    model = MattingDGF('mobilenetv2')
    model = model.to(device).eval()
    model.load_state_dict(torch.load("E:/green_screen_keying/deep-learning-V3-main/deep-learning-V3-main/TrainedModel"
                                     "-V3/GSK-V3-3.pth", map_location=device))

    # Worker function
    def writer(img, path):
        img = to_pil_image(img[0].cpu())
        img.save(path)

    # 讀取單張圖像數據
    transforms = T.Compose([T.Resize((1080, 1920)), T.ToTensor()])
    with Image.open(images_src) as img:
        img = img.convert('RGB')
    src = transforms(img)
    src = src.to(device, non_blocking=True)
    src = torch.unsqueeze(src, 3).permute(3, 0, 1, 2)  # [B,C,H,W]

    # Conversion loop
    with torch.no_grad():
        pred_pha_hr, pred_fgr_hr, pred_pha_lr, pred_fgr_lr, pred_err_lr = model(src)

    # 轉換回numpy格式
    # 返回com
    tgt_bgr = torch.tensor([1.0, 1.0, 1.0], device=device).view(1, 3, 1, 1)
    com = pred_fgr_hr * pred_pha_hr + tgt_bgr * (1 - pred_pha_hr)
    com = com.cpu().permute(2, 3, 1, 0).squeeze(3).numpy()
    com = cv2.cvtColor(com, cv2.COLOR_BGR2RGB)*255
    # com = cv2.resize(com, (400, 400))
    # 返回pha
    # pha = pred_pha_hr.cpu().permute(2, 3, 1, 0).squeeze(3).numpy()
    # pha = cv2.resize(pha, (400, 400))*255
    return com.astype(np.uint8)

if __name__ == "__main__":
    img_src = "E:/green_screen_keying/test_set/test2.png"
    pha = test_image_c(img_src)  # (400,400,3) numpy
    # pha = show_image_return_numpy(img_src) #(400,400,3) numpy
    print(type(pha))
    print(pha.shape)
    print(pha.dtype)
    cv2.imshow("img-show", pha)
    cv2.waitKey(0)
    cv2.destroyAllWindows()

C++代碼

#include<iostream>
#include<Python.h>
#include <numpy/arrayobject.h>//numpy的頭文件
#include<opencv/cv.hpp>//opencv的頭文件
using namespace cv;
using namespace std;

int main() {
	Py_Initialize();
	if (!Py_IsInitialized()) {
		cout << "python initialize failed!" << endl;
		return 0;
	}
	import_array();//加載numpy相關的庫

	PyRun_SimpleString("import sys");
	PyRun_SimpleString("sys.path.append('./script/code-V3')");

	PyObject* pModule = PyImport_ImportModule("test_image_C++");
	if (pModule == NULL) {
		cout << "module not found!" << endl;
		return 0;
	}
	PyObject* pFunc = PyObject_GetAttrString(pModule, "test_image_c");
	if (pFunc == NULL || PyCallable_Check(pFunc) == NULL) {
		cout << "function not found!" << endl;
		return 0;
	}

	PyObject* args = Py_BuildValue("(s)", "E:/green_screen_keying/test_set/test2.png");

	PyObject* pRetValue = PyObject_CallObject(pFunc, args);
	
	/* 解析返回結果 */
	PyArrayObject* ret_array;
	PyArray_OutputConverter(pRetValue, &ret_array);
	npy_intp* shape = PyArray_SHAPE(ret_array);
	Mat mat(shape[0], shape[1], CV_8UC3, PyArray_DATA(ret_array));
	

	cv::imshow("res", mat);
	cv::waitKey(0);

	Py_DECREF(pModule);
	Py_DECREF(pFunc);
	Py_DECREF(args);
	Py_DECREF(ret_array);
	//Py_DECREF(pRetValue);
	

	Py_Finalize();

	return 0;
}

C++接收python返回的numpy的list

#include<iostream>
#include<Python.h>
#include <numpy/arrayobject.h>//numpy的頭文件
#include<opencv/cv.hpp>//opencv的頭文件
using namespace cv;
using namespace std;

int main() {
	Py_Initialize();
	if (!Py_IsInitialized()) {
		cout << "python initialize failed!" << endl;
		return 0;
	}
	import_array();//加載numpy相關的庫

	PyRun_SimpleString("import sys");
	PyRun_SimpleString("sys.path.append('./script/code-V3')");

	PyObject* pModule = PyImport_ImportModule("test_image_C++");
	if (pModule == NULL) {
		cout << "module not found!" << endl;
		return 0;
	}
	PyObject* pFunc = PyObject_GetAttrString(pModule, "test_image_c");
	if (pFunc == NULL || PyCallable_Check(pFunc) == NULL) {
		cout << "function not found!" << endl;
		return 0;
	}

	PyObject* args = Py_BuildValue("(s)", "E:/green_screen_keying/test_set/test2.png");

	PyObject* pRetValue = PyObject_CallObject(pFunc, args);
	
	/* 解析返回結果 */
	PyArrayObject* array_com,*array_pha;
	PyArray_OutputConverter(PyList_GetItem(pRetValue, 0), &array_com);
	PyArray_OutputConverter(PyList_GetItem(pRetValue, 1), &array_pha);
	npy_intp* shape = PyArray_SHAPE(array_com);
	Mat com(shape[0], shape[1], CV_8UC3, PyArray_DATA(array_com));
	Mat pha(shape[0], shape[1], CV_8UC1, PyArray_DATA(array_pha));
	

	cv::imshow("com", com);
	cv::waitKey(0);
	cv::imshow("pha", pha);
	cv::waitKey(0);

	Py_DECREF(pModule);
	Py_DECREF(pFunc);
	Py_DECREF(args);
	Py_DECREF(array_com);
	Py_DECREF(array_pha);
	//Py_DECREF(pRetValue);
	

	Py_Finalize();

	return 0;
}

8.將圖像mat數據轉換成numpy傳遞給python

注意:
C++那裏輸入的圖像參數必須得是tuple類型的參數,即使只有一個圖像參數。
python代碼

import cv2

def simple_func(a,b):return a+b

def super_resolution(img, scale=4):
    height, width = img.shape[:2]
    dsize = (width*scale, height*scale)
    big_img = cv2.resize(img, dsize)
    return big_img

C++代碼

#include <Python.h> 
#include <iostream>
#include <numpy/arrayobject.h>
#include<opencv/cv.hpp>
using namespace std;
using namespace cv;

int main() {
	Py_Initialize();
	import_array();
	/* 讀圖 */
	Mat sml_img = imread("E:/green_screen_keying/test_set/test2.png");

	/* 導入模塊和函數 */
	PyRun_SimpleString("import sys");
	PyRun_SimpleString("sys.path.append('./script')");
	PyObject* pName = PyUnicode_DecodeFSDefault("simple_module");
	PyObject* pModule = PyImport_Import(pName);
	PyObject* pFunc = PyObject_GetAttrString(pModule, "super_resolution");
	/* 準備輸入參數 */
	PyObject* pArgs = PyTuple_New(2);

	npy_intp dims[] = { sml_img.rows, sml_img.cols, sml_img.channels() };
	//生成包含這個多維數組的PyObject對象,使用PyArray_SimpleNewFromData函數,
	//第一個參數2表示維度,第二個爲維度數組Dims,第三個參數指出數組的類型,第四個參數爲數組
	PyObject* pValue = PyArray_SimpleNewFromData(3, dims, NPY_UINT8, sml_img.data);
	PyTuple_SetItem(pArgs, 0, pValue);	/* pValue的引用計數被偷偷減一,無需手動再減 */
	PyTuple_SetItem(pArgs, 1, Py_BuildValue("i", 3));	/* 圖像放大4倍 */
	/* 調用函數 */
	PyObject* pRetValue = PyObject_CallObject(pFunc, pArgs);

	/* 解析返回結果 */
	PyArrayObject* ret_array;
	PyArray_OutputConverter(pRetValue, &ret_array);
	npy_intp* shape = PyArray_SHAPE(ret_array);
	Mat big_img(shape[0], shape[1], CV_8UC3, PyArray_DATA(ret_array));

	cv::imshow("res", big_img);
	cv::waitKey(0);

	/* 釋放所有 */
	Py_DECREF(pName);
	Py_DECREF(pModule);
	Py_DECREF(pFunc);
	Py_DECREF(pArgs);
	Py_DECREF(pValue);
	Py_DECREF(pRetValue);
	Py_Finalize();
	return 0;
}

9. 將視頻每一幀傳遞給python

注意:
不要再循環內Py_DECREF
python代碼

import torch
import os
from torchvision import transforms as T
from torchvision.transforms.functional import to_pil_image
from threading import Thread
from torch.nn import functional as F
from model.model import MattingDGF
from PIL import Image
import cv2
import numpy as np


def load_model():
    global device, model
    device = torch.device('cuda')
    # Load model
    model = MattingDGF('mobilenetv2')
    model = model.to(device).eval()
    model.load_state_dict(torch.load("E:/green_screen_keying/deep-learning-V3-main/deep-learning-V3-main/TrainedModel"
                                     "-V3/GSK-V3-3.pth", map_location=device))


def test_image_c(img):
    # 讀取單張圖像數據
    transforms = T.Compose([T.Resize((1080, 1920)), T.ToTensor()])

    # with Image.open(images_src) as img:
    #     img = img.convert('RGB')

    img = Image.fromarray(img)  # numpy 轉 PIL image類
    src = transforms(img)
    src = src.to(device, non_blocking=True)
    src = torch.unsqueeze(src, 3).permute(3, 0, 1, 2)  # [B,C,H,W]

    # Conversion loop
    with torch.no_grad():
        pred_pha_hr, pred_fgr_hr, pred_pha_lr, pred_fgr_lr, pred_err_lr = model(src)

    # 轉換回numpy格式
    # 返回com
    tgt_bgr = torch.tensor([1.0, 1.0, 1.0], device=device).view(1, 3, 1, 1)
    com = pred_fgr_hr * pred_pha_hr + tgt_bgr * (1 - pred_pha_hr)
    com = com.cpu().permute(2, 3, 1, 0).squeeze(3).numpy()
    com = cv2.cvtColor(com, cv2.COLOR_BGR2RGB)
    com = cv2.resize(com, (400, 400)) * 255
    # 返回pha
    pha = pred_pha_hr.cpu().permute(2, 3, 1, 0).squeeze(3).numpy()
    pha = cv2.resize(pha, (400, 400)) * 255
    # return com.astype(np.uint8)
    return [com.astype(np.uint8), pha.astype(np.uint8)]


if __name__ == "__main__":
    img_src = "E:/green_screen_keying/test_set/test2.png"
    img = cv2.imread(img_src)
    load_model()
    com, pha = test_image_c(img)  # (400,400,3) numpy
    # pha = show_image_return_numpy(img_src) #(400,400,3) numpy
    # print(type(pha))
    # print(pha.shape)
    # print(pha.dtype)
    cv2.imshow("img-show", pha)
    cv2.waitKey(0)
    cv2.destroyAllWindows()

C++代碼

#include<iostream>
#include<Python.h>
#include <numpy/arrayobject.h>//numpy的頭文件
#include<opencv/cv.hpp>//opencv的頭文件
using namespace cv;
using namespace std;

int main() {
	Py_Initialize();
	if (!Py_IsInitialized()) {
		cout << "python initialize failed!" << endl;
		return 0;
	}
	import_array();//加載numpy相關的庫

	PyRun_SimpleString("import sys");
	PyRun_SimpleString("sys.path.append('./script/code-V3')");

	PyObject* pModule = PyImport_ImportModule("test_image_C++");
	if (pModule == NULL) {
		cout << "module not found!" << endl;
		return 0;
	}

	/*加載模型*/
	PyObject* pFunc_load = PyObject_GetAttrString(pModule, "load_model");
	if (pFunc_load == NULL || PyCallable_Check(pFunc_load) == NULL) {
		cout << "function not found!" << endl;
		return 0;
	}
	PyObject_CallObject(pFunc_load, NULL);

	PyObject* pFunc = PyObject_GetAttrString(pModule, "test_image_c");
	if (pFunc == NULL || PyCallable_Check(pFunc) == NULL) {
		cout << "function not found!" << endl;
		return 0;
	}
	/* 準備輸入參數 */
	//讀入圖片
	//Mat img = imread("E:/green_screen_keying/test_set/test2.png");
	//讀入視頻
	VideoCapture capture;
	capture.open("E:/green_screen_keying/test_video_13/test_videos/chizi.mp4");
	if (!capture.isOpened())
	{
		printf("can not open ...\n");
		return -1;
	}
	Mat img;
	PyObject* pValue=NULL, *pArgs=NULL,*pRet = NULL;
	PyArrayObject* array_com = NULL, * array_pha = NULL;

	while (capture.read(img)){
		cv::cvtColor(img, img, cv::COLOR_BGR2RGB);
		npy_intp dims[] = { img.rows, img.cols, img.channels() };
		//PyArray_SimpleNewFromData可以將void*的數據轉換成np.ndarray
		
		pValue = PyArray_SimpleNewFromData(3, dims, NPY_UINT8, img.data);
		pArgs = PyTuple_New(1);
		PyTuple_SetItem(pArgs, 0, pValue);	/* pValue的引用計數被偷偷減一,無需手動再減 */
		//PyTuple_SetItem(pArgs, 1, Py_BuildValue("i", 3));	

		
		pRet = PyObject_CallObject(pFunc, pArgs);

		// 解析返回結果 
		//PyArrayObject* array_com, * array_pha;
		PyArray_OutputConverter(PyList_GetItem(pRet, 0), &array_com);
		PyArray_OutputConverter(PyList_GetItem(pRet, 1), &array_pha);
		npy_intp* shape = PyArray_SHAPE(array_com);
		Mat com(shape[0], shape[1], CV_8UC3, PyArray_DATA(array_com));
		Mat pha(shape[0], shape[1], CV_8UC1, PyArray_DATA(array_pha));


		cv::imshow("com", com);
		cv::waitKey(10);
		/*
		cv::imshow("pha", pha);
		cv::waitKey(0);
		*/
		com.release();
		pha.release();
		img.release();
	}
	Py_DECREF(pValue);
	
	Py_DECREF(pRet);
	Py_DECREF(pArgs);

	Py_DECREF(pModule);
	Py_DECREF(pFunc_load);
	Py_DECREF(pFunc);
	Py_DECREF(array_com);
	Py_DECREF(array_pha);
	//Py_DECREF(pRetValue);
	
	capture.release();
	Py_Finalize();

	return 0;
}

繼續更新中…

參考鏈接

C++調用python腳本
C++調用numpy和OpenCV
C++調用python時opencv和numpy的轉換和使用

 
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章