Windows下C++调用Python版的Pytorch模型

 本文旨在记录本人在Windows下,用VS2015实现C++调用Python版的Pytorch模型过程中,踩过的坑,方便你我他!

 个人环境:Win10+CUDA10+VS2015+PyTorch1.1+Python36
 本文实例:CenterNet
 论文翻译:https://blog.csdn.net/u011681952/article/details/90901379

文章目录

  • 1 先让CenterNet在Windows上跑起来
    • 1.1 Pytorch环境搭建
    • 1.2 CenterNet环境搭建
    • 1.3 demo
  • 2 C++调用Python版Pytorch模型
    • 2.1 VS2015调用Python配置
    • 2.2 VS2015调用CenterNet
    • 2.3 调试技巧
    • 2.4 传参Mat
    • 2.5 CenterNet应用

1 先让CenterNet在Windows上跑起来

1.1 Pytorch环境搭建

关于环境搭建,本文不会详细描述,网上博客一大堆,只简单叙述下:
 1)CUDA安装:本文默认有GPU
 2)Anaconda安装:
  A)Windows下,常用Pytorch或Tensorflow等,建议使用Anaconda来管理包
  B)直接安装python,用pip来管理Pytorch,import torch 容易出现下面问题:

		 from torch._C import *
		 ImportError: DLL load failed: 找不到指定的模块

  我尝试过很多方法,没能解决,放弃了

 3)一些基础库安装:如python、opencv、numpy等等
 4)Pycharm安装:windows下python代码调试工具,挺好用
 4)Pytorch安装
 5)根据CenterNet的demo提示,缺啥库安装啥库

1.2 CenterNet环境搭建

 1)依赖包安装
 打开Anaconda prompt - -> cd到进入CenterNet目录

     pip install -r requirements.txt

 2)DCNv2编译
 官网给出的CenterNet,自带的DCNv2是linux下torch0.4.1版本的,在windows下编译不了,还有torch版本冲突,会报出
 cffi包的问题:

     问题1:
	 ImportError: torch.utils.ffi is deprecated. Please use cpp extensions instead.
	 问题2:
	 from ._ext import dcn_v2 as _backend
	 ModuleNotFoundError: No module named 'models.networks.DCNv2._ext'

解决链接:https://github.com/xingyizhou/CenterNet/issues/7

 根据解决链接,直接替换DCNv2,并修改dcn_v2_cuda.cu

       //extern THCState *state;                           
     THCState *state = at::globalContext().lazyInitCUDA();   // Modified

编译
编译方案A:进入DCNv2,直接运行make.sh

  • make.sh中:

     python setup.py build develop  
    
  • 这样编译的linux下so库或windows下pyd库文件,在当前文件夹DCNv2中

  • pip依赖包内只生成链接文件DCNv2.egg-link,该文件保存的时DCNv2的绝对路径
    在这里插入图片描述

  • 当然同时pip依赖包下easy-install.pth文件也会指向DCNv2的绝对路径
    在这里插入图片描述

  • 当然在DCNv2文件夹不变的情况下,运行centernet是正常的

  • 若需要打包移植或so文件地址变动就会出问题,可以参见方案B

编译方案B:若为了方便移植,修改make.sh运行:

	python3 setup.py build
	python3 setup.py install

	#可删除本地无用文件
	rm -rf build
	rm -rf dist
	rm -rf DCNv2.egg-info

Windows下C++调用Python版的Pytorch模型_第1张图片

  • 这样编译的so或pyd文件,将会被放到pip依赖包内,即将DCNv2打包为python包。
    Windows下C++调用Python版的Pytorch模型_第2张图片
  • 同时pip依赖包下easy-install.pth文件也会指向DCNv2包
    在这里插入图片描述

 它是将其安装为依赖包了,编译好后,在pycharm里会看到它


Windows下C++调用Python版的Pytorch模型_第3张图片

 3)external编译
 进入external目录

  	python setup.py build_ext --inplace

 编译结果


Windows下C++调用Python版的Pytorch模型_第4张图片

 4)cl.exe问题

 上面2,3编译中,若出现:

      cl.exe 找不到的问题

解决
 A)确定安装VS2015时选择了C++,若没有,再次安装VS2015,选择modify模式,勾选上C++安装即可
 B)将cl.exe所在目录(自己的vs2015安装路径下找)加入到系统path目录


Windows下C++调用Python版的Pytorch模型_第5张图片

 到此应该解决所有问题,也可能有些小问题,我给忘了(本文是事后写的)

1.3 demo

 这里就是下载模型、设置路径什么的,不多说;
 用我自己训练的COCO模型(ubuntu下训练的),展示下,只训练了55 epoch(两个1080ti都跑了5天多,时间关系没继续训练)


2 C++调用Python版Pytorch模型

 这部分才是本文要吐槽的重点,知识点其实不难,只是本人之前没有用过C++和Python的混编,在完成这个任务时遇到了一些坑,差点把我整吐了

2.1 VS2015调用Python配置

 1)在vs2015中新建一个win32控制台应用程序(x64,Debug模式,方便调试)
2)配置
  A)头文件,库目录,配置


Windows下C++调用Python版的Pytorch模型_第6张图片

  因为我程序中要用到numpy,所以这里也加了numpy头文件目录(当然还有opencv配置,根据自己情况而定)

B)库


