将Python机器学习模型集成到C++ Qt客户端应用程序中|Qt调用python详解

0、前言

有几个不同的选项可以将你的Python机器学习模型集成到你的C++ Qt客户端应用程序中。以下是一些可能的解决方案:

  1. 创建API: 将你的机器学习模型部署为一个API服务。你可以使用像Flask这样的轻量级Web框架来创建一个简单的HTTP服务。这样,你的Qt应用程序可以通过HTTP请求来与这个服务交互,并获取模型的预测结果。部署完成后,你需要在Qt中使用QNetworkAccessManager或其他HTTP客户端来呼叫远端API。
  2. 使用Python和Qt的集成: 如果你希望避免网络请求,可以考虑在你的应用程序中直接使用Python。这可以通过使用像PyQt5或PyOtherSide这样的库实现。
  3. C++机器学习库: 如果模型不是特别复杂,你可以考虑使用C++机器学习库(例如Shark、dlib或mlpack)重新实现你的模型。这样可以避免使用Python,但可能需要重新训练模型并调整算法。
  4. 使用嵌入式Python解释器: 在你的Qt应用程序中嵌入or指定一个Python解释器。这允许你直接从C++代码中调用Python代码,并使用你的模型。
    	Py_SetPythonHome(L"D:\\Anaconda\\envs\\cp39_torch1_13_1_vision_0_14_cu117");
        Py_Initialize();
    

     通过嵌入式Python部署方法,目标机器(用户的机器)无需单独安装Python。这是因为所有必要的Python组件都应该被包含在你的应用程序中,作为该应用程序的一部分进行分发。这意味着Python解释器和所有必要的库、模块及其他依赖都被静态链接到应用程序或以其他形式捆绑在一起,用户不需要执行额外的安装步骤。

     但是,这种方法也意味着你的Qt应用程序的部署包会更大,因为它可能需要包括Python解释器和额外的Python库。

  5. 将模型转换为ONNX: 如果你的机器学习模型支持导出为ONNX(Open Neural Network Exchange)格式,你可以将其转换为ONNX,然后在C++应用程序中使用支持ONNX的机器学习库(例如ONNX Runtime)来加载和运行模型。
  6. 使用远程服务器处理: 如果客户端应用程序的性能和网络延迟不是问题,你可以在远程服务器上托管你的Python模型,并通过网络请求进行交互。
    每个选项都有其优缺点,你应该根据你的具体需求来决定使用哪一种。比如,第一个选项可能是最简单的开始方式,而第三个和第五个选项可能提供了最好的性能。最后选择哪种方法,取决于你的应用需求、性能考虑以及你对于不同技术的熟悉程度。

最近和之前在做的项目都是在Qt中用c++编码项目的开发,其中的功能涉及到机器学习的算法,之前有尝试过直接调用python,但是一直不成功, 再加上想到直接打包python程序也会更大,遂放弃。前面几篇博客讲到把Yolov8的目标检测和分割模型部署成c++的,是因为当时采用了上述的第五类方法,直接将算法由python转换为c++嵌入到程序中了,但是实际中这样虽然性能最好,但是很麻烦。这次的项目涉及到多种检测算法,且算法需要随着实验进行实时迭代,不可能说将python转换成c++了(这样十分麻烦,涉及到重新编写…配置环境…),于是这次打算再次采用第四种方法,在Qt项目中直接使用c++来调用python。为什么不适用创建API的方法呢?主要还是因为数据涉及到不能通过网络传输。总之,让我们快速进入正题,如何使用Qt调用python把!

一、Qt调用python

如果是vs2019,可以看看前面讲解在vs2019下部署yolov8中的配置依赖环境,都是一样的,这里因为换成你了Qtcreator,那就以qtcreator来讲:

注意,看一下自己的项目环境是否是64位,否则后面会报错:
在这里插入图片描述

1.1:pro文件增加python目录

INCLUDEPATH += -I D:\software\anaconda3\envs\yolov8\include
LIBS += -LD:\software\anaconda3\envs\yolov8\libs -lpython39

在这里插入图片描述

1.2:将Python集成到Qt中

工具->选项->环境->外部工具,添加->添加目录 (双击可任意更改名称这里更改为RunPy)->添加工具(双击可任意更改名称这里更改为Python3)。点击Python3,配置执行档、参数等配置:

  • 执行档: python的安装目录,我这里(D:\software\anaconda3\envs\yolov8\python.exe),你自己找到自己安装的python.exe目录
  • 参数:%{CurrentDocument:FilePath}
  • 工作目录:%{CurrentDocument:Path}
    在这里插入图片描述

