C++调用Python传入参数、图片并接受返回值
最近在做C++调用Pytorch模型进行识别的任务,C++关于Pytorch的教程很少,基本上都是用Python写的,但因为要识别任务是实时的,Python的执行效率不如C++,所以主题代码还是没用Python。
网上利用C++调用Pytorch模型的方法主要是把模型文件转化成C++可以加载和执行的模型文件,利用的是Torch Script,但这个方法我目前还没有看懂…(先放上一个链接,后续再看:https://blog.csdn.net/tlzhatao/article/details/86555269)
现在我使用的方法是在C++文件里调用Python,Python文件主要执行的是调用模型、接受图片进行检测并返回坐标结果的功能,C++需要向Python传入模型所在位置(因为需要根据不同的任务选择不同的模型)以及图片(视频流),并接受返回的坐标
原始的C++代码里有涉及到摄像头的打开,图片的实时获取等功能,所以先写了一个简单的代码来实现C++调用Python的功能,代码如下:
#include <iostream> #include <Python.h> #include <vector> #include "opencv2/opencv.hpp" #include <numpy/arrayobject.h> using namespace std; int transport(PyObject *pDict) { import_array(); cv::Mat img = cv::imread("2.jpg", CV_LOAD_IMAGE_COLOR); cout<<"读取完毕"<<endl; int m, n; n = img.cols*3; m = img.rows; unsigned char *data = (unsigned char*)malloc(sizeof(unsigned char) * m * n); int p = 0; for (int i = 0; i < m; i++) { for (int j = 0; j < n; j++) { data[p]下面是Python代码:= img.at<unsigned char>(i, j); p++; } } npy_intp Dims[2] = { m, n }; //给定维度信息 PyObject*PyArray = PyArray_SimpleNewFromData(2, Dims, NPY_UBYTE, data); PyObject *ArgArray = PyTuple_New(2); PyObject *arg = PyLong_FromLong(30); PyTuple_SetItem(ArgArray, 0, PyArray); PyTuple_SetItem(ArgArray, 1, arg); PyObject*pFuncFive = PyDict_GetItemString(pDict, "load_image"); PyObject* pReturnValue = PyObject_CallObject(pFuncFive, ArgArray); if(PyList_Check(pReturnValue)){ cout<<"finish"<<endl; } int Size0List = PyList_Size(pReturnValue); vector<vector<double> > detections; double t; detections.resize(Size0List); cout<<"Size0List:"<<Size0List<<endl; for(int i=0;i<Size0List;i++){ cout<<"turn:"<<i<<endl; PyObject *ListItem = PyList_GetItem(pReturnValue,i); int Num0Items = PyList_Size(ListItem); int j=0; while(j<Num0Items){ if(j>=Num0Items) { break; } PyObject *Item = PyList_GetItem(ListItem,j); detections[i].push_back(PyFloat_AsDouble(Item)); PyArg_Parse(Item,"d",&t); cout<<"t:::"<<t<<endl; Py_DECREF(Item); j++; } Py_DECREF(ListItem); } cout<<"getget"<<endl; for(int i=0;i<detections.size();i++){ for(int j=0;j<detections[i].size();j++) cout<<detections[i][j]<<endl; } } int main() { Py_Initialize();PyObject* sys = PyImport_ImportModule("sys"); PyRun_SimpleString("import sys"); // 执行 python 中的短语句 PyRun_SimpleString("sys.path.append('python文件所在路径')"); PyObject *pModule(0); pModule = PyImport_ImportModule("test1");//myModel:Python文件名 if(pModule){ cout<<"读取到python文件"<<endl; }PyObject *pDict = PyModule_GetDict(pModule); int count = 0; while(count<3){ count++; PyObject *ArgArray2 = PyTuple_New(1); PyObject *arg2 = PyLong_FromLong(20); PyTuple_SetItem(ArgArray2, 0, arg2); PyObject *pFuncFive2 = PyDict_GetItemString(pDict, "init"); PyObject_CallObject(pFuncFive2, ArgArray2); }transport(pDict); return 0; }
import numpy as np import cv2 bb = [[10,1011],[10,2],[2,56],[3,3.4]] def init(choose): global bb if choose == 20: bb.append([4,64]) def arrayreset(array): a = array[:, 0:len(array[0] - 2):3] b = array[:, 1:len(array[0] - 2):3] c = array[:, 2:len(array[0] - 2):3] a = a[:, :, None] b = b[:, :, None] c = c[:, :, None] m = np.concatenate((a, b, c), axis=2) return m def load_image(image,a): rgbImg = arrayreset(image) # rgbImg=cv2.cvtColor(img,cv2.COLOR_GRAY2BGR) print(rgbImg.shape) cv2.imshow("test",rgbImg) cv2.waitKey(1000) global bb bb.append([2,34]) return bb
下面是功能解读:
首先,Python文件里,init函数我模拟的是一个初始化网络并加载模型的操作,这里的bb是一个全局变量,所以在C++里使用init函数的部分我也放在了主函数的位置。因为检测的时候模型只需要加载一次就可以了,所以C++调用模型的地方也会与传图片进行检测的部分分离开。
而在transport函数里,就是一个传图片的功能,在实际任务中,开启摄像头,获取到每一帧的图片后进行传入即可。参考博客:https://www.geek-share.com/detail/2722074897.html
下面是实现方法:
C++调用Python主要是用的<Python.h>头文件,首先初始化:
Py_Initialize();
下来是加载Python模型:
PyObject* sys = PyImport_ImportModule("sys"); PyRun_SimpleString("import sys"); // 执行 python 中的短语句 PyRun_SimpleString("sys.path.append('python文件所在路径')"); PyObject *pModule(0); pModule = PyImport_ImportModule("test1");//test1:Python文件名
注意的是,如果python文件所在的位置和可执行文件不在一块的话,一定要导入python文件的路径。
判断是否读取到python文件:
if(pModule){ cout<<"读取到python文件"<<endl; }
下面这个操作我也不是很懂,所有的函数原理在百度上都能找到,我只大概了解了一下实现,pDict里面就是python文件里的函数了,如果想要使用某个函数的话,用
PyDict_GetItemString(pDict,函数名)即可
PyTuple_New(n)是传入的参数个数
PyLong_FromLong(n)是传入的参数,这里是C++的long型
PyTuple_SetItem(a,b,c),中间的参数为传入参数的位置
PyObject_CallObject()是函数的实现,这个是有返回值的,但也可以不返回
PyObject *pDict = PyModule_GetDict(pModule); int count = 0; while(count<3){ count++; PyObject *ArgArray2 = PyTuple_New(1); PyObject *arg2 = PyLong_FromLong(20); PyTuple_SetItem(ArgArray2, 0, arg2); PyObject *pFuncFive2 = PyDict_GetItemString(pDict, "init"); PyObject_CallObject(pFuncFive2, ArgArray2); }
图片的传入是类似的,只不过多了个Mat到Numpy的转化,记得加上<numpy/arrayobject.h> 头文件并
import_array()
下面是接受返回值(pReturnValue)的处理:
int Size0List = PyList_Size(pReturnValue); vector<vector<double> > detections; double t; detections.resize(Size0List); cout<<"Size0List:"<<Size0List<<endl; for(int i=0;i<Size0List;i++){ cout<<"turn:"<<i<<endl; PyObject *ListItem = PyList_GetItem(pReturnValue,i); int Num0Items = PyList_Size(ListItem); int j=0; while(j<Num0Items){ if(j>=Num0Items) { break; } PyObject *Item = PyList_GetItem(ListItem,j); detections[i].push_back(PyFloat_AsDouble(Item)); PyArg_Parse(Item,"d",&t); cout<<"t:::"<<t<<endl; Py_DECREF(Item); j++; } Py_DECREF(ListItem); }
具体的就不解释了…
如果变量是int,double之类的,需要使用PyArg_Parse()来转到C++里面去,用完别忘了释放。
至于实际任务的代码,参考了 https://www.jianshu.com/p/c9f5f4ce3e7a?utm_campaign=maleskine 这位博主,感觉写的挺好的,我的代码差不多就是这个形式。
最后最后,写CMakeLists的时候,一定要加上下面的东西:
find_package(PythonLibs REQUIRED) include_directories(${PYTHON_INCLUDE_DIRS} target_link_libraries( 工程名 ${PYTHON_LIBRARIES})
当然实际情况中不可能这么简洁…
- C++ 调用Python文件方法传递字典参数并接收返回值
- python调用dll文件时传入参数是int*,返回值是char*,对传入的char*所指的内容的修改
- Windows下QT中用C++调用Python之三 - 基础参数的传入和传出
- C++调用Python函数并传入传出参数
- php 调用python 脚本 传入参数 , python 接收参数 ,php接收返回值
- C++调用python并取返回值
- c++调用python解析list返回值
- java调用Python脚本文件的同时,并向其中传入参数
- c++调用python返回值
- Python学习笔记(10)-函数-函数定义、调用、参数、返回值、嵌
- Python学习笔记(10)-函数-函数定义、调用、参数、返回值、嵌
- c++ 调用python传输图片
- Linux下python调用C++接口实现图片及文件的AES加解密
- c++ 调用 python 实例 涉及 类 多参数 列表作为参数
- linux 下C++调用python返回值(python.so)
- C# 调用C++动态链接库 之一 传入参数
- python调用c++回调图片
- 【python】c++调用python文件设置参数Py_BuildValue()
- Python中将函数作为另一个函数的参数传入并调用
- python嵌入C++------ boost.python如何在C++中调用含有不定长参数tuple变量和关键字参数dict变量的函数