DAMOYOLO-S快速原型开发:使用Qt构建跨平台桌面检测工具

张开发
2026/4/10 10:22:07 15 分钟阅读

分享文章

DAMOYOLO-S快速原型开发:使用Qt构建跨平台桌面检测工具
DAMOYOLO-S快速原型开发使用Qt构建跨平台桌面检测工具最近在做一个计算机视觉的小项目需要把训练好的DAMOYOLO-S模型打包成一个能直接给同事用的桌面工具。一开始想着用Python脚本凑合一下但每次都要开命令行、传参数对非技术同事来说太不友好了。后来决定用Qt来做个图形界面没想到效果出奇的好一套代码就能在Windows、macOS和Linux上跑起来开发效率也高。今天就来分享一下这个经历聊聊怎么用Qt快速搭一个功能完善的桌面检测工具。这个工具能加载图片和视频用DAMOYOLO-S模型做实时检测把结果标出来还能导出检测报告。整个过程涉及UI设计、多线程处理、信号槽通信这些Qt的核心玩法我会尽量用大白话讲清楚让你也能快速上手。1. 为什么选择Qt来做这个工具做桌面应用框架选择挺多的。PyQt/PySideQt的Python绑定能让我继续用熟悉的Python和PyTorch生态这是最大的吸引力。DAMOYOLO-S模型本身是用PyTorch写的用PyQt/PySide就能无缝集成不用折腾语言转换。更重要的是Qt的跨平台特性。写一次代码稍微调整下界面就能编译成各个系统的原生应用。对于我这种需要给用不同操作系统的同事分发工具的人来说简直太省事了。Qt的UI设计工具Qt Designer也很直观拖拖拽拽就能把界面搭个大概再写代码把逻辑连起来就行。最后Qt的信号槽机制处理起来特别顺手。比如检测是个耗时的活儿不能卡住界面。用Qt的多线程把检测任务扔到后台做完之后发个信号通知界面更新整个过程很清晰不容易写出bug。2. 工具长什么样需要哪些功能动手之前先想清楚工具要做成什么样有什么功能。我画了个简单的草图核心界面大概分这么几个区菜单栏和工具栏放一些全局操作比如打开文件、开始/停止检测、导出结果、调整设置。主显示区这块最大用来显示原始图片、视频流以及检测后画上了框框和标签的结果。控制面板放在侧面或下面用来调整模型参数比如置信度阈值、NMS阈值、选择检测的类别、控制视频播放暂停、下一帧。结果列表显示当前画面里检测到了哪些东西包括类别、置信度、坐标可以点选查看详情。状态栏显示点实时信息比如当前文件路径、检测状态、帧率FPS这些。功能上我希望它能搞定这几件事文件支持能打开常见的图片格式jpg, png等和视频格式mp4, avi等。实时检测图片点一下就能出结果视频能逐帧或实时检测。结果可视化检测到的物体要能用不同颜色的框标出来旁边写上类别和分数。交互操作最好能支持在图片上框选区域进行局部检测或者点选结果列表里的项能在图上高亮对应的框。结果导出能把带标注的图片保存下来或者生成一个包含所有检测结果的文本报告比如JSON或TXT格式。3. 动手搭建从界面到代码想清楚之后就可以打开Qt Designer开始拖控件了。我用的是PySide6过程都差不多。3.1 用Qt Designer摆好界面在Qt Designer里主要用到了这些控件QMainWindow作为主窗口。QGraphicsView和QGraphicsScene用来显示和操作图片这是实现缩放、平移等功能的基础。QLabel、QPushButton、QComboBox、QSpinBox、QSlider这些是按钮、下拉框、数字框、滑块用来做控制面板。QListWidget或QTableWidget用来展示检测结果列表。QStatusBar状态栏。QVBoxLayout、QHBoxLayout垂直和水平的布局管理器用来把控件整齐地排列起来。摆的时候记得用好布局管理器这样窗口大小变化时控件能自适应不会乱掉。设计完保存成一个.ui文件。3.2 把UI文件变成Python代码Qt Designer生成的.ui文件是XML格式的需要转换成Python代码才能用。用PySide6自带的工具pyside6-uic就能搞定pyside6-uic mainwindow.ui -o ui_mainwindow.py这个命令会生成一个ui_mainwindow.py文件里面定义了界面的类。在我们的主程序里可以继承这个类或者动态加载它。我更喜欢动态加载的方式比较灵活# main.py import sys from PySide6.QtWidgets import QApplication, QMainWindow from PySide6.QtCore import QFile from PySide6.QtUiTools import QUiLoader class DetectionApp(QMainWindow): def __init__(self): super().__init__() # 动态加载UI文件 ui_file QFile(mainwindow.ui) ui_file.open(QFile.ReadOnly) loader QUiLoader() self.ui loader.load(ui_file) ui_file.close() self.setCentralWidget(self.ui) self.setWindowTitle(DAMOYOLO-S 检测工具) # 接下来在这里连接信号槽初始化其他部件 self._setup_connections() def _setup_connections(self): 连接按钮点击等信号到对应的槽函数 self.ui.btn_open_image.clicked.connect(self.open_image) self.ui.btn_start_detection.clicked.connect(self.start_detection) # ... 连接其他信号 if __name__ __main__: app QApplication(sys.argv) window DetectionApp() window.show() sys.exit(app.exec())3.3 让检测任务在后台跑起来不能让检测模型卡住界面。这里要用到Qt的QThread。我们创建一个专门的工作线程来跑检测。# detector_thread.py from PySide6.QtCore import QThread, Signal import cv2 import torch from damoyolo import DAMOYOLO_S # 假设这是你的模型加载方式 class DetectorThread(QThread): # 定义信号用于向主线程传递结果 detection_finished Signal(object, object) # 发送结果和原始图像/帧 error_occurred Signal(str) def __init__(self): super().__init__() self.model None self.current_frame None self.conf_threshold 0.5 self.iou_threshold 0.45 self._load_model() def _load_model(self): 加载DAMOYOLO-S模型 try: # 这里替换成你实际的模型加载代码 self.model DAMOYOLO_S(pretrainedTrue).eval() if torch.cuda.is_available(): self.model.cuda() print(模型加载成功) except Exception as e: self.error_occurred.emit(f模型加载失败: {e}) def set_frame(self, frame): 设置要检测的帧图片或视频帧 self.current_frame frame def set_thresholds(self, conf, iou): 设置检测阈值 self.conf_threshold conf self.iou_threshold iou def run(self): 线程运行的主函数 if self.current_frame is None or self.model is None: self.error_occurred.emit(未设置检测帧或模型未加载) return try: # 预处理图像 input_tensor self._preprocess(self.current_frame) # 执行推理 with torch.no_grad(): predictions self.model(input_tensor) # 后处理得到框、分数、类别 boxes, scores, class_ids self._postprocess(predictions) # 发射信号传递结果 self.detection_finished.emit((boxes, scores, class_ids), self.current_frame) except Exception as e: self.error_occurred.emit(f检测过程中出错: {e}) def _preprocess(self, frame): 将OpenCV图像转换为模型需要的张量 # 这里需要根据DAMOYOLO-S的具体要求实现 # 例如调整大小、归一化、转换通道顺序 (HWC to CHW) 等 # 返回一个torch.Tensor pass def _postprocess(self, predictions): 将模型输出解析为框、分数、类别ID # 这里需要根据DAMOYOLO-S的输出格式实现 # 通常包括非极大值抑制 (NMS) 等步骤 # 返回 boxes (xyxy格式), scores, class_ids pass在主窗口类里我们创建这个工作线程并连接它的信号# 在DetectionApp类的__init__或_setup_connections方法中 self.detector_thread DetectorThread() self.detector_thread.detection_finished.connect(self.on_detection_finished) self.detector_thread.error_occurred.connect(self.on_detection_error) def start_detection(self): if self.current_image is not None: # 将当前图像传给线程 self.detector_thread.set_frame(self.current_image.copy()) # 更新阈值如果用户调整了 conf self.ui.slider_confidence.value() / 100.0 iou self.ui.slider_iou.value() / 100.0 self.detector_thread.set_thresholds(conf, iou) # 禁用按钮防止重复点击 self.ui.btn_start_detection.setEnabled(False) self.ui.statusbar.showMessage(检测中...) # 启动线程 self.detector_thread.start() def on_detection_finished(self, results, original_frame): boxes, scores, class_ids results # 在主线程中更新UI显示检测结果 self.display_detection_results(original_frame, boxes, scores, class_ids) self.ui.btn_start_detection.setEnabled(True) self.ui.statusbar.showMessage(检测完成) def on_detection_error(self, error_msg): # 在主线程中显示错误信息 self.ui.statusbar.showMessage(f错误: {error_msg}, 5000) # 显示5秒 self.ui.btn_start_detection.setEnabled(True)3.4 在界面上把结果画出来检测结果回来了我们要在QGraphicsView里把框和标签画上去。可以用QGraphicsPixmapItem显示图片用QGraphicsRectItem画框用QGraphicsTextItem写标签。def display_detection_results(self, frame, boxes, scores, class_ids): 在GraphicsView上绘制检测结果 # 1. 将OpenCV图像 (BGR) 转换为Qt图像 (RGB) height, width, channel frame.shape bytes_per_line 3 * width qt_image QImage(frame.data, width, height, bytes_per_line, QImage.Format_RGB888).rgbSwapped() # 2. 创建QPixmap并清空场景 pixmap QPixmap.fromImage(qt_image) self.graphics_scene.clear() # graphics_scene是QGraphicsScene实例 # 3. 添加背景图片 pixmap_item self.graphics_scene.addPixmap(pixmap) self.graphics_scene.setSceneRect(QRectF(pixmap.rect())) # 4. 绘制每个检测框和标签 for box, score, cls_id in zip(boxes, scores, class_ids): x1, y1, x2, y2 box class_name self.class_names[cls_id] # 假设有类别名称列表 # 画矩形框 rect_item QGraphicsRectItem(x1, y1, x2-x1, y2-y1) rect_item.setPen(QPen(QColor(0, 255, 0), 2)) # 绿色边框宽度2 self.graphics_scene.addItem(rect_item) # 画文本标签类别置信度 label f{class_name}: {score:.2f} text_item QGraphicsTextItem(label) text_item.setDefaultTextColor(QColor(255, 255, 0)) # 黄色文字 text_item.setPos(x1, y1 - 20) # 放在框的上方 # 给文本加个半透明背景看得更清楚 text_item.setFont(QFont(Arial, 10)) self.graphics_scene.addItem(text_item) # 5. 更新结果列表 self.update_result_list(boxes, scores, class_ids)3.5 保存和导出功能最后实现一下导出功能。保存图片很简单用QPixmap.save()就行。导出报告比如JSON格式可以遍历结果列表来生成。def export_results(self): 导出检测结果到JSON文件 if not self.current_results: # 假设current_results保存了最近一次的结果 return file_path, _ QFileDialog.getSaveFileName(self, 保存检测报告, , JSON Files (*.json)) if file_path: export_data { image_path: self.current_image_path, detections: [] } for box, score, cls_id in zip(self.current_results[boxes], self.current_results[scores], self.current_results[class_ids]): detection { bbox: box.tolist() if hasattr(box, tolist) else list(box), score: float(score), class_id: int(cls_id), class_name: self.class_names[int(cls_id)] } export_data[detections].append(detection) import json with open(file_path, w, encodingutf-8) as f: json.dump(export_data, f, indent4, ensure_asciiFalse) self.ui.statusbar.showMessage(f报告已保存至: {file_path})4. 实际用起来怎么样把这个工具给几个同事试用了一下反馈还不错。对于非技术同事打开软件、点几下按钮就能跑检测、看结果、导出报告比用命令行或者看Jupyter Notebook直观多了。对于开发者的我来说用Qt做界面比用Tkinter或者纯Web前端比如FlaskHTML感觉更顺手尤其是处理实时视频流和复杂的交互时Qt的响应速度和稳定性都很好。当然也遇到些小问题比如不同操作系统上字体渲染有点差异打包成独立可执行文件用PyInstaller的时候要小心处理动态库。但总的来说Qt这套组合拳——直观的UI设计、清晰的信号槽机制、稳定的跨平台能力——让它成为快速开发这类AI模型桌面工具的一个非常棒的选择。获取更多AI镜像想探索更多AI镜像和应用场景访问 CSDN星图镜像广场提供丰富的预置镜像覆盖大模型推理、图像生成、视频生成、模型微调等多个领域支持一键部署。

更多文章