1.3:将相关的文件拷贝到项目的可执行目录

  • 所需拷贝文件:python相关的Dll文件、以及对应的你想要调用的py文件

在这里插入图片描述

🌸在Qt中创建一个Python脚本测试一下:

  • 创建python脚本(或者前面已经将Python脚本移动到可执行目录下,这个时候在Other Files中也能看到)
    在这里插入图片描述

这里我新建一个测试脚本test.py:

import matplotlib.pyplot as plt

def temperImg():
    plt.plot([1, 2, 1, 2])
    plt.show()

temperImg()
  • 选中文件->点击 工具->外部->RunPy->Python3,运行脚本
    在这里插入图片描述
    OK可以看到可以成功调用python:
    在这里插入图片描述

1.4:添加环境变量

在这里插入图片描述

1.5:修改include文件夹中的object.h文件

修改include文件夹中的object.h文件,因为Python中slots是关键字,Qt中slots也是关键字,会冲突。
编译报错error: expected unqualified-id before ‘;’ token,这是与qt的slots关键字冲突,解决办法是将python中的slot取消宏定义,然后再恢复,如下图
在这里插入图片描述

#undef slots

PyType_Slot *slots; /* terminated by slot==0. */

#define slots Q_SLOTS

1.6:C++程序调用

具体流程大概包括以下几个步骤:

  1. 设置Python环境:
    使用 Py_SetPythonHome() 指定Python环境路径。这个路径通常指向你希望使用的Python解释器的环境。

  2. 调用 Py_Initialize() 来初始化Python解释器。
    检查Python解释器是否初始化成功:

  3. 使用 Py_IsInitialized() 验证Python解释器是否已经成功初始化,如果没有,则输出相应的错误日志。

  4. 修改Python搜索路径:
    通过 sys.path.append(‘./’) 来修改Python模块搜索路径。这样做通常是为了确保Python能够找到你的脚本所在的目录。

  5. 导入Python模块:
    使用 PyImport_ImportModule(“testTunnel”) 来导入名为testTunnel的Python模块。

  6. 获取模块中函数的指针:
    获得模块中名为judge_image的函数指针,以便能够调用该函数。

  7. 准备函数参数:
    创建一个Python元组(PyTuple_New(1)),用以传递参数给Python函数。
    将C++字符串(在这里是一个文件夹路径)转换为Python字符串,并通过 PyTuple_SetItem() 设置到参数元组中。

  8. 调用Python函数:
    使用 PyEval_CallObject(pTest_1,pPara) 来调用Python函数,并传递参数元组pPara。这将执行Python脚本中的judge_image函数。

  9. 处理Python函数的返回值:
    PyEval_CallObject() 返回的PyObject* pyValue是Python函数的返回值。需要根据实际情况处理这个返回值。
    错误处理:
    如果在任何步骤中遇到错误,使用 PyErr_Print() 打印Python错误信息,并进行适当的错误处理。

  10. 清理资源:
    不要忘记在使用完Python对象后释放资源,比如使用 Py_DECREF() 减少引用计数,以及在最后使用 Py_Finalize() 终止Python解释器。

  // 获取选中项的文本,这里假设是文件夹的完整路径
    QString folderPath = item->text();
    QList<QFileInfo> noTargetList;
    
    
    Py_SetPythonHome(L"D:/software/anaconda3/envs/yolov8");
    Py_Initialize();
    if( !Py_IsInitialized() )
        qDebug()<<"图片加载模块的Python初始化失败";
    
    PyRun_SimpleString("import sys");
    PyRun_SimpleString("sys.path.append('./')");//这一步很重要,修改Python路径
    //创建模块指针
    PyObject* pModule = PyImport_ImportModule("testTunnel");
    if (!pModule)
        qDebug()<<"图片加载模块的Python获取模块指针失败";
    else {
        qDebug()<<"successfuly obtained Module!";
    }
    const char* filename = PyModule_GetFilename(pModule);
    if (filename == NULL) {
        PyErr_Print();
        Py_DECREF(pModule);
        Py_Finalize();
    }
    
    //创建函数指针
    PyObject* pTest_1 = PyObject_GetAttrString(pModule, "judge_image");
    if (!pTest_1) {
        // Failed to get function pointer
        PyErr_Print(); // Print Python error indicator if available
        qDebug() << "获取函数指针失败";
    } else {
        qDebug() << "Successfully obtained function pointer";
    }
    
    PyObject* pPara = PyTuple_New(1);
    if (pPara == NULL) {
        // 元组创建失败,进行错误处理
        PyErr_Print();
        // 继续下一个迭代
    }
    
    qDebug() << "fPath:"<<folderPath;
    const char* cstr = folderPath.toUtf8().constData();  // 将 QString 转换为 C 字符串
    
    PyTuple_SetItem(pPara, 0, Py_BuildValue("s",cstr));  //参数1为String型 "Hello"
    
    
    PyObject* pyValue = PyEval_CallObject(pTest_1,pPara);
    PyErr_Print();
    qDebug() << "pyValue"<<pyValue;
    
    ...
      // 在使用完 pModule 后减少其引用计数
    Py_DECREF(pModule);
    // 在使用完列表后,减少其引用计数
    Py_DECREF(pyValue);
    // 在使用完参数元组后,减少其引用计数
    Py_DECREF(pPara);
    
    //    Py_Finalize();

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/572927.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Linux---Socket