Windows下C++调用Python版的Pytorch模型_第7张图片

  安装python时,没有生成python36_d.dll,网友称可以复制python36.dll改为python36_d.dll,我尝试了没有问题(当然直接安装python是可以生成python36_d.dll的)

复制python36.dll到工程exe同级目录

C)环境变量:PYTHONHOME
  将python.exe所在目录设为PYTHONHOME,重启电脑;这步还是很重要的


Windows下C++调用Python版的Pytorch模型_第8张图片

2.2 VS2015调用CenterNet

1)头文件

#include 
#include 

#include "Python.h"
#define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION
#include "numpy/ndarrayobject.h"

#include "opencv2/opencv.hpp"

using namespace cv;
using namespace std;

2)main函数
 main函数中,实现了C++调用pytorch模型需要的步骤,包括初始化,python模块加载,python类加载,类方法调用,函数调用,参数设置
 这里相当于实现的是centernet的demo.py中的__main__函数

int main(int argc, wchar_t **argv)
{
    PyObject* pOpt = NULL;
    PyObject* pDict = NULL;
	PyObject* pClass = NULL;
	PyObject* pInit = NULL;
	PyObject* pInstance = NULL;

	PyObject* pName = NULL;
	PyObject* pMod = NULL;
	PyObject* pFunc = NULL;
	PyObject* pParm = NULL;
	PyObject* pRetVal = NULL;

    //初始化python解释器
	Py_Initialize();
	if (!Py_IsInitialized()) {
		return -1;
	}
	PySys_SetArgv(argc, argv);
	
	//加载运行目录
	PyRun_SimpleString("import sys");
	PyRun_SimpleString("sys.path.append('E:/MyProjects/PyCharm/CenterNet/src')");
	PyRun_SimpleString("sys.path.append('E:/MyProjects/PyCharm/CenterNet/src/lib')");

	//opt 初始化化
	
	// Load the module object
	//加载opts.py
	pOpt = PyImport_ImportModule("opts");
	if (!pOpt)
	{
		return -1;
	}

    //因为是opts class,则用类加载方法
	// pDict is a borrowed reference
	pDict = PyModule_GetDict(pOpt);

	// Build the name of a callable class
	const char* initClassName = "opts";  
	pClass = PyDict_GetItemString(pDict, initClassName);
	
	// Create an instance of the class
	if (PyCallable_Check(pClass))
	{
		pInstance = PyObject_CallObject(pClass, NULL);
	}

	// Call a method of the class with no parameters
	const char* initFuncName = "init";
	pInit = PyObject_CallMethod(pInstance, initFuncName, NULL);

    //加载demo.py
	//demo
	pName = PyUnicode_FromString("demo");
	pMod = PyImport_Import(pName);
	if (!pMod)
	{
		return -1;
	}
	const char* demoFuncName = "demo";  //这是此py文件模块中被调用的函数名字
	pFunc = PyObject_GetAttrString(pMod, demoFuncName);
	if (!pFunc)
	{
		return -2;
	}
	
	//参数设置
	pParm = PyTuple_New(1);
	PyTuple_SetItem(pParm, 0, pInit);

	pRetVal = PyEval_CallObject(pFunc, pParm);
	
	return 0;
}

3)效果展示


4)小补充:PySys_SetArgv(argc, argv)
  在mian函数中,有argc, argv这两个参数,像上面用没有问题;
  但在实际应用中,我们可能需要将init重新封装为C++类函数,我们可能不需要传参,这时也没有argc, argv这两个参数,就会报错,可以这样解决:

wchar_t **argv_tmp;
wchar_t *tmp = (wchar_t *)"xxx";
argv_tmp = &tmp;
PySys_SetArgv(0, argv_tmp);

2.3 调试技巧

  在调试过程中,调用python时,出错了,一般是不会抛出具体错误提示的,只会看到这样的提示:


Windows下C++调用Python版的Pytorch模型_第9张图片

  我的办法是在出错的那句代码后调用下一PyRun_SimpleString,如:

	PyRun_SimpleString("import sys");  

  这样,在调试时,dos窗口会抛出具体错误信息,根据错误信息修改bug

2.4 传参Mat

 在C++调用python过程中,不避免需要为其传参,上面main函数也有展示,这里有些参考博客;可以看到,传一些数字,字符串等比较简单
 但我们常常需要传入图像,如Mat对象,就有点麻烦了;对于不熟悉C++和Python混编的我,差点吐了;最终找到一篇文档,才完美解决,直接写一个转换函数,将Mat图像转化为PyObject对象

PyObject* convertImage(const cv::Mat& image) {
	//2D image with 3 channels.
	npy_intp dimensions[3] = { image.rows, image.cols, image.channels() };

	//image.dims = 2 for a 2D image, so add another dimension for channels.
	return  PyArray_SimpleNewFromData(image.dims + 1, (npy_intp*)&dimensions, NPY_UINT8, (void *)image.data);
}

2.5 CenterNet应用

 将CenterNet应用到自己的场景下,在本任务中,我们成功将其迁移到公司项目,效果非常不错,相较之前一些经典目标检测算法,效果杠杠的,漏检低,误检低,预测框的精度高
 因涉及公司项目,这里就不描述太多


Windows下C++调用Python版的Pytorch模型_第10张图片

你可能感兴趣的:(Pytorch,Python,深度学习)