文章目录
win10 C++调用python脚本实现两个环境下opencv参数传
本文主要实现C++调用python脚本,实现向python函数传递图像和从python函数的返回值接收图像。
实现环境:win10+vs2015+Anaconda3
vs2015配置opencv
略
vs2015配置python环境
由于涉及到opencv参数传递,我们除了要在C++中调用python的相关函数,还需要调用numpy的相关函数。因此配置环境时即需要配置python,又需要配置numpy。
- 在release模式下添加属性表,这里我将其命名为python
- 打开新创建的属性表,添加包含目录D:\Anaconda3\include 和 D:\Anaconda3\Lib\site-packages\numpy\core\include。这里的目录是我的Anaconda安装目录。第一个目录中包含python解释器,第二个目录中包含numpy的头文件。如下图所示。
- 添加库目录,路径分别为D:\Anaconda3\libs 和 D:\Anaconda3\Lib\site-packages\numpy\core\lib。同理,前者对应python,后者对应numpy。如下图所示。
- 添加依赖库,如下图所示。这两个依赖库分别是python37.lib 和 npymath.lib。分别位于D:\Anaconda3\libs 和 D:\Anaconda3\Lib\site-packages\numpy\core\lib。
- 到此为止属性配置完成。需要注意的是,目前只是针对release模式下进行属性配置。由于确实debug模式下所需要的依赖库,因此无法对debug模式进行属性配置。也有些博客讲解了如何生成debug模式下的python依赖库。
代码(部分代码来自于其他CSDN博客,由于该博客成文较晚,忘记了那些参考链接,如有侵权请留言)
C++代用python代码最糟心的一点是内存泄漏,我的代码并不能完全避免内存泄漏,至少目前还不敢保证没有问题。仍在进一步的测试中。
- python代码
#coding:utf-8
import cv2
def run(imgdata): # 输入参数是一张图片
print(imgdata.shape) # 打印图片尺寸
return imgdata, 0 # 返回图片和一个数字
- C++代码
#include "Python.h"
#include <numpy/arrayobject.h>
//配置属性表以后vs就可以检测到这两个头文件。如果没有,切换一下release、debug模式,刷新一下
#include <opencv2/opencv.hpp>
using namespace cv;
using namespace std;
int main() {
Py_SetPythonHome(L"D:\\Anaconda3"); //设置Python路径
Py_Initialize(); // 初始化Python
import_array(); // 使用Numpy的相关函数前必须声明这一行代码
// 检查初始化是否成功
if (!Py_IsInitialized())
return -1;
PyRun_SimpleString("import sys");
PyRun_SimpleString("sys.path.append('./')"); // demo_test.py的路径,这里与c_call_python放在同一个目录下
PyObject *moduleName = PyUnicode_FromString("demo_test"); //载入名为demo_test.py的文件
PyObject *pModule = PyImport_Import(moduleName);
if (!pModule) //如果不存在改文件,则结束
{
printf("can't find py");
getchar(); //点击回车关闭窗口
Py_Finalize(); //关闭python解释器
return -1;
}
int *a;
for (int i = 1; i<10000000000; i++) // 重复执行测试稳定性
{
cout << i << endl;
Mat img = imread("su.jpg"); // 读取图片
if (img.empty())
{
Py_Finalize();
return -1;
}
// CV::Mat 转 python numpy
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 *ArgList = PyTuple_New(1); //参数列表:创建一个长度为1的元组
PyObject *PyArray = PyArray_SimpleNewFromData(3, Dims, NPY_UBYTE, CArrays);
PyTuple_SetItem(ArgList, 0, PyArray); //将PyArray的引用指向元组ArgList的第0个元素
PyObject *pFunc = PyObject_GetAttrString(pModule, "run"); //获取函数
if (!pFunc)
{
printf("can't find function [run]");
getchar();
Py_Finalize();
return -1;
}
PyObject *pReturn = PyObject_CallObject(pFunc, ArgList);
if (pReturn == NULL)
{
Py_Finalize();
return -1;
}
PyArrayObject *Py_array1;
//读取从python脚本返回的numpy值
//查看是否是元组数据
if (PyTuple_Check(pReturn))
{
//当返回值不止一个,pReturn是一个元组
PyArg_UnpackTuple(pReturn, "ref", 2, 2, &Py_array1, &a); //解析元组的内容
//获取矩阵维度
npy_intp *Py_array1_shape = PyArray_DIMS(Py_array1); //获取元组第一个元素(矩阵)的大小
npy_intp array1row = Py_array1_shape[0];
npy_intp array1col = Py_array1_shape[1];
npy_intp array1high = Py_array1_shape[2];
Mat mask(array1row, array1col, CV_8UC3, PyArray_DATA(Py_array1)); //转三通道彩色图
// Py_XDECREF(PyArray);
/*这里Py_XDECREF(ArgList); 和 Py_XDECREF(PyArray);不能同时使用,否则会引起内存访问冲突
* 我的理解是:PyTuple_SetItem并不复制数据,只是引用的复制。因此对这两个对象中的任意一个使用
* Py_XDECREF都可以回收对象。使用两次的话反而会导致冲突。
*/
Py_XDECREF(ArgList);
delete[] CArrays; // 释放数组内存,最好在PyArray被使用完以后释放
CArrays = nullptr;
Py_XDECREF(pFunc); //Py_XDECREF是很有必要的,为了避免内存泄漏
Py_XDECREF(pReturn);
// delete(Py_array1);
// imshow("su", mask);
// waitKey(0);
mask.release();
}
}
// Py_CLEAR(moduleName);
// Py_CLEAR(pModule);
Py_XDECREF(moduleName);
Py_XDECREF(pModule);
Py_Finalize(); // 关闭Python
// system("pause");
// getchar();
return 0;
}