首页 > 编程语言 >VS2019 C++ 调用python函数/类对象的方法

VS2019 C++ 调用python函数/类对象的方法

时间:2023-08-11 14:12:55浏览次数:52  
标签:调用 VS2019 python Py C++ PyObject ArgArray

1.环境配置

  • VS工程配置要和python一致,安装的python如果是64位的,工程配置也要选成64位的

  • 在工程配置中添加包含目录和库目录,添加python环境目录里的include和libs文件夹路径。想要运行的keras-yolo3是在Anaconda中配置的环境,所以相应的文件夹路径可以在Anaconda的环境文件中找到

  • 打开项目属性页,【VC++目录】中的包含目录和库目录中设置上图中的python头文件和库文件目录


  • 添加依赖项,【链接器】→【输入】→【附加依赖项】,添加libs目录里面的python35_d.lib,一开始是没有这个的,只有pyth35.lib,这里复制一份重命名为python35_d.lib,因为VS默认会检测后者,不改的话可能会报错。

  • 配置结束

二、C++中运行python代码

#include<python.h>
#include<iostream>

int main()
{
      Py_Initialize();//使用python之前,要调用Py_Initialize();这个函数进行初始化
      PyRun_SimpleString("print('hello world!')");
      Py_Finalize();
}

生成解决方案报错如下:

原因:slots是QT的关键字,所以才出现该问题
解决方法:可以暂时将slots改名为slots1,重新生成解决方案,错误消除。

报错:缺少python35.dll,程序无法继续运行
解决方法:在Anaconda的安装包package文件夹下,找到复制粘贴到C:\Windows\System32目录下。

报错:未经处理的异常


Fatal Python error: Py_Initialize: unable to load the file system codec
报错原因:电脑上有其他版本的python,以前装的
解决方法:删除以前装的,或者是使用Py_SetPythonHome()指定python.exe路径,如下图:

运行程序发现跑不出print的结果,需要设置
1.在配置属性中,找到生成事件,在找到后期生成事件;
2.在命令行的右边空白处添加“editbin /SUBSYSTEM:CONSOLE $(OUTDIR)$(ProjectName).exe”,点击确定

运行结果:

三、c++调用python函数并输出返回值

	Py_SetPythonHome(L"D:\\Users\\simon\\Anaconda3\\envs\\keras-yolo3");//指定python.exe位置
	Py_Initialize(); //使用python前要调用此函数,进行初始化
	
	if (!Py_IsInitialized()) //如果没有初始化成功
	{
		cout << "fail to initial!" << endl;
		Py_Finalize();
	}

	PyRun_SimpleString("import sys");
	PyRun_SimpleString("sys.path.append('./')");//设置.py文件所在位置

	//声明变量
	PyObject* pModule = NULL; //.py文件 
	PyObject* pFunc = NULL;  //py文件中的函数
	PyObject* pParams = NULL; //函数参数
	PyObject* pResult = NULL;  //函数返回的结果
	
	pModule = PyImport_ImportModule("testPython"); //调用上述路径下的testPython.py文件
	if (pModule == NULL)
	{
		cout << "don't find the python file!" << endl;
	}
	 
	pFunc = PyObject_GetAttrString(pModule, "add_number");  //从指定.py文件中调用函数add_number
	pParams = Py_BuildValue("(ii)",1,1);//设置函数参数,i表示int整型,两个i表示有两个参数,s表示字符串等
	pResult = PyObject_CallObject(pFunc, pParams);//调用函数,返回计算结果

	int res;
	PyArg_Parse(pResult, "i", &res);//将返回结果转换成C++类型
	cout << "res:" << res << endl;

	Py_Finalize();

testPython.py中的add_number函数定义如下:

def add_number(a,b):
    return a+b

运行结果:

python函数中的不能含有print语句,否则PyImport_ImportModule(“testPython”)返回值为空。
替代方法:

import sys

	sys.stderr.write('test .....\n')

四、c++调用python类(调用yolo3对象,传图像参数)

因为需要转换图像格式,从opencv的Mat格式转为python的PIL格式,需要 用到numpy的C++接口,所以需要先配置一下
项目属性页:【VC++目录】→【包含目录】,配置如下目录,在头文件中 #include<numpy/arrayobject.h>

在程序中写入import_array()时,报错

原因:import_array其实是一个宏定义,宏的最后有返回值,但和外层函数的返回值不同,所以报错。右击→速览定义可以看到

解决方法:
1.去掉宏定义中的返回值(不合适,破坏封装好的numpy函数)
2.直接调用_import_array()函数,就没有返回值了

生成解决方案报错:

无法解析的外部符号 __imp___Py_RefTotal

无法解析的外部符号 __imp___Py_NegativeRefcount,该符号在函数 __import_array 中被引用

原因:产生这个错误,主要是因为python/C API配置文件中的Py_DEBUG/Py_TRACE_REFS引起,