网络套接字(socket) 网络通信仅仅是为了让两台主机间传送数据吗&#xff1f;数据是被谁需要的呢&#xff1f;--- 进程&#xff0c;所以网络通信的本质是两个进程间的通信。那么如何找到两台主机上的两个进程呢&#xff1f; 1、通过IP地址确定网络中的唯一一台主机 2、通过po…

Linux关闭swap分区操作[适用于CDH报警等]

1.查看swap分区挂载路径(没卵用) swapon -s 2.设置配置文件的swap配置 echo “vm.swappiness 0” > /etc/sysctl.conf 3.设置内存中的swap状态。有时候配置文件为0&#xff0c;但集群或服务仍然使用了swap分区&#xff0c;可能原因就是内存没有同步配置 echo “0” > …

C++入门----内联函数auto范围fornullptr指针

1.内联函数 顾名思义&#xff0c;内联函数也是函数的一种&#xff0c;我们在C语言的学习过程里面知道了函数和宏之间的区别和各自的优缺点&#xff1b; 函数的使用需要建立栈帧&#xff0c;宏的使用需要考虑各种符号的优先级问题&#xff0c;很容易出错&#xff0c;因为宏在使…

jmeter之跨线程关联

1&#xff09;_setproperty函数&#xff1a;将值保存成jmeter属性 2&#xff09;_property函数&#xff1a;在其他线程组中使用property函数读取属性 一、跨线程接口引用变量 1. 法一&#xff1a;jmeter自带函数_setProperty和_property 1. 1线程组 01 创建登录的【HTTP请求】…

Java知识总结---并发篇

线程 线程的状态 Java中线程可以有如下6中状态&#xff1a; NEW 新创建 RUNNABLE 可运行 BLOCKED 阻塞 WAITING 等待 TIMED WAITING 计时等待 TERMINATED 终止 线程的创建 &#xff08;1&#xff09;继承Thread类 public class ExtendsThread extends Thread { O…

后端工程师——C++工程师招聘需求

相比 Java 语言方向&#xff0c;C 入门简单&#xff0c;精通难&#xff0c;找工作竞争压力更小&#xff0c;但 C 依然是近年来招聘的热门岗位之一。本文将从以下三个方面进行详细讲解&#xff0c;帮助你对 C 相关岗位的就业前景、岗位要求、学习路线等有更充分的了解。 C工程师…

48-70V降12V/33V 5A高效同步降压DC-DC——AH1007

AH1007是一款高效率、高压外置MOSFET管降压芯片TEL&#xff1a;186*4884*3702*&#xff0c;芯片典型输入是8V~100V,输出 电压可调&#xff0c;AH1007最大输出电流可支持6A以上&#xff0c;需要注意板子的散热和温升。 AH1007典型开关频率为150KHz。轻载时会自动降低开关频率以…

SSRF—服务器请求伪造 漏洞详解

漏洞简述 SSRF(Server-Side Request Forgery:服务器端请求伪造) 是一种由攻击者构造&#xff0c;由服务端发起请求的一个网络攻击&#xff0c;一般用来在外网探测或攻击内网服务&#xff0c;其影响效果根据服务器用的函数不同&#xff0c;从而造成不同的影响。 SSRF 形成的原因…

H5点击复制功能 兼容安卓、IOS

