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