C++调用Python(混合编程)函数整理总结

文章目录

  • C++调用python概述
  • 相关官方文档
  • 相关函数
  • 1.初始化python解释器环境
  • 2.调用python脚本的静态简单方式
  • 3.动态加载python模块并执行函数
  • 3.1不带参数和返回值的举例说明
  • 3.2带参数和返回值的举例说明
  • 4. c++调用numpy和OpenCV
  • 5.C++调用python显示图片(在python中显示)
  • 6.C++调用python显示图片(在C++中显示)
  • 7.C++调用python深度学习显示结果图片(在C++中显示)
  • 8.将图像mat数据转换成numpy传递给python
  • 9. 将视频每一帧传递给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)
    

    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的转换和使用

    物联沃分享整理
    物联沃-IOTWORD物联网 » C++调用Python(混合编程)函数整理总结

    发表评论