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

news2025/1/16 20:17:51

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.coloradmin.cn/o/1622994.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

如何在一台服务器上同时运行搭载JDK 8, JDK 17, 和 JDK 21的项目:终极指南

&#x1f42f; 如何在一台服务器上同时运行搭载JDK 8, JDK 17, 和 JDK 21的项目&#xff1a;终极指南 &#x1f680; 摘要 在企业开发环境中&#xff0c;常常需要在同一台服务器上运行使用不同Java开发工具包&#xff08;JDK&#xff09;版本的多个项目。本文详细介绍如何在L…

华为鸿蒙应用--封装通用标题栏:CommonTitleBar(鸿蒙工具)-ArkTs

0、效果图 自定义通用标题栏 支持左、中、右常规标题栏设置&#xff1b; 支持自定义视图&#xff1b; 支持搜索功能 一、CommTitleBar代码 import router from ohos.router; import { Constants } from ../../constants/Constants; import { StyleConstants } from ../../…

JavaEE 初阶篇-深入了解 UDP 通信与 TCP 通信(综合案例:实现 TCP 通信群聊)

&#x1f525;博客主页&#xff1a; 【小扳_-CSDN博客】 ❤感谢大家点赞&#x1f44d;收藏⭐评论✍ 文章目录 1.0 UDP 通信 1.1 DatagramSocket 类 1.2 DatagramPacket 类 1.3 实现 UDP 通信&#xff08;一发一收&#xff09; 1.3.1 客户端的开发 1.3.2 服务端的开发 1.4 实现 …

FTP与SMB深度对比:文件传输协议谁更胜一筹?

在数字化时代&#xff0c;文件传输已成为日常工作中不可或缺的一部分。 FTP&#xff08;文件传输协议&#xff09;和SMB&#xff08;服务器消息块&#xff09;是两种最为常见的文件传输协议。它们各自在文件传输领域拥有独特的优势和特点&#xff0c;但同时也存在一些差异。 今…

六个月滴滴实习:轻松、舒心又高薪!

不久前&#xff0c;一位在滴滴后端研发部门实习了六个月的小伙伴在牛客网上分享了他的实习体验&#xff0c; 作者详细描述了他在滴滴的实习生活。 从他的叙述中&#xff0c;我们可以感受到与其他互联网公司相比&#xff0c;滴滴的工作环境显得相对轻松和舒适。 他提到&#x…

【汇编语言】流程转移和子程序

【汇编语言】流程转移和子程序 文章目录 【汇编语言】流程转移和子程序前言一、“转移”综述二、操作符offset三、jmp指令jmp指令——无条件转移jmp指令&#xff1a;依据位移进行转移两种段内转移远转移&#xff1a;jmp far ptr 标号转移地址在寄存器中的jmp指令转移地址在内存…

神经网络进阶

⚠申明&#xff1a; 未经许可&#xff0c;禁止以任何形式转载&#xff0c;若要引用&#xff0c;请标注链接地址。 全文共计3077字&#xff0c;阅读大概需要3分钟 &#x1f308;更多学习内容&#xff0c; 欢迎&#x1f44f;关注&#x1f440;【文末】我的个人微信公众号&#xf…

socket套接字在tcp客户端与tcp服务器之间的通信,以及socket中常用的高效工具epoll

1.socket&#xff08;套接字&#xff09;的概念 Socket是对TCP/IP协议的封装&#xff0c;Socket本身并不是协议&#xff0c;而是一个调用接口&#xff08;API&#xff09;&#xff0c;通过Socket&#xff0c;我们才能使用TCP/IP协议,主要利用三元组【ip地址&#xff0c;协议&am…

10G网络布线:DAC线缆与AOC光缆的选择指南

在10G网络部署中&#xff0c;选择合适的传输介质是确保网络性能和可靠性的关键。本文将全面比较10G DAC高速线缆和10G AOC有源光缆&#xff0c;帮助您做出明智的选择。 10G DAC高速线缆 VS 10G AOC有源光缆 定义与构造 10G DAC高速线缆&#xff08;Direct Attach Cable&…

配置有效的防爬虫技术保护网站

本文主要介绍了防爬虫的概念、目的以及一些有效的防爬虫手段。防爬虫是指网站采取各种技术手段阻止爬虫程序对其数据进行抓取的过程。为了保护网站的数据和内容的安全性&#xff0c;防止经济损失和恶意竞争&#xff0c;以及减轻服务器负载&#xff0c;网站需要采取防爬虫机制。…

