用QT调用python代码,将QT读取的图像(Mat矩阵)作为参数传入python中,将QT的二维数组作为参数传递给python,python接收QT传入的图像进行计算,将结果返回给QT。
说明:QT的编译环境我使用的MinGW 64,编译的release包
(1)按照下图依次选择Application,Qt Widgets Application, 然后点击右下角Choose。
然后给项目起一个名字,名称:Qt_call_Python,创建路径:D:\QTWorkSpace(选择自己的路径)
继续下一步
继续下一步,在这里我使用的是Widget,在Base class里面选择QWiget即可,其它会自动默认,无须改变,当然改一下名字也可以,没什么问题。
继续下一步
这里注意,我选择的是Desktop Qt 5.14.2 MinGW 64-bit,主要是因为我编译Opencv时使用的就是这个,因此这里我选用这个编译器,这里需要搞清楚自己的opencv编译时自己选择的编译器,否则乱用的话可能会出现报错,例如找不到opencv中某些dll文件等等。
继续下一步
到此,项目新建完成,可以运行一下,看是否出现了空白窗体。
(1)图像的读取操作需要借助Opencv库进行,因此项目新建完成后首先要配置Opencv库目录
首先在项目上右键,选择“添加库”
选择外部库
点击库文件,依次参照我的目录层级,找到自己opencv编译好的文件,也就是libopencv_world410.dll.a的目录,然后选中该文件,点击打开。
如下图,此处1位置,就是我们刚刚选择的libopencv_world410.dll.a的路径。注意2这个位置,包含路径在选择了1之后默认自己生成了一个目录,这个目录是错的,我们要通过浏览自己选择include的目录位置。
选到include之后,然后其它的不要动,全部默认即可,点击下一步
点击完下一步,出现这个界面,点击完成即可。
(2)配置python环境
由于我们需要调用python,所以python环境我们也需要添加到库目录中,执行步骤与上面类似。
选择库文件,我使用的是anaconda的虚拟环境,如果电脑是使用的python,也参照我的目录去寻找红色标记3位置的文件,然后选择打开即可。
包含路径同样是错误的,需要自己选择到include目录下。
python科学计算库,numpy的使用频率太高,所以设计到图像的运算一般离不开它,但凡使用到python的numpy库了,不管是显示的调用还是潜在的调用,如果不配置这一步,都会报错
选择numpu库里面的npymath.lib即可,而且这次的包含目录不用自己选择,选择好库文件之后,包含路径正好就是numpy目录的include目录,如果不是的话自己修改一下即可。
至此,环境配置完毕,运行一下,看没有报错吧
此时的项目文件增加了很多库目录的配置代码,如下
按照下面的步骤依次选择编译器运行,可以下是否报错,没报错就说明成功了,没什么大问题,但是不保证
python文件一定要放置在运行目录下面,也就是func.py
func.py代码如下
# This Python file uses the following encoding: utf-8
import cv2
import numpy as np
def myfunc(imgdata):
print(121)
image = cv2.imread("3.jpeg")
cv2.imshow("mywindow", image)
print(123)
print(imgdata.shape)
print(124)
a,b=imgdata.shape[0],imgdata.shape[1]
print(125)
result=imgdata[0:a>>1,0:b>>1]
print(126)
print("python out",result.shape)
#return result,0
# print(imgdata.shape)
#a,b=imgdata.shape[0],imgdata.shape[1]
#result=imgdata[0:a>>1,0:b>>1]
#print("python out",result.shape)
#return result,0
print(np.arange(2))
return image
def myfunc1(data):
print(911)
print(data)
print("over")
# if__name__ == "__main__":
# pass
QT项目中,其它都默认即可,主要是对main.cpp进行了改动,代码如下
#include "widget.h"
#include
#include
#include
#include
#include
#include
#include
using namespace std;
using namespace cv;
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
// 设置主目录,就是python的主目录
Py_SetPythonHome(L"D:\\Anaconda3\\envs\\python38");
// 初始化python模块
Py_Initialize();
import_array();
// 判定是否初始化成功
if(!Py_IsInitialized())
{
cout<<"初始化python环境失败!"<<endl;
return -1;
}
/*
* 测试初始化后的python环境,自测使用
PyRun_SimpleString("import sys");//引入sys模块
PyRun_SimpleString("sys.path.append('./')");//将存放python文件的路径加入搜寻路径
PyRun_SimpleString("import numpy as np");
PyRun_SimpleString("import os");
PyRun_SimpleString("from datetime import datetime, time");
PyRun_SimpleString("print(\"hello python\")");
cout << "point1" << endl;
*/
// 将python文件导入成PyObject对象,func是python文件的名称,此处不要加.py
PyObject* m_pyObj = PyImport_ImportModule("func");
// 获取函数,myfunc表示调用的python文件内的myfunc函数
PyObject *pFunc = PyObject_GetAttrString(m_pyObj, "myfunc");
PyObject *pFunc_1 = PyObject_GetAttrString(m_pyObj, "myfunc1");
/*
* 简单调用测试,测试python文件是否调用成功
QString inputPara = "this is paras";
//调用python中的Myfun函数,返回值是ret,s代表是字符串格式,如果是int则写i,double写d
auto ret = PyObject_CallMethod(m_pyObj, "myfunc", "i", 2);
*/
/
// 读取一张图像
cv::Mat image = cv::imread("3.jpeg");
// 打印图像的大小
cout<<image.size()<< endl;
// 保存图像大小
auto sz = image.size();
int x = sz.width;
int y = sz.height;
int z = image.channels();
//注意这个维度数据!
npy_intp Dims[3] = {y, x, z};
cout<<"point2"<<endl;
cout << Dims[0] << endl;
int array_x = 3;
int array_y = 3;
// 定义二维数组
double CArrays[3][3] = {{1.3, 2.4, 5.6}, {4.5, 7.8, 8.9}, {1.7, 0.4, 0.8}};
// 设置维度数据
npy_intp Dims_array[2] = {3, 3};
int nElem_array = 3 * 3;
uchar* m_array = new uchar[nElem_array];
// 将数组复制到新建的数组中
std::memcpy(m_array, image.data, nElem_array * sizeof(uchar));
// 生成包含这个多维数组的PyObject对象,使用PyArray_SimpleNewFromData函数,第一个参数3表示维度,第二个为维度数组Dims,第三个参数指出数组的类型,第四个参数为数组
PyObject *PyArray_array = PyArray_SimpleNewFromData(2, Dims_array, NPY_DOUBLE, CArrays);
// 创建一个大小为1的元组对象
PyObject *ArgArray_array = PyTuple_New(1);
// 设置Tuple的某一个元素,参数为索引index和要传入的PyObject对象
PyTuple_SetItem(ArgArray_array, 0, PyArray_array);
// 调用函数,传入Numpy Array 对象。
PyObject_CallObject(pFunc_1, ArgArray_array);
//调用函数,传入Numpy Array 对象。
PyObject_CallMethod(m_pyObj, "myfunc1", "O", PyArray_array);
// 所有像素点的数量,要乘以通道数,灰度图单通道,彩色图三通道
int nElem = x * y * z;
// 创建与像素个数大小相同的数组
uchar* m = new uchar[nElem];
// 将Mat格式矩阵数据复制到新建的数组中
std::memcpy(m, image.data, nElem * sizeof(uchar));
// 生成包含这个多维数组的PyObject对象,使用PyArray_SimpleNewFromData函数,第一个参数3表示维度,第二个为维度数组Dims,第三个参数指出数组的类型,第四个参数为数组
PyObject *PyArray = PyArray_SimpleNewFromData(3, Dims, NPY_UINT8, (void*)m);
// 创建一个大小为1的元组对象
PyObject *ArgArray = PyTuple_New(1);
// 设置Tuple的某一个元素,参数为索引index和要传入的PyObject对象
PyTuple_SetItem(ArgArray, 0, PyArray);
// 调用函数,传入Numpy Array 对象。
PyObject_CallObject(pFunc, ArgArray);
//调用函数,传入Numpy Array 对象。
PyObject *rest = PyObject_CallMethod(m_pyObj, "myfunc", "O", PyArray);
// 获取返回值
if(rest)
{
PyObject *repr = PyObject_Repr(rest);
PyObject *imgfrompy = PyUnicode_AsEncodedString(repr, "utf-8", "strict");
char *result = PyBytes_AsString(imgfrompy);
// 打印返回值
qDebug()<<result<<"end"<<endl;
}
Py_Finalize();
Widget w;
w.show();
return a.exec();
}
然后就是愉快的运行程序了,不出意外的话结果如下
上图中1标记,表示的是QT接收的python的返回结果,打印的是图像的像素矩阵,标识2,是python代码运行时的窗口,显示的是一幅图像。
上图1是python程序的打印结果,1标记是图像部分,2标记是QT传递二维数据,python打印结果。部分代码已经做了二次修正,结果都是正确没问题的。
整个项目包括运行目录的程序包组合可以在下面的链接进行获取。
https://download.csdn.net/download/weixin_43552197/87360247