C++ 调用 Python 接口 Mat转Numpy

发布时间 2023-08-15 11:19:05作者: DoubleLi

参考网站:

  1. https://blog.csdn.net/qq7835144/article/details/106073110?utm_medium=distribute.pc_relevant.none-task-blog-baidujs_title-1&spm=1001.2101.3001.4242

  2. https://blog.csdn.net/weixin_46400740/article/details/116711323?spm=1001.2014.3001.5501

 

1、配置项目属性表

将Python目录下的include和libs文件夹路径配置到属性表中,如果需要用到虚拟环境,就把envs虚拟环境下的相关文件路径配置到属性表

如果 需要用到一些其他的第三方库 就要再次进行关联。例如用numpy 就需要去Lib/site-packages 下 关联需要用到的包

 

 

2、C++代码编辑

导入头文件 Python.h:#include <Python.h>

 
 
// 架构
 
#include <iostream>
 
#include "Python.h"
 
using namespace std;
 
int main()
 
{
 
//初始化Python环境
 
Py_SetPythonHome(L"D:\\anaconda");//路径为python.exe所在路径,注意使用双斜杠或反斜杠
 
Py_Initialize();
 
// 添加项目所属 目录
 
PyRun_SimpleString("import sys");
 
PyRun_SimpleString("sys.path.append('.')"); //路径问py文件所在目录,如果在当前目录下则使用 '.'
 
PyObject * pFunc = NULL;
 
//导入模块
 
PyObject* pModule = PyImport_ImportModule("test");//py文件名
 
if (!pModule)
 
{
 
cout << "Python get module failed." << endl;
 
return 0;
 
}
 
cout << "Python get module succeed." << endl;
 
 
 
// PyObject_GetAttrString
 
pFunc = PyObject_GetAttrString(pModule, "func"); //函数名为 func
 
PyEval_CallObject(pFunc, NULL); //调用 无返回类型
 
Py_Finalize();
 
return 0;
 
}
 
 

报错1:

 

 

 

3、传参类型表

1.调用无参 无返回值

PyEval_CallObject(pFunc, NULL);  //直接调用
 

 

2.调用无参 但有返回值

 
 
//调用无参 但有返回值
 
PyObject * pp = PyEval_CallObject(pFunc, NULL); //返回值给pp Object对象
 
int res = 0; //接收数据 int类型
 
PyArg_Parse(pp, "i", &res); //类型转换 i就是整形
 
cout << "res:" << res << endl;//输出结果
 
 

3.调用有参 无返回值

 
 
PyObject* pArgs = PyTuple_New(2); //初始化俩个参数
 
PyTuple_SetItem(pArgs, 0, Py_BuildValue("i", 2)); //0:表示序号。第一个参数。
 
PyTuple_SetItem(pArgs, 1, Py_BuildValue("i", 4)); //1:也表示序号。第二个参数。i:表示传入的参数类型是int类型。
 
PyEval_CallObject(pFunc, pArgs); //无返回类型
 
 

4.调用有参 有返回值

 
 
PyObject* pArgs = PyTuple_New(2); //初始化两个参数
 
PyTuple_SetItem(pArgs, 0, Py_BuildValue("i", 2)); //0:表示序号。第一个参数。
 
PyTuple_SetItem(pArgs, 1, Py_BuildValue("i", 4)); //1:也表示序号。第二个参数。i:表示传入的参数类型是int类型。
 
PyObject * pp = PyEval_CallObject(pFunc, pArgs); //无返回类型
 
int res = 0;
 
PyArg_Parse(pp, "i", &res);//转换返回类型
 
cout << "res:" << res << endl;//输出结果
 
 

5.c++ Mat 转 python Numpy

Mat转Numpy需要配置numpy库,见上面

 
 
#include <numpy/arrayobject.h> //导入numpy头文件
 
 
 
Mat img = imread("./frame.png"); // 读取图片
 
if (img.empty())
 
{
 
cout << "img read wrong" << endl;
 
Py_Finalize();
 
return -1;
 
}
 
cout << img.size() << endl;
 
 
 
// 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];//连续空间
 
}
 
}
 
import_array1(-1);
 
npy_intp Dims[3] = { y, x, z }; //注意这个维度数据!
 
PyObject *PyArray = PyArray_SimpleNewFromData(3, Dims, NPY_UBYTE, CArrays);
 
 

报错:

解决办法:Py_Initialize()后加入“import_array()”语句即可。 在有返回值的函数里,括号里需要有对应类型的返回值

注意:当编译"import_array()"时,可能会出现以下错误: 

解决方法:在自己安装的python目录中搜索"object.h"文件,将其56行"#define Py_REF_DEBUG"语句注释掉即可。

6、 c++ PyArrayObject 转 Mat

 
 
//将PyObject *pReturn 转换为Mat类型
 
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));
 
imwrite("./cut.png", mask);
 
