前言
由于python高度的封装性和易用性,类似于matlab语言,在算法开发过程中比其他语言有更大的优势。但是在图像处理算开发中,笔者认为还是python比matlab更加简单。
笔者在学习数字图像处理过程中,最先接触到的便是开源的视觉图像库opencv。
在笔者早期开发图像处理算法过程中,通常是使用c++版本的opencv处理图像,得到需要的特征,然后把这些特征保存成txt,在matlab中绘图查看特征的分布,然后在matlab中进行处理,得到满意的结果后,把matlab代码用c++重新实现一遍。这个过程可以参考笔者原来做的https://blog.csdn/iamqianrenzhan/article/details/83537779线缆颜色顺序识别的算法。
但是这一过程有个弊端,在c++中使用opencv进行图像预处理并存入txt的特征是固定的,在matlab中处理如果发现效果不理想,需要更改在c++中进行图像预处理程序,这样势必在两个开发环境间来回切换,开发过程极不流畅。
最近在学习python的过程中,发现其可以直接使用opencv,并且其绘图的一些语法和matlab很类似,最重要的是c++调用python的方法不像调用matlab那样复杂。
所以就有了此文,在c++中调用python。
了解了在c++中调用python,以后开发视觉算法可以直接在python中进行,然后在c++中调用。至于采用这种方法的效率,可以暂时先不考虑。事实上,经过测试,满足一般需求绰绰有余,毕竟10ms和20ms的延迟人还感觉不到。
c++中调用python
pyhton模块初始化
path是pyhton模块所在路径,一定不能错。
bool pythonInit(string path)
{
Py_Initialize();
if (!Py_IsInitialized())
{
cout << "Py_Initialize failed." << endl;
return false;
}
// 将Python工作路径切换到待调用模块所在目录,一定要保证路径名的正确性
string chdir_cmd = string("sys.path.append(\"") + path + "\")";
const char *cstr_cmd = chdir_cmd.c_str();
PyRun_SimpleString("import sys");
PyRun_SimpleString(cstr_cmd);
return true;
}
pyhton函数加载
PyObject *pythonLoadModuleAndFunction(string moduleName, string functionName)
{
//加载模块
PyObject *pModule = PyImport_ImportModule(moduleName.c_str());
if (!pModule) // 加载模块失败
{
cout << "[ERROR] Python get module failed." << endl;
return pModule;
}
cout << "[INFO] Python get module succeed." << endl;
//加载函数
PyObject *pFunc = PyObject_GetAttrString(pModule, functionName.c_str()); //print_hello
if (!pFunc || !PyCallable_Check(pFunc)) // 验证是否加载成功
{
cout << "[ERROR] Can't find function" << endl;
return pFunc;
}
cout << "[INFO] Get function succeed 123." << endl;
return pFunc;
}
c++中把opencv的Mat传递给python
PyObject *MatToArray(Mat src)
{
import_array();
clock_t start, finish;
start = clock();
cv::Mat img = src;
//imread("1.bmp"); // CV_LOAD_IMAGE_COLOR
PyObject *PythonArray = PyTuple_New(1);
auto sz = img.size();
int x = sz.width;
int y = sz.height;
int z = img.channels();
uchar *CArrays = new uchar[x * y * z];
int iChannels = img.channels();
int iRows = img.rows;
int iCols = img.cols * iChannels;
// if (img.isContinuous())
// {
// iCols *= iRows;
// iRows = 1;
// }
uchar *p;
int id = -1;
for (int i = 0; i < iRows; i++)
{
// get the pointer to the ith row
p = img.ptr<uchar>(i);
// operates on each pixel
for (int j = 0; j < iCols; j++)
{
CArrays[++id] = p[j]; //连续空间
}
}
npy_intp Dims[3] = {y, x, z};
PyObject *PyArray = PyArray_SimpleNewFromData(3, Dims, NPY_UBYTE, CArrays);
PyTuple_SetItem(PythonArray, 0, PyArray);
finish = clock();
//cout << "\n赋值为" << (double)(finish - start) / CLOCKS_PER_SEC << "秒!" << endl;
return PythonArray;
}
调用是可以这样:
Mat mat;
pythonCallFunction(pFunc, MatToArray(mat));
python 文件中函数:
import cv2
def showimg(img):
cv2.imshow('img', img)
cv2.waitKey(0)
python函数参数制作
python函数参数就是一个元组,有几个参数,元组中就有几个元素。
比如python需要三个整数参数,可以这样写:
PyObject *pArgs = PyTuple_New(3);
PyTuple_SetItem(pArgs, 0, Py_BuildValue(“i”, 1));
PyTuple_SetItem(pArgs, 1, Py_BuildValue(“i”, 2));
PyTuple_SetItem(pArgs, 2, Py_BuildValue(“i”, 3));
python需要字典参数
PyObject *pDict = PyDict_New(); //创建字典类型变量
PyDict_SetItemString(pDict, “Name”, Py_BuildValue(“s”, “Zhangsan”)); //往字典类型变量中填充数据
PyDict_SetItemString(pDict, “Address”, Py_BuildValue(“s”, “BeiJing”));
PyObject *pArgs = PyTuple_New(1);
PyTuple_SetItem(pArgs, 0, pDict)
同理,python需要数组参数,可以使用PyList_New 函数
python返回参数解析
python如果多个参数返回,也是元组对象。
环境配置相关
windows
1.编译时选择的64位和32位要和python版本匹配。一般环境都配置好,但是出现“无法解析的外部函数”,多半就是位数不对。
2.python3需要修改object.h和pyconfig.h 文件,pyhton2没有测试。
3.在qt中调用python还需要修改object.h中slots变量定义的地方,这个问题只有python3有。
qt中pro文件配置:
INCLUDEPATH+= D:\Python\Lib\site-packages\numpy\core\include \
D:\Python\include
CONFIG(debug, debug|release) {
message("debug")
LIBS += D:\Python\libs\python36_d.lib \
D:\Python\libs\python3_d.lib
} else {
message("release")
LIBS += D:\Python\libs\python36.lib \
D:\Python\libs\python3.lib
}
Ubuntu:
在qt中调用python还需要修改object.h中slots变量定义的地方,这个问题只有python3有。
Ubuntu中cmake配置CmakeList.txt:
# find required opencv
find_package(OpenCV 3.2 REQUIRED)
# find required python
find_package(PythonLibs 3.6 REQUIRED)
# directory of opencv headers
include_directories(${OpenCV_INCLUDE_DIRS})
# directory of python headers
include_directories(${PYTHON_INCLUDE_DIRS})
include_directories("./")
# directory of opencv library
link_directories(${OpenCV_LIBRARY_DIRS})
link_directories(${PYTHON_LIBRARY_DIRS})
Ubuntu中QT配置:
INCLUDEPATH+=/usr/include \
/usr/include/python3.6m \
/home/qian/.local/lib/python3.6/site-packages/numpy/core/include/
if(contains(DEFINES,ARM)){
message("compile for arm linux")
LIBS += /usr/lib/aarch64-linux-gnu/libmysqlclient.so
LIBS += /usr/lib/libopencv_*.so.3.3
}else{
message("compile for amd linux")
LIBS += /usr/lib/x86_64-linux-gnu/libmysqlclient.so
LIBS += /usr/lib/x86_64-linux-gnu/libopencv_*.so.3.2
LIBS += /usr/lib/python3.6/config-3.6m-x86_64-linux-gnu/libpython3.6.so
}
一些测试结论
一个python函数导入c++后可以被调用多次,但是不能在不用保护措施的前提下在多线程中多次调用。
可以在一次初始化后导入多个python函数。
程序执行到导入python模块函数返回值是0x0,说明该python模块不在搜索路径,可以在初始化后添加或者直接把自定义的python文件放到exe同级目录下。
程序执行到导入python模块函数直接崩溃,有可能是在该python模块中import某些模块导致的。
pyhton正常执行,但程序返回值是-1,可能是python函数中有错误,最常见的是读取文件路径等。
调试过程中遇到的一些问题
问题1
在windows中,链接时报告
1>pythonIniti.obj : error LNK2019: 无法解析的外部符号 __imp___Py_NegativeRefcount,该符号在函数 “public: __thiscall boost::python::api::object_base::~object_base(void)” (??1object_base@api@python@boost@@QAE@XZ) 中被引用
1>pythonIniti.obj : error LNK2001: 无法解析的外部符号 __imp___Py_RefTotal
解决方法:
修改两个头文件
1 注释掉object.h
//#define Py_TRACE_REFS
2 pyconfig.h
//# define Py_DEBUG
问题2
不管在windows还是Ubuntu平台,在QT中调用python3时,在object.h中有一行slots会出错,这是因为QT宏和python3中变量命名冲突,可修改object文件,在slots变量定义前取消slots宏定义,之后重新定义slots为Q_SLOTS。
当然还有一些比较小的问题,这都可以在网上找到解决办法。
更多推荐
QT和cmake工程中实现c++调用python具体实现,环境配置以及常见问题
发布评论