文心一言4.0、智谱清言、MoonshotAI实测对比(上)

前言 前两天看到这张图&#xff0c;又刚好拿到了文心一言的4.0内测号&#xff0c;就想着把新版国内御三家横向对比测评一下。 文末领取免费领取AI学习基地 AI交流群 前一段时间也一直在研究复杂提示词&#xff08;结构化提示词&#xff09;向国内大模型迁移适配的问题&#…

matlab新手快速上手5(蚁群算法)

本文根据一个较为简单的蚁群算法框架详细分析蚁群算法的实现过程&#xff0c;对matlab新手友好&#xff0c;源码在文末给出。 蚁群算法简介&#xff1a; 蚁群算法是一种启发式优化算法&#xff0c;灵感来源于观察蚂蚁寻找食物的行为。在这个算法中&#xff0c;解决方案被看作是…

Fhopify:跨境电商行业迎来发展新机遇打造购物者天堂

随着全球化的推进和互联网的普及,跨境电商作为一种新型的商业模式,日益受到人们的关注和重视。它通过互联网平台,打破了地域限制,使得消费者可以便捷地购买来自世界各地的商品,同时也为企业提供了更广阔的市场和发展空间。在这样的背景下,跨境电商的新趋势和未来发展备受关注。…

ShardingSphere 5.x 系列【24】集成 Nacos 配置中心

有道无术,术尚可求,有术无道,止于术。 本系列Spring Boot 版本 3.1.0 本系列ShardingSphere 版本 5.4.0 源码地址:https://gitee.com/pearl-organization/study-sharding-sphere-demo 文章目录 1. 前言2. ShardingSphereDriverURLProvider3. 方式一:基于 Nacos Java SDK…

初学python记录:力扣2739. 总行驶距离

题目&#xff1a; 卡车有两个油箱。给你两个整数&#xff0c;mainTank 表示主油箱中的燃料&#xff08;以升为单位&#xff09;&#xff0c;additionalTank 表示副油箱中的燃料&#xff08;以升为单位&#xff09;。 该卡车每耗费 1 升燃料都可以行驶 10 km。每当主油箱使用了…

演示在一台Windows主机上运行两个Mysql服务器(端口号3306 和 3307),安装步骤详解

目录 在一台Windows主机上运行两个Mysql服务器&#xff0c;安装步骤详解因为演示需要两个 MySQL 服务器终端&#xff0c;我只有一个 3306 端口号的 MySQL 服务器&#xff0c;所以需要再创建一个 3307 的。创建一个3307端口号的MySQL服务器1、复制 mysql 的安装目录2、修改my.in…

基于springboot实现海滨学院班级回忆录的设计项目【项目源码+论文说明】计算机毕业设计

基于springboot实现海滨学院班级回忆录的设计演示 摘要 随着信息技术在管理上越来越深入而广泛的应用&#xff0c;管理信息系统的实施在技术上已逐步成熟。本文介绍了海滨学院班级回忆录的开发全过程。通过分析海滨学院班级回忆录管理的不足&#xff0c;创建了一个计算机管理海…

Andorid进程间通信之 UNIX SOCKET

1&#xff0c;什么是UNIX SOCKET UNIX SOCKET&#xff0c;域套接字&#xff0c;UNIX SOCKET可用于同一台设备进程间通信&#xff0c;它不需要经过网络协议栈&#xff0c;不需要打包拆包、计算校验和、维护序列号应答等&#xff0c;只需要将数据从一个进程复制到另一个进程&…

xray使用入门

简介 长亭科技旗下的一款网络安全漏洞扫描工具&#xff0c;用于检测和评估web应用程序的安全性。具有一下特点&#xff1a;检测速读快、检查范围广、代码质量高、高级可定制以及安全无危害。属于不开源的项目&#xff0c;用户直接下载xray的可执行文件&#xff0c;即可运行该工…

SSH远程连接docker容器-Linux-SSH -L 打隧道

问题&#xff1a;在物理机上用podman创建了一个容器&#xff0c;想SSH直接远程连接docker容器 解决方式&#xff1a; 步骤1: 在本地terminal输入以下命令&#xff1a; ssh -L 容器端口号:localhost:容器端口号 物理机用户名物理机ip -p 物理机端口号 即可&#xff0c;可新打…