imshow("su", mask);
 
waitKey(0);
 
// Py_XDECREF(PyArray);
 
/*这里Py_XDECREF(ArgList); 和 Py_XDECREF(PyArray);不能同时使用,否则会引起内存访问冲突
 
* 我的理解是:PyTuple_SetItem并不复制数据,只是引用的复制。因此对这两个对象中的任意一个使用
 
* Py_XDECREF都可以回收对象。使用两次的话反而会导致冲突。
 
*/
 
Py_XDECREF(ArgList);
 
delete[] CArrays; // 释放数组内存,最好在PyArray被使用完以后释放
 
CArrays = nullptr;
 
Py_XDECREF(pReturn);
 
}
 
 

 

VS Qt中C++调用Python 报错

问题出在python.h这个头文件中存在一个变量名叫“slots”,然而slots又是qt的关键字,所以qt的moc会把它当成宏先处理掉。

找到python/include下object.h文件 将第448行改为

 
 
#undef slots
 
PyType_Slot *slots; /* terminated by slot==0. */
 
#define slots Q_SLOTS
 
 

c++ 调用Python接口  Mat传参与接收 完整代码

c++端代码

 
 
#include "Python.h"
 
#include <numpy/ndarrayobject.h>
 
#include <opencv2/opencv.hpp>
 
#include <iostream>
 
 
 
using namespace cv;
 
using namespace std;
 
 
 
int main() {
 
// 初始化-----------------------------------------------
 
Py_SetPythonHome(L"D:/anaconda");
 
Py_Initialize();
 
cout << "Python start --------------------" << endl;
 
if (!Py_IsInitialized()) {
 
cout << "Python start defeat--------------------" << endl;
 
return -1;
 
}
 
// 加载python文件----------------------------------------------
 
PyRun_SimpleString("import sys");
 
PyRun_SimpleString("sys.path.append('.')"); // demo_test.py的路径
 
PyObject *moduleName = PyUnicode_FromString("demo_test"); //载入名为demo_test.py的文件
 
PyObject *pModule = PyImport_Import(moduleName);
 
if (!pModule) { //如果不存在改文件,则结束
 
printf("can't find python file");
 
Py_Finalize(); //关闭python解释器
 
return -1;
 
}
 
PyObject *pFunc = PyObject_GetAttrString(pModule, "run"); //获取函数
 
if (!pFunc)
 
{
 
printf("can't find function [run]");
 
Py_Finalize();
 
return -1;
 
}
 
 
 
int *a;
 
// 读取图片---------------------------------------------------
 
Mat img = imread("frame.png"); // 读取图片
 
if (img.empty())
 
{
 
cout << "img read wrong" << endl;
 
Py_Finalize();
 
return -1;
 
}
 
cout << img.size() << endl;
 
 
 
// 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];//连续空间
 
}
 
}
 
import_array1(-1);
 
npy_intp Dims[3] = { y, x, z }; //注意这个维度数据!
 
PyObject *PyArray = PyArray_SimpleNewFromData(3, Dims, NPY_UBYTE, CArrays);
 
 
 
// 准备其他python函数需要的参数
 
//...
 
 
 
 
 
// 将图片以及其他参数进行封装
 
PyObject *ArgList = PyTuple_New(1); //参数列表:创建一个长度为1的元组
 
PyTuple_SetItem(ArgList, 0, PyArray); //将PyArray的引用指向元组ArgList的第0个元素
 
 
 
PyObject *pReturn = PyObject_CallObject(pFunc, ArgList);
 
if (pReturn == NULL) {
 
printf("Return value is 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));
 
imwrite("test.png", mask);
 
imshow("out", mask);
 
waitKey(0);
 
// Py_XDECREF(PyArray);
 
/*这里Py_XDECREF(ArgList); 和 Py_XDECREF(PyArray);不能同时使用,否则会引起内存访问冲突
 
* 我的理解是:PyTuple_SetItem并不复制数据,只是引用的复制。因此对这两个对象中的任意一个使用
 
* Py_XDECREF都可以回收对象。使用两次的话反而会导致冲突。
 
*/
 
Py_XDECREF(ArgList);
 
delete[] CArrays; // 释放数组内存,最好在PyArray被使用完以后释放
 
CArrays = nullptr;
 
Py_XDECREF(pReturn);
 
 
 
}
 
//Py_CLEAR(moduleName);
 
//Py_CLEAR(pModule);
 
Py_XDECREF(moduleName);
 
Py_XDECREF(pModule);
 
Py_XDECREF(pFunc); //Py_XDECREF是很有必要的,为了避免内存泄漏
 
Py_Finalize(); // 关闭Python
 
return 0;
 
}
 
 

python端代码

注意这里的返回值是元祖形式

 
 
#coding:utf-8
 
import cv2
 
import numpy as np
 
 
 
def run(imgdata):
 
 
 
return imgdata, 0
 
 

完结