DoubleLi

qq: 517712484 wx: ldbgliet

  博客园 :: 首页 :: 博问 :: 闪存 :: 新随笔 :: 联系 :: 订阅 订阅 :: 管理 ::

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接口函数

 
posted on 2023-01-18 14:57  DoubleLi  阅读(365)  评论(0编辑  收藏  举报