QT调用python传递图像和二维数组,并接受python返回值(图像)

任务目的:

用QT调用python代码,将QT读取的图像(Mat矩阵)作为参数传入python中,将QT的二维数组作为参数传递给python,python接收QT传入的图像进行计算,将结果返回给QT。

实现过程

1.新建QT项目

说明:QT的编译环境我使用的MinGW 64,编译的release包
(1)按照下图依次选择Application,Qt Widgets Application, 然后点击右下角Choose。
QT调用python传递图像和二维数组,并接受python返回值(图像)_第1张图片
然后给项目起一个名字,名称:Qt_call_Python,创建路径:D:\QTWorkSpace(选择自己的路径)
QT调用python传递图像和二维数组,并接受python返回值(图像)_第2张图片
继续下一步
QT调用python传递图像和二维数组,并接受python返回值(图像)_第3张图片
继续下一步,在这里我使用的是Widget,在Base class里面选择QWiget即可,其它会自动默认,无须改变,当然改一下名字也可以,没什么问题。
QT调用python传递图像和二维数组,并接受python返回值(图像)_第4张图片
继续下一步
QT调用python传递图像和二维数组,并接受python返回值(图像)_第5张图片
这里注意,我选择的是Desktop Qt 5.14.2 MinGW 64-bit,主要是因为我编译Opencv时使用的就是这个,因此这里我选用这个编译器,这里需要搞清楚自己的opencv编译时自己选择的编译器,否则乱用的话可能会出现报错,例如找不到opencv中某些dll文件等等。
QT调用python传递图像和二维数组,并接受python返回值(图像)_第6张图片
继续下一步
QT调用python传递图像和二维数组,并接受python返回值(图像)_第7张图片
到此,项目新建完成,可以运行一下,看是否出现了空白窗体。

QT调用python传递图像和二维数组,并接受python返回值(图像)_第8张图片

2.配置项目环境

(1)图像的读取操作需要借助Opencv库进行,因此项目新建完成后首先要配置Opencv库目录
首先在项目上右键,选择“添加库”
QT调用python传递图像和二维数组,并接受python返回值(图像)_第9张图片
选择外部库
QT调用python传递图像和二维数组,并接受python返回值(图像)_第10张图片
点击库文件,依次参照我的目录层级,找到自己opencv编译好的文件,也就是libopencv_world410.dll.a的目录,然后选中该文件,点击打开。
QT调用python传递图像和二维数组,并接受python返回值(图像)_第11张图片
如下图,此处1位置,就是我们刚刚选择的libopencv_world410.dll.a的路径。注意2这个位置,包含路径在选择了1之后默认自己生成了一个目录,这个目录是错的,我们要通过浏览自己选择include的目录位置。
QT调用python传递图像和二维数组,并接受python返回值(图像)_第12张图片
选到include之后,然后其它的不要动,全部默认即可,点击下一步
QT调用python传递图像和二维数组,并接受python返回值(图像)_第13张图片
点击完下一步,出现这个界面,点击完成即可。
QT调用python传递图像和二维数组,并接受python返回值(图像)_第14张图片
(2)配置python环境
由于我们需要调用python,所以python环境我们也需要添加到库目录中,执行步骤与上面类似。
选择库文件,我使用的是anaconda的虚拟环境,如果电脑是使用的python,也参照我的目录去寻找红色标记3位置的文件,然后选择打开即可。
QT调用python传递图像和二维数组,并接受python返回值(图像)_第15张图片
包含路径同样是错误的,需要自己选择到include目录下。
QT调用python传递图像和二维数组,并接受python返回值(图像)_第16张图片

然后点击下一步,随后选择完成即可。
QT调用python传递图像和二维数组,并接受python返回值(图像)_第17张图片

(3)配置numpy环境

python科学计算库,numpy的使用频率太高,所以设计到图像的运算一般离不开它,但凡使用到python的numpy库了,不管是显示的调用还是潜在的调用,如果不配置这一步,都会报错

选择numpu库里面的npymath.lib即可,而且这次的包含目录不用自己选择,选择好库文件之后,包含路径正好就是numpy目录的include目录,如果不是的话自己修改一下即可。
QT调用python传递图像和二维数组,并接受python返回值(图像)_第18张图片
QT调用python传递图像和二维数组,并接受python返回值(图像)_第19张图片
QT调用python传递图像和二维数组,并接受python返回值(图像)_第20张图片
至此,环境配置完毕,运行一下,看没有报错吧
此时的项目文件增加了很多库目录的配置代码,如下
QT调用python传递图像和二维数组,并接受python返回值(图像)_第21张图片
按照下面的步骤依次选择编译器运行,可以下是否报错,没报错就说明成功了,没什么大问题,但是不保证
QT调用python传递图像和二维数组,并接受python返回值(图像)_第22张图片

(4)运行代码

python文件一定要放置在运行目录下面,也就是func.py
QT调用python传递图像和二维数组,并接受python返回值(图像)_第23张图片
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();
}

然后就是愉快的运行程序了,不出意外的话结果如下
QT调用python传递图像和二维数组,并接受python返回值(图像)_第24张图片
上图中1标记,表示的是QT接收的python的返回结果,打印的是图像的像素矩阵,标识2,是python代码运行时的窗口,显示的是一幅图像。

QT调用python传递图像和二维数组,并接受python返回值(图像)_第25张图片

上图1是python程序的打印结果,1标记是图像部分,2标记是QT传递二维数据,python打印结果。部分代码已经做了二次修正,结果都是正确没问题的。

整个项目包括运行目录的程序包组合可以在下面的链接进行获取。
https://download.csdn.net/download/weixin_43552197/87360247

你可能感兴趣的:(QT,OpenCV,python,qt,python,opencv)