C++调用Python
- C++调用Python
- 官方文档
- 准备工作
- 环境搭建
- 调用方法
- 包含头文件以及链接到库
- 直接调用简单语句
- 简单函数调用
- Mat数据转PyObject
- C++中解析python脚本返回的元组数据
- 参考文档
C++调用Python
在毕业设计中需要用到一个深度学习网络的已有算法,但是整个框架是用C++写的,所以需要用C++调用Python,整个过程持续了一周,难受~ 但也学到了不少,在这里记录一下!
文中可能会有一些错误,如果又发现的,麻烦留言,我及时更正,非常感谢!
官方文档
- Python/C API Reference Manual
- NumPy C-API
- Embedding Python in Another Application
准备工作
环境搭建
- 本机环境:
- Ubuntu18.04
- Anaconda
- 虚拟环境:
- Python 3.6
- numpy 1.17
调用方法
包含头文件以及链接到库
#include<Python.h>
set(PYTHON_INCLUDE_DIRS "/home/jia/Software/anaconda3/envs/Test/include/python3.6m/")
INCLUDE_DIRECTORIES(${PYTHON_INCLUDE_DIRS})
link_directories(/home/jia/Software/anaconda3/envs/Test/lib/python3.6/config-3.6m-x86_64-linux-gnu)
set(PYTHON_LIBRARIES "/home/jia/Software/anaconda3/envs/Test/lib/libpython3.6m.so")
add_executable(test main.cpp)
target_link_libraries(test ${PYTHON_LIBRARIES
直接调用简单语句
#include "Python.h"
int main()
{
Py_Initialize(); ## 初始化
PyRun_SimpleString("print 'hello'");
Py_Finalize(); ## 释放资源
}
简单函数调用
- C++代码
#include <iostream>
#include <string>
#include <Python.h>
using namespace std;
int main()
{
/*****************************************
* Python初始化及路径加载
******************************************/
cout << "---------------------初始化Python--------------------" << endl;
Py_Initialize(); // 使用Python系统前,必须使用Py_Initialize对其进行初始化
if ( !Py_IsInitialized() ) //检查初始化是否成功
{
return -1;
}
cout << "---------------------添加路径--------------------" << endl;
// PyRun_SimpleString:这个函数就是执行一条简单的python代码
PyRun_SimpleString("import sys");
PyRun_SimpleString("import os");
//建议用os先获得绝对路径。再将路径加载到python中,不然路径设置的不一定对
//我这里是获得了编译好的可执行文件的上一级路径,也就是工程根目录,这里只要能够讲python脚本的路径加载正确即可
PyRun_SimpleString("sys.path.append(os.path.abspath(os.path.join(os.getcwd(), \"..\"))+'/python/')");
//这里我将路径打印出来,可以查看python脚本的路径是否加载进去了,打印出来的路径有很多,仔细查看
PyRun_SimpleString("print(sys.path)");
/*****************************************
* Python脚本获取以及脚本内的函数获取
******************************************/
cout << "---------------------脚本获取--------------------" << endl;
PyObject *pName,*pModule,*pDict,*pFunc,*pArgs;
pName = PyUnicode_FromString("mypythonfile"); //python3中用这个
pModule = PyImport_Import(pName);
if ( !pModule ) {
printf("can't find mypythonfile.py");
getchar();
return -1;
}
pDict = PyModule_GetDict(pModule);
if ( !pDict ) {
return -1;
}
cout << "---------------------函数获取--------------------" << endl;
// 找出函数名为display的函数
pFunc = PyDict_GetItemString(pDict, "display");
if ( !pFunc || !PyCallable_Check(pFunc) ) {
printf("can't find function [display]");
getchar();
return -1;
}
/*****************************************
* 构造参数对象
******************************************/
cout << "---------------------构造参数对象--------------------" << endl;
//将参数传进去。1代表一个参数。
pArgs = PyTuple_New(1);
// PyObject* Py_BuildValue(char *format, ...)
// 把C++的变量转换成一个Python对象。当需要从
// C++传递变量到Python时,就会使用这个函数。此函数
// 有点类似C的printf,但格式不同。常用的格式有
// s 表示字符串,
// i 表示整型变量,
// f 表示浮点数,
// O 表示一个Python对象。
//这里我要传的是字符串所以用s,注意字符串需要双引号!
PyTuple_SetItem(pArgs, 0, Py_BuildValue("s"," python in C++"));
/*
这里展示多个参数:
pArgs = PyTuple_New(3);
PyTuple_SetItem(pArgs, 0, Py_BuildValue("s"," 第一个参数"));
PyTuple_SetItem(pArgs, 1, Py_BuildValue("s"," 第二个参数"));
PyTuple_SetItem(pArgs, 2, Py_BuildValue("s"," 第三个参数"));
如果已经有PyObject*的数据对象,那么可以:
假设有三个图像PyObject*数据对象
PyObject* img1,img2,img3;
...
pArgs = PyTuple_New(3);
PyTuple_SetItem(pArgs, 0, img1);
PyTuple_SetItem(pArgs, 1, img2);
PyTuple_SetItem(pArgs, 2, img3);
*/
/*****************************************
* 调用python脚本中的函数
******************************************/
// 调用Python函数
PyObject_CallObject(pFunc, pArgs);
/*****************************************
* 脚本执行完之后,需要关闭python
******************************************/
// 关闭Python
Py_Finalize();
return 0;
}
- Python代码
#-* -coding: UTF-8 -* -
def display(name):
print ("hi",name)
class test:
def say(self):
print ("hello")
- CMakeLisits.txt
cmake_minimum_required(VERSION 2.8)
project(test)
set(CMAKE_CXX_STANDARD 14)
set(PYTHON_INCLUDE_DIRS "/home/jia/Software/anaconda3/envs/Test/include/python3.6m/")
INCLUDE_DIRECTORIES(${PYTHON_INCLUDE_DIRS})
link_directories(/home/jia/Software/anaconda3/envs/Test/lib/python3.6/config-3.6m-x86_64-linux-gnu)
set(PYTHON_LIBRARIES "/home/jia/Software/anaconda3/envs/Test/lib/libpython3.6m.so")
add_executable(test main.cpp)
target_link_libraries(test ${PYTHON_LIBRARIES})
- 编译运行
mkdir build
cd build
./test
- 错误解决(1)
$ ./test
Could not find platform independent libraries <prefix>
Could not find platform dependent libraries <exec_prefix>
Consider setting $PYTHONHOME to <prefix>[:<exec_prefix>]
Fatal Python error: Py_Initialize: Unable to get the locale encoding
ModuleNotFoundError: No module named 'encodings'
Current thread 0x00007f7618918740 (most recent call first):
已放弃 (核心已转储)
解决方法:
因为用的是anaconda环境中的python,所以在程序执行的之前,需要先激活ananconda的相应环境,这样才能够成功运行。
conda activate Test
./test
Mat数据转PyObject
我没找到有什么快速的办法实现,只能用遍历的笨办法。
- opencv Mat转PyObject
Mat img=imread("1.png");
int row = img.rows;
int col = img.cols;
int channel = img.channels();
auto *imgData = new uchar[row*col*channel];
int irow = row,icol = col*channel;
//判断图像的Mat数据是不是一行保存的
if (!img.isContinuous())
{
icol *= irow;
irow = 1;
}
int id = 0;
for(int i = 0; i<irow; i++)
{
for(int j = 0; j<icol; j++)
{
imgData[id] = img.at<uchar>(i,j);
id++;
}
}
npy_intp Dims[3] = { row, col, channel}; //注意这个维度数据!
//这里相当于reshape了,构建了numpy可接受的PyObject对象
PyObject *PyImg = PyArray_SimpleNewFromData(3, Dims, NPY_UBYTE, imgData);
C++中解析python脚本返回的元组数据
C++中解析python脚本返回的元组数据,转换为了vector
这里我的项目中需要vector数据,其实解析出来之后你想转成什么数据都可以
-
python代码:
def test: array1 = np.random.rand(4,3) array2 = np.random.rand(3,2) array3 = np.random.rand(7,8) return array1,array2,array3
-
C++代码:
/* * 其中的PyArray是numpy的对象,如果需要查看函数,需要在numpy C API Reference中查看, * 其他在Python C API Reference 中 */ // 调用Python函数 PyObject* Py_result = PyObject_CallObject(pFunc, pArgs); //判断返回是否为空 if(Py_result == NULL) { Py_Finalize(); return false; } PyArrayObject *Py_array1,*Py_array2,*Py_array3; vector<vector<double>> array1; vector<vector<double>> array2; vector<vector<double>> array3; vector<float> thisData; //查看是否是元组数据 if(PyTuple_Check(Py_result)) { //将元组数据解开 //这里的参数先留一个坑,以后填,如果着急,可以自行对阅读Python C Reference,文章开头有链接 //如果只返回一个矩阵,则不需要这一句 PyArg_UnpackTuple(Py_result, "ref", 3, 3, &Py_array1, &Py_array2, &Py_array3); //获取矩阵维度 npy_intp *Py_array1_shape = PyArray_DIMS(Py_pts1); npy_intp *Py_array2_shape = PyArray_DIMS(Py_pts2); npy_intp *Py_array3_shape = PyArray_DIMS(Py_matches); int array1row = Py_array1_shape[0]; int array1col = Py_array1_shape[1]; int array2row = Py_array2_shape[0]; int array2col = Py_array2_shape[1]; int array3row = Py_array3_shape[0]; int array3col = Py_array3_shape[1]; vector<double> temp; double thisdata; //array1 for(npy_intp row = 0; row < (npy_intp)array1row; row++) { temp.clear(); for(npy_intp col = 0; col < (npy_intp)array1col; col++) { thisdata = *(double*)PyArray_GETPTR2(Py_array1,row,col); temp.push_back(thisData); } array1.push_back(temp); } //array2 for(npy_intp row = 0; row < (npy_intp)array2row; row++) { temp.clear(); for(npy_intp col = 0; col < (npy_intp)array2col; col++) { thisdata = *(double*)PyArray_GETPTR2(Py_array2,row,col); temp.push_back(thisData); } array2.push_back(temp); } //array3 for(npy_intp row = 0; row < (npy_intp)array3row; row++) { temp.clear(); for(npy_intp col = 0; col < (npy_intp)array3col; col++) { thisdata = *(double*)PyArray_GETPTR2(Py_array3,row,col); temp.push_back(thisData); } array3.push_back(temp); } //至此,python返回的array数据解析完成 }
参考文档
非常感谢各位博主的博文,受益匪浅!
- 浅析 C++ 调用 Python 模块
- C++调用Python
- c++调用python numpy编程
- c++向python传输图片 高效方法 mat转numpy
- ubuntu下C++如何调用python程序,gdb调试C++代码
- Python3执行PyImport_Import()一直返回NULL—路径设置问题
- C++调用Python浅析
- C++ 调用 Python3.6中的各种坑
更多推荐
C++调用python,并且在python和C++之间传输数据(numpy和mat数据)
发布评论