效果图 HTML代码 <div>链接&#xff1a;<span style"color: #FF8A21" click"CopyUrl" id"copyId"> https://blog.csdn.net/qq_51463650?spm1000.2115.3001.5343</span> </div>复制方法 const CopyUrl () > {let …

水库大坝安全白蚁监测系统解决方案

一、系统背景 白蚁作为河岸生态系统中的重要病害&#xff0c;不仅会导致水库大坝外部环境发生改变&#xff0c;甚至会引发水库大坝破坏&#xff0c;进而导致自身结构失去稳定性&#xff0c;严重影响水库大坝的正常运行。因此&#xff0c;治理水库大坝白蚁是确保水库大坝工程顺利…

chrome 浏览器 f12 如何查看 websocket 消息?

1. 打开目标页面 2. f12--》网络--》WS&#xff0c;然后刷新页面( 如果不刷页面&#xff0c;就会看不到 websocket 请求&#xff0c;因为 websocket 是长连接&#xff0c;页面加载后只发出一次连接请求&#xff0c;不像 http 接口&#xff0c;不用刷新页面&#xff0c;待会儿也…

Linux Docker下载镜像更改默认存储位置/usr/lib/docker

用于解决docker默认存储位置磁盘空间不足&#xff0c;切换存储位置 1、执行下面命令查看 现在docker的存储位置 docker info | grep "Docker Root Dir" 1.2、如果之前已经下载过镜像可以用mv命令把原来的镜像复制到新的地址 mv /var/lib/docker /data/docker 2、…

十堰着力建设城市级供应链安全检测平台,全力打造数字政府安全示范区

4月23日&#xff0c;十堰市互联网信息办公室、十堰政务服务和大数据管理局、十堰数安技术有限公司共同签订了《城市供应链安全检测平台战略合作框架协议》&#xff0c;通过政企深度协作&#xff0c;加强十堰市数字安全体系建设&#xff0c;为推动十堰市乃至湖北省数字经济产业发…

RS1G08XF5规格详情

RS1G08XF5 是一款由润石&#xff08;RUNIC&#xff09;公司生产的电子元器件。根据所提供的信息&#xff0c;这是一款SOT-23-5封装的器件&#xff0c;其主要参数包括最小电源电压为5V&#xff0c;最大电源电压为6V&#xff0c;最小工作温度为-40C。 为了更准确地了解这款器件的…

【开源】I.Mx6uLL的QT项目合集

文章目录 前言仓库11.下载仓库代码2.使用qmake交叉编译3.效果展示 前言 不定期的更新基于I.Mx6uLL的arm架构下的QT项目合集&#xff1a; 项目移植前的准备 【环境安装】 仓库1 1.下载仓库代码 https://github.com/cocowts/QtCustomcomponent git clone https://github.com/…

ssm框架的网上招聘系统的设计与实现,ssm框架,java编程,mysql数据库 myeclipse开发平台

网上招聘是一个功能很复杂的系统&#xff0c;各个部门之间要有一定的协调能力。 要建立一个高效的管理系统的关键问题就是系统内部的各个模块的相互作用&#xff0c;简单的编写一个网站 只用html &#xff0c;css &#xff0c;javascript &#xff0c;xml &#xff0c;xsl技术…

XUbuntu18.04 源码编译Qt4.5.3的过程

由于新公司很多旧的软件都是基于这个版本做的嵌入式开发。 所以想要自己搭一套基于Linux的非嵌入式开发环境&#xff0c;方便用来调试和编译代码。 这样就可以完成在linux下开发&#xff0c;然后直接嵌入式打包&#xff0c;涉及到界面的部分就不需要上机调试看问题了。 所以…

java 当中的流

初识io 代码演示 private final static String URL "E:\\";private final static String READURL "H:\\kj\\202402\\LS0205.00P";Testpublic void testOutputStream() {long start System.currentTimeMillis();try (InputStream in new FileInputStrea…

RC电路延时时间常数在线计算器

RC电路延时时间常数在线计算器: https://www.838dz.com/calculator/1888.html 急用时&#xff0c;找不到。

古籍数字化平台中的OCR:这个平台更精准

在浩瀚的历史长河中&#xff0c;古籍作为中华民族的文化瑰宝&#xff0c;承载着无数先人的智慧与心血。然而&#xff0c;由于岁月侵蚀、保存不当等多种原因&#xff0c;许多珍贵的古籍面临损坏、失传的危机。为了守护这些无价之宝&#xff0c;云聪研发团队倾力打造了一款尖端的…