有时常常遇到两种语言切换的问题,比如在C++中实现了一个图像处理算法,将其封装为一个函数,该函数的输入输出类型为OpenCV中常用的类型如cv::Mat等,需要在Python中调用,这时可以愉快地利用OpenCV生成Python包的方法。
利用OpenCV生成Python包的原理,将我们写的C++程序编译成Python包(本文介绍的是在Windows环境下,使用VS,Python 64位,其他环境按需修改即可,主要是几个文件)
原理参考 How OpenCV-Python Bindings Works?
参考learnopencv,本文介绍了cv2.cpp
的修改方法,与参考有些区别,改动少一些
将cv2.cpp
,pycompat.hpp
,gen2.py
,hdr_parser.py
这几个文件从OpenCV源码中拷贝出来,大致路径在moudules/python/src
目录结构(要新建的后文介绍)
--cv2.cpp
--pycompat.hpp
--gen2.py
--hdr_parser.py
--headers.txt
--src
----pycvtest.h
----pycvtest.cpp
--pycvtest
pycvtest.h
中写这些,注意命名空间必须使用cv,减少后续工作量,注意不要直接导入opencv.hpp
,因为它导入了大量的库,如果不需要不用导入,按需修改,本例中只是用了core.hpp
用来展示#pragma once
//#include
#include
namespace cv {
CV_EXPORTS_W void add_one_for_test(cv::InputArray t_in, cv::OutputArray t_out);
}
pycvtest.cpp
中写这些#include "pycvtest.h"
namespace cv {
void add_one_for_test(cv::InputArray t_in, cv::OutputArray t_out)
{
cv::add(t_in, 1, t_out);
}
hdr_parser.py
中的列表opencv_hdr_list
,把我们要加的头文件写进去,其他都不要opencv_hdr_list = [
"src/pycvtest.h"
]
gen2.py
中if hdr.find('opencv2/') >= 0: #Avoid including the shadow files
self.code_include.write( '#include "{0}"\n'.format(hdr[hdr.rindex('opencv2/'):]) )
改成
self.code_include.write( '#include "{0}"\n'.format(hdr[hdr.rindex('src/'):]) )
这个会影响好后生成的pyopencv_generated_include.h
文件导入头文件的代码,生成后可查看,按需修改~
src/pycvtest.h
python gen2.py pycvtest headers.txt
,这时看pycvtest
文件夹,应该已经生成好了pyopencv
开头的文件了OpenCV
)cv2.cpp
,pycompat.h
,pycvtest.h
,pycvtest.cpp
include
路径,就是刚刚生成以pyopencv_
文件名开头的文件目录pycvtest
CVAPI_EXPORTS
,必须写出来,不然不会导出lib
目录,大致是Python安装路径\Python35\libs
include
目录,大致是Python安装路径\Python35\include
什么什么\Lib\site-packages\numpy\core\include
.pyd
pycvtest
下面愉快地改cv2.cpp
文件
PyOpenCV_Converter
,里面的两个函数to
from
没有写,但是后面调用了,VS会编译不通过,我改的方法是注释取消,然后把函数定义写出来,函数内容什么都不用写就是下面这样,后面针对具体类型的模板函数会重载下面的pyopencv_to
和pyopencv_from
,所以不用担心,有更好改法的朋友请告知,谢谢~template<typename T, class TEnable = void> // TEnable is used for SFINAE checks
struct PyOpenCV_Converter
{
static inline bool to(PyObject* obj, T& p, const char* name) {
return true;
}
static inline PyObject* from(const T& src) {
return nullptr;
}
};
// #include "opencv2/opencv_modules.hpp"
#undef HAVE_OPENCV_HIGHGUI
#undef HAVE_OPENCV_DNN
//#include "pyopencv_custom_headers.h"
PyInit_cv2()
这个初始化包的函数名称改成自己的PyInit_pycvtest()
,文件中的全都改一下,就改两个地方就行了,就函数声明那个位置pycvtest.pyd
拷贝需要的动态链接库到pyd文件夹,或者添加动态链接库的目录到Python运行目录,然后导入包,还有一种方法是先导入cv2
然后再导入pycvtest
,如下所示
import numpy as np
import cv2
import pycvtest as pct
a = np.array([[2, 3, 4]])
output = pct.add_one_for_test(a)
print(output)
好了~