现象:
使用c++调用python时,根据网上搜到的教程使用python提供C API库调用时,发现只要py里包含了cv2库,在导入模块时就会报错。报错信息如下:
这个报错信息在不同的python版本可能具体信息稍有不同,但总的来说就是报错找不到cv2这个dll。但是如果直接用python运行py脚本是可以运行成功的。
原因分析:
省时间直接跳解决方法。
1.python文件分析
通过更改py文件发现即使是使用最简单的代码,只要import cv2,使用c++调用就会报错。
import sys
sys.OpenCV_LOADER_DEBUG = 1
import cv2
def add_fun(a,b):
print(cv2.__version__)
return a+b
if __name__ == '__main__':
print(add_fun(2,3))
python环境下运行不报错,所以不是代码问题。根据网上教程尝试在py里手动添加path库路径
sys.path.append("E:\\ProgramData\\Anaconda3\\envs\\ctest\\Lib\\site-packages")
sys.path.append("E:\\ProgramData\\Anaconda3\\envs\\ctest\\Scripts")
发现依旧不行。手动添加第三方库路径可以解决某些库报错的情况,但是不适合cv2库报错。
2.c文件分析
#include <Python.h>
#include <iostream>
int main() {
初始化Python解释器
//_putenv_s("PYTHONHOME", "D:\\Users\\16092\\Python\\Python38");
//_putenv_s("PYTHONPATH", "D:\\Users\\16092\\Python\\Python38\\Lib\\site-packages");
//_putenv_s("PATH", "D:\\Users\\16092\\anaconda3\\envs\\bino_tracker\\Library\\bin;D:\\Users\\16092\\Python\\Python38\\Lib\\site-packages\\cv2;");
_putenv_s("PYTHONHOME", "D:\\Users\\16092\\anaconda3\\envs\\bino_tracker");
Py_Initialize();
// 设置PYTHONPATH以包含你的Python文件的目录
PyRun_SimpleString("import sys");
PyRun_SimpleString("sys.path.append('.')"); // '.'表示当前目录
/*PyRun_SimpleString("import sys");
PyRun_SimpleString("print(sys.path)");*/
// 导入Python模块
PyObject* pName = PyUnicode_DecodeFSDefault("mytest"); // 模块名
PyObject* pModule = PyImport_Import(pName);
Py_DECREF(pName);
去掉py文件中的import cv2 ,发现可以调用成功,所以可以确定vs中配置的python库没有问题。
后面尝试修改调用方式,发现无论是使用最原始的python capi,还是使用boost 和qt 调用python,只要加了cv2模块,就会报错。报错信息大同小异都是找不到cv2模块。
又尝试在c++里面指定path发现依然报错。
3.环境分析
由于已经能够确定代码没有问题,所以最后的可能只能是环境问题。由于报错信息是缺少cv2 DLL文件。使用本地搜索工具发现没有cv2.dll文件,只有cv2.pyd文件。而python调用的也是这个文件。所以大概率是这个文件的问题。尝试复制这个文件到当前目录发现还是不行。
解决方案:
由于报错是cv2.dll但是只有cv2.pyd库,所以猜测是因为cv2.pyd的依赖在c++里找不到。使用Dependency Walker分析cv2.pyd的依赖,发现了很奇怪的事
这个pyd文件依赖了python3.dll。将python3.dll拷贝到与py文件同目录,发现调用成功。
总结:
网上c++python互相调用的代码很多,但是关于环境配置教程比较少。我使用的是vs2022根据教程在c++附加目录,和链接器输入,分别添加了头文件和库目录后,进行编译vs会提示没有python38.dll文件,这个提示和你使用的lib相对应。此时将这个dll放入项目目录,是可以编译过去的,但是因为py中有时导入的第三方库引用了其它的dll,这个时候就会加载失败,但是输出的报错信息又不明确,这是个巨坑。所以以后其它类似报错也可以用此方法排查。
此外我使用的是vs2022+conda +python3.8,有文章说conda环境不能使用,实测是可以的。至于上面提到的在py文件中手动添加第三方库的做法是适用于某些情况的,实测不适用于cv2模块。
还有一点是注意c++代码里加载的是哪个python环境,如果不设置回去加载系统环境变量里的python环境,建议手动指定,保持与直接运行py文件的环境一致,这样方便打包移植。
最后,调用pyd方法和py文件方法一模一样。教程网上很多,这里就不写了。