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解釋器】。
相關官方文檔
相關函數
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的轉換和使用