C++工程调用python脚本(VS2017)

最近由于项目需求需要爱x265中调用python脚本,记录一下大致的使用过程

一、编译

编译x265/VTM/HM时候,需要在CMakeLists.txt中添加如下语句

# python3
include_directories("D:/Anaconda/include/")
link_libraries("D:/Anaconda/libs/python36.lib")

因为我下载的是Anaconda,所以这里需要包含的是anaconda下的路径。添加完成后,正常编译就行

二、C++调用Python的脚本

编译完成后,用VS2017打开工程

1、添加python头文件

#include<Python.h>

2、在使用python库函数之前应该初始化python接口

Py_Initialize();

与之对应的是,调用完python脚本之后,记得释放python接口:

Py_Finalize();

 注意:初始化和释放这两个函数整个程序中只能调用一次,所以需要放在整个程序最开始和最后的地方,如果出现多次调用的情况,则可能会发生访问冲突或者找不到.dll等的错误。

 3、切换python脚本下的工作路径

	PyRun_SimpleString("import sys");
	PyRun_SimpleString("import os");
	PyRun_SimpleString("os.chdir('./Net')");
	PyRun_SimpleString("sys.path.append('./')");

这里使用os.chdir切换到python脚本所在的路径,然后使用sys.path.append('./')将当前目录添加到工作路径下

调用完成之后,使用os.chdir切换回到之前的工作目录下

4、加载模块和函数

	m_pMod = PyImport_ImportModule("testnet");
	if (!m_pMod)
	{
		PyErr_Print();
	}

    m_pFunc = PyObject_GetAttrString(m_pMod, "run");
	if (!m_pFunc)
	{
		PyErr_Print();
	}

这里我调用的python的脚本名称是testnet.py文件,所以使用PyImport_ImportModule函数将该python文件调用进来,然后使用PyObject_GetAttrString调用相应的函数,这里的run是定义在testnet.py文件中的python函数名

注意:我们每次引用完脚本和函数之后,需要使用PyErr_Print()函数检查是否出错;

5、设置参数,并将参数传递给python函数,调用python函数

	PyObject *pReturn = NULL;
	PyObject *pInputRec = PyList_New(TuWidth * TuHeight);
	PyObject *pParam = PyTuple_New(3);

	for (int row = 0; row < TuHeight; row++)
	{
		for (int col = 0; col < TuWidth; col++)
		{
			PyList_SetItem(pInputRec, row * TuWidth + col, Py_BuildValue("i", NNRecon[row * TuWidth + col]));
		}
	}
	PyTuple_SetItem(pParam, 0, pInputRec);
	PyTuple_SetItem(pParam, 1, Py_BuildValue("i", TuWidth));
	PyTuple_SetItem(pParam, 2, Py_BuildValue("i", TuHeight));
	pReturn = PyEval_CallObject(m_pFunc, pParam);//调用python中函数
	PyList_Check(pReturn);

我们可以使用PyList_New函数定义一个python列表,PyTuple_New函数定义一个python元组,Py_BuildValue函数将C++类型的数转换为python对象,"i"表示转换后的格式(整型),除了"i"还有以下格式:

  • "s" (string) [char *] :将C字符串转换成Python对象,如果C字符串为空,返回NONE。
  • "s#" (string) [char *, int] :将C字符串和它的长度转换成Python对象,如果C字符串为空指针,长度忽略,返回NONE。
  • "z" (string or None) [char *] :作用同"s"。
  • "z#" (string or None) [char *, int] :作用同"s#"。
  •  "i" (integer) [int] :将一个C类型的int转换成Python int对象。
  • "b" (integer) [char] :作用同"i"。
  • "h" (integer) [short int] :作用同"i"。
  • "i" (integer) [long int] :将C类型的long转换成Pyhon中的int对象。
  • "c" (string of length 1) [char] :将C类型的char转换成长度为1的Python字符串对象。
  • "d" (float) [double] :将C类型的double转换成python中的浮点型对象。
  • "f" (float) [float] :作用同"d"。
  • "O&" (object) [converter, anything] :将任何数据类型通过转换函数转换成Python对象,这些数据作为转换函数的参数

使用PyList_SetItem函数可以设置列表中的元素值,第一个参数是所要设置的列表,第二个参数指的是列表的索引值,第三个参数指的是所要设置的值。同理PyTuple_SetItem函数可以设置元组中的值

设置完参数之后,使用PyEval_CallObject函数可以调用python的函数并传递参数,其第一个参数指的是所要调用的函数,第二个参数指的是函数的参数。

因为我定义的run函数返回的是一个python列表,所以获得python返回值之后,调用PyList_Check函数判断是否是一个Python List(列表)

6、解析返回值,将python对象转换为C++类型

	for (int row = 0; row < TuHeight; row++)
	{
		for (int col = 0; col < TuWidth; col++)
		{
			PyArg_Parse(PyList_GetItem(pReturn, row * TuWidth + col), "i", &NNRecon[row * TuWidth + col]);
		} 
	}

PyList_GetItem函数可以获得python列表中的对应索引处的值,第一个参数表示python列表,第二个参数表示索引。

使用PyArg_Parse函数可以将python对象转换为C++类型的值,其第一个参数表示python对象,第二个参数表示转换之后的C++类型格式,第三个参数转换后的值赋给的变量(必须加上&符号)

7、结束

Py_DECREF(pInputRec);
Py_DECREF(pReturn);
Py_DECREF(pParam);
Py_Finalize();

整个python调用结束之后,使用 Py_DECREF(PyObject*)函数释放资源,方便python对象垃圾回收。

猜你喜欢

转载自blog.csdn.net/BigDream123/article/details/112549128
今日推荐