解决方案:
修改python/C API的配置文件,修改目录:\include下的pyconfig.h和object.h文件:
1.找到目录,因为是用的Anaconda的虚拟配置环境,所以要修改的文件在Anaconda路径下
2.用everything搜索pyconfig,根据要调用的python文件所用的python版本,我用的3.5所以找到对应的路径



3.修改两个头文件object.h和pyconfig.h
在object中,修改54行的

/* Py_DEBUG implies Py_TRACE_REFS. */
#if defined(Py_DEBUG) && !defined(Py_TRACE_REFS)
#define Py_TRACE_REFS
#endif

/* Py_DEBUG implies Py_TRACE_REFS. */
#if defined(Py_DEBUG) && !defined(Py_TRACE_REFS)
//#define Py_TRACE_REFS
#endif

在pyconfig中,修改358行的

#ifdef _DEBUG
#	define Py_DEBUG
#endif

#ifdef _DEBUG
//#	define Py_DEBUG
#endif

4.保存两个h文件后,重新生成解决方案,报错消失。

PyObject_CallMethod()返回值为NULL

PyObject* pList = PyObject_CallMethod(pInstanceYOLO, "detect_image","O", ArgArray);
def detect_image(self, image):

原因:别人博客中说类方法的self参数不需要参数,但这边就是因为缺少了self参数才会出现问题

改为下式,pInstanceYOLO实例自身对应的就是self参数,format改为(OO)表示有两个输入对象参数

PyObject* pList = PyObject_CallMethod(pInstanceYOLO, "detect_image","(OO)", pInstanceYOLO, ArgArray);

调用类对象方法的代码如下

    Py_SetPythonHome(L"D:\\Users\\simon\\Anaconda3\\envs\\keras-yolo3");//指定python.exe位置
	Py_Initialize(); //使用python前要调用此函数,进行初始化,开启虚拟机
	
	if (!Py_IsInitialized()) //如果没有初始化成功
	{
		cout << "fail to initial!" << endl;
		Py_Finalize();
	}
	_import_array(); //PyArray_SimpleNewFromData之前必须先引入此函数

	PyRun_SimpleString("import sys");
	PyRun_SimpleString("sys.path.append('./keras-yolo3/')");//设置.py文件所在位置

	//声明变量
	PyObject* pModule = NULL; //.py文件 
	PyObject* pFunc = NULL;  //py文件中的函数
	PyObject* pClass = NULL; //类 
	PyObject* pInstance = NULL; //实例
	
	pModule = PyImport_ImportModule("yolo"); //调用上述路径下的yolo.py文件
	if (pModule == NULL)
	{
		cout << "Can't find the python file!" << endl;
	}

	//模块的字典列表
	PyObject* pDict = PyModule_GetDict(pModule); //获得Python模块中的函数列
	if (pDict == NULL)
	{
		cout << "Can't find the dictionary!" << endl;
		return;
	}

	//获取YOLO类
	PyObject* pClassYOLO = PyDict_GetItemString(pDict, "YOLO");//获取函数字典中的YOLO类
	if(pClassYOLO == NULL)
	{
		cout << "Can't find YOLO class!" << endl;
		return;
	}

	//构造YOLO类的实例
	PyObject* pInstanceYOLO = PyInstanceMethod_New(pClassYOLO);
	if (pInstanceYOLO == NULL)
	{
		cout << "Can't find YOLO instance!" << endl;
		return;
	}

	//读取图像并转换格式,opencv读取图像的存储格式是BGR格式,而PIL中为RGB,
	//本项目是用opencv读取图像,yolo3算法中用的是PIL格式,所以图像在传入yolo类之前需要先转换格式
	Mat src = imread("./keras-yolo3/small10.jpg");
	cvtColor(src, src, COLOR_BGR2RGB);//转成PIL格式
	int m, n;
	m = src.rows; //行数
	n = src.cols * 3; //列数*通道数
	unsigned char* data = (unsigned char*)malloc(sizeof(unsigned char) * m * n);
	int num = 0;
	for (int i = 0; i < m; i++)
	{
		for (int j = 0; j < n; j++)
		{
			data[num] = src.at<unsigned char>(i, j);
			num++;
		}
	}
	npy_intp Dims[2] = { m,n };  //图像的维度信息
	PyObject* PyArray = PyArray_SimpleNewFromData(2, Dims, NPY_UBYTE, data); //建立函数的形参
	PyObject* ArgArray = PyTuple_New(1);//新建长度为1的元组
	PyTuple_SetItem(ArgArray, 0, PyArray);//设置元组ArgArray[0]为PyArray图像

	//Mat src = imread("./keras-yolo3/small10.jpg");
	//cvtColor(src, src, COLOR_BGR2RGB);//转成PIL格式
	//int m, n,channels;
	//m = src.rows; //行数
	//n = src.cols ; //列数
	//channels = src.channels();//通道数
	//unsigned char* data = (unsigned char*)malloc(sizeof(unsigned char) * m * n*3);
	//int num = 0;
	//for (int i = 0; i < m; i++)
	//{
	//	for (int j = 0; j < n*3; j++)
	//	{
	//		data[num] = src.at<unsigned char>(i, j);
	//	}
	//}
	//npy_intp Dims[3] = { m, n, channels };  
	//PyObject* PyArray = PyArray_SimpleNewFromData(3, Dims, NPY_UBYTE, data);
	//PyObject* ArgArray = PyTuple_New(1);//新建长度为1的元组
	//PyTuple_SetItem(ArgArray, 0, PyArray);//设置元组ArgArray[0]为PyArray图像

	
	//调用YOLO实例对象的函数
	PyObject* pList = PyObject_CallMethod(pInstanceYOLO, "detect_image","(OO)", pInstanceYOLO, ArgArray);//pInstanceYOLO对应self,ArgArray对应image
	if(pList == NULL)
	{
		cout << "Failed to use fucntion!" << endl;
		return;
	}

	Py_DECREF(ArgArray);
	Py_DECREF(PyArray);
	Py_DECREF(pInstanceYOLO);
	Py_DECREF(pDict);
	Py_Finalize();

是想在C++项目里调用python类封装的目标检测对象YOLO的,发现有很多问题,暂时解决不了…应该是因为python环境的问题,放弃了… 尝试keras模型的C++调用

比较有价值的参考

C++与Python的混合编程-C++调用Python
如何在C++中使用python类
c++调用python的代码、函数、类
C++中如何将图像作为参数传给Python函数
python的C接口中对参数类型的描述文件,如:i,O,s…
Python的C接口函数

https://blog.csdn.net/omg_orange/article/details/100106926

 

标签:调用,VS2019,python,Py,C++,PyObject,ArgArray
From: https://www.cnblogs.com/lidabo/p/17622822.html

相关文章

  • linux python版本切换alternatives
    查看发现linux的python3命令指向alternativesalternatives是Linux系列操作系统的一个内置命令,即使最小化安装也有该命令,它的主要作用就是版本控制切换,比如,你的系统内有多个Python版本,Python3.8,Python2.7.5,Python3.6,。[root@santiagod-andibleopenstack-ansible]#find/usr/b......
  • Python打印类的属性
    一、使用__dict__打印类的属性classPerson:def__init__(self,name,age):self.name=nameself.age=ageperson=Person("Tom",18)print(person.__dict__)使用__dict__方法可以直接打印出类的属性及其对应的值。上述代码中,我们首先定义了一个Pe......
  • [数据分析与可视化] Python绘制数据地图5-MovingPandas绘图实例
    MovingPandas是一个基于Python和GeoPandas的开源地理时空数据处理库,用于处理移动物体的轨迹数据。关于MovingPandas的使用见文章:MovingPandas入门指北,本文主要介绍三个MovingPandas的绘图实例。MovingPandas官方仓库地址为:movingpandas。MovingPandas官方示例代码仓库地址为:movin......
  • 软件开发入门教程网 Search之C++ 动态内存
       C++基本的输入输出   ......
  • 软件开发入门教程网 Search之C++ 环境设置
       C++基本的输入输出   ......
  • C++ Boost库简介
    1、boost是一个功能强大、构造精良、跨平台、代码开源、完全免费的c++程序库。1)功能强大:共包含160余个库/组件,涵盖字符串与文本处理、容器、迭代器、算法、图像处理、模板元编程、并发编程等多个领域。2)构造精良: 由c++标准委员会成员发起倡议并建立boost社区,C+......
  • C++ Boost库介绍
    Boost库是C++的一个开源类库,包含了大量实用工具和组件,可以大大简化C++编程过程中的繁琐操作。以下是Boost库常见的运用场景:1.多线程编程:Boost.Thread模块提供了丰富的线程相关功能,如锁、条件变量、线程池等,使得多线程编程更加容易。2.正则表达式处理:Boost.Regex模块提供了对正......
  • C++ 构造函数初始化:提高代码可读性和执行效率
    在C++中,构造函数是用来初始化对象数据成员的。一个对象在创建的时候,构造函数会被自动调用,以便为该对象的数据成员赋初值。传统的初始化方式是在构造函数内部对数据成员逐一进行初始化,这种方式虽然可行,但是代码复杂度高且效率低下。本文将介绍如何使用构造函数初始化列表来提高......
  • python在抛出错误raise时,如何将相关信息一同携带出来(抛出自定义对象)?
    示例代码示例classMyClass(Exception):def__init__(self,info):self.info=infodefmy_method(self):print('MyClass.my_methodprintself.info:',self.info)try:raiseMyClass('错误信息')exceptMyClassase:......
  • C/C++住院病人管理系统[2023-08-11]
    C/C++住院病人管理系统[2023-08-11]22、住院病人管理系统(难度等级8)使用C或C++,选择一种计算机编程软件和数据库管理系统来实现一个住院病人管理系统。系统需要实现的功能如下:(1)添加、删除和修改病人信息:向系统中添加、删除和修改仓库信息,信息包括(住院号、姓名、年龄、住院时间、......