为了更便捷地使用YOLOv12;YOLOv11;YOLOv10;YOLOv8等基于ultralytics的目标检测,尤其是对于没有深度编程经验的用户,一个可视化界面(GUI)显得尤为重要。为此,基于 PySide6 开发了一个最新的支持YOLOv12的可视化界面(GUI)(其他低版本YOLO也可用),该界面能够实现对 YOLOv11/YOLOv8模型的简单操作,包括模型选择、图片检测、视频检测、摄像头检测并进行结果展示等功能,且完全兼容官方源代码。单文件即插即用,仅300多行左右,无论是研究人员、工程师,还是学生或 AI 爱好者,都能通过这个工具更加直观和高效地进行模型应用和调试。
本文仅针对图像处理部分给出代码示例,只需要在根目录下新建一个main.py即可正常运行,需要完整功能可私聊或通过公众号购买成品。
改进的模型也能用,只需要把代码插入到改进的文件夹中即可。
本文的可视化UI界面对于Ultralytics(目前的YOLOv12 & YOLOv11 & YOLOv10 & YOLOv8通用)的检测、分割、分类、姿势估算(detection, segmentation, obb, classification, and pose estimation)等均可正常显示。
上个版本链接YOLOv11(Ultralytics)可视化界面GUI设计,基于pyside6,单文件即插即用 兼容官方源码,可打包成软件_yolov11 gui-CSDN博客
此次美化了界面外观布局,添加了检测ip摄像头功能,左侧增加检测信息输出。
使用之前需要配置环境,运行有问题应该先重新按照下文配置环境。
目标检测:YOLOv11(Ultralytics)环境配置,适合0基础纯小白,超详细_yolov11环境配置-CSDN博客文章浏览阅读5.6w次,点赞211次,收藏983次。深度学习目标检测YOLOv11小白教程环境搭建,超级简单的教程,纯新手方便理解,一看就会。_yolov11环境配置https://blog.csdn.net/qq_67105081/article/details/143270109?spm=1001.2014.3001.5502
YOLOv12则看这篇文章
目标检测:YOLOv12环境配置,超详细,适合0基础纯小白-CSDN博客文章浏览阅读1.2k次,点赞30次,收藏26次。小白也可以看懂的YOLOv12教程!YOLOv12 是 YOLO 系列中首个打破传统基于卷积神经网络(CNN)方法的模型,它通过将注意力机制直接集成到目标检测过程中实现了这一突破。因此YOLOv12需要额外配置FlashAttention,此前的YOLO环境均不可用,需要按照最新的教程配置。此外,30系显卡以前的架构不支持较新的FlashAttention2.x,只能通过更换显卡解决。https://blog.csdn.net/qq_67105081/article/details/146316615?spm=1001.2014.3001.5501基于上文环境之外,只需要额外多安装一个pyside6库即可。输入如下指令
pip install pyside6
主界面
检测效果
本文代码图片检测运行界面如下
检测效果
ip摄像头可通过这个文章使用手机测试
通过ip摄像头软件将手机变成云ip摄像头,并用Python读取视频流,可用于实时目标检测摄像头调用_ip摄像头lite-CSDN博客文章浏览阅读731次,点赞5次,收藏8次。通过将手机模拟成IP摄像头,我们可以实现将手机摄像头的画面通过网络传输,进而通过Python程序读取视频画面进行分析、处理等。本文将介绍如何通过一款局域网IP摄像头软件将手机模拟成云摄像头,并使用Python进行视频流的读取,同时运用目标检测算法进行实时物体识别。使用手机软件模拟ip摄像头相比购买专门的IP摄像头设备,成本大大降低。这里我们选择局域网的第一个MJPEG进行连接,可以将网址输入浏览器中,再输入账号密码admin进行查看,或者通过以下python代码在python上调用视频流画面,代码如下。_ip摄像头litehttps://blog.csdn.net/qq_67105081/article/details/143668267?spm=1001.2014.3001.5501
import cv2,sys
import numpy as np
from PySide6.QtGui import *
from PySide6.QtWidgets import *
from PySide6.QtCore import *
from ultralytics import YOLO
from collections import Counter
class GradientWidget(QWidget):
def paintEvent(self, event):
painter = QPainter(self)
gradient = QLinearGradient(0, 0, self.width(), self.height())
gradient.setColorAt(0, QColor(240, 248, 255))
gradient.setColorAt(1, QColor(230, 240, 250))
painter.fillRect(self.rect(), gradient)
class MainWindow(QMainWindow):
def __init__(self):
super().__init__()
self.xiaolian_ui()
self.model = None
self.result = None
self.con = 0.25
def xiaolian_ui(self):
self.setFixedSize(1280, 590)
self.setMinimumSize(1280, 590)
self.setMaximumSize(1280, 590)
self.setWindowTitle('@author:笑脸惹桃花')
self.setWindowIcon(QIcon("icon.png"))
# 主布局
main_widget = GradientWidget()
main_layout = QHBoxLayout(main_widget)
main_layout.setContentsMargins(15, 15, 15, 15)
main_layout.setSpacing(15)
# ===== 左侧控制面板 =====
left_panel = QWidget()
left_panel.setMaximumWidth(220)
left_layout = QVBoxLayout(left_panel)
left_layout.setContentsMargins(5, 5, 5, 5)
left_layout.setSpacing(8)
# Logo
logo = QLabel("""
YOLOv12 Detection""")
left_layout.addWidget(logo)
# 操作按钮
btn_group = QGroupBox("操作面板")
btn_group.setStyleSheet("""
QGroupBox {
border: 1px solid #95a5a6;
border-radius: 4px;
margin-top: 6px;
}
QGroupBox::title {
subcontrol-origin: margin;
left: 6px;
color: #34495e;
font: bold 11px;
}
""")
btn_layout = QVBoxLayout()
buttons = [
("加载模型", self.load_model),
("图片检测", self.select_image),
("停止", self.stop_detect)
]
for text, slot in buttons:
btn = QPushButton(text)
btn.setFixedHeight(32)
btn.setStyleSheet(f"""
QPushButton {{
font: 12px 'Microsoft YaHei';
background: '#f8f9fa' ;
border: 1px solid #ced4da;
border-radius: 3px;
padding: 4px;
}}
QPushButton:hover {{ background: #e9ecef; }}
""")
btn.clicked.connect(slot)
btn_layout.addWidget(btn)
btn_group.setLayout(btn_layout)
left_layout.addWidget(btn_group)
# 修改状态指示部分为信息输出框
status_group = QGroupBox("检测信息")
status_group.setStyleSheet(
"QGroupBox { border: 1px solid gray; border-radius: 5px; margin-top: 1ex; } QGroupBox::title { subcontrol-origin: margin; subcontrol-position: top left; padding: 0 3px; }")
status_layout = QVBoxLayout()
self.output_text = QTextEdit(self)
self.output_text.setReadOnly(True)
status_layout.addWidget(self.output_text)
status_group.setLayout(status_layout)
left_layout.addWidget(status_group)
left_layout.addStretch()
# 退出按钮
exit_btn = QPushButton("退出系统")
exit_btn.setFixedHeight(30)
exit_btn.setStyleSheet("""
QPushButton {
background: #e74c3c;
color: white;
font: bold 12px 'Microsoft YaHei';
border-radius: 3px;
padding: 4px;
}
QPushButton:hover { background: #c0392b; }
""")
exit_btn.clicked.connect(self.close)
left_layout.addWidget(exit_btn)
# ===== 右侧显示区域 =====
right_panel = QWidget()
right_layout = QHBoxLayout(right_panel)
right_layout.setContentsMargins(0, 0, 0, 0)
right_layout.setSpacing(12)
def create_display(title):
box = QWidget()
box.setStyleSheet("""
background: white;
border: 1px solid #bdc3c7;
border-radius: 5px;
""")
layout = QVBoxLayout(box)
layout.setContentsMargins(0, 0, 0, 0)
# 标题栏
title_bar = QLabel(title)
title_bar.setStyleSheet("""
background: #f8f9fa;
color: #2c3e50;
font: bold 30px 'Microsoft YaHei';
padding: 6px;
border-bottom: 1px solid #ced4da;
""")
title_bar.setAlignment(Qt.AlignCenter)
layout.addWidget(title_bar)
# 图像显示
img_label = QLabel()
img_label.setAlignment(Qt.AlignCenter)
img_label.setMinimumSize(500, 500)
img_label.setMaximumSize(500, 500)
img_label.setStyleSheet("background: #2c3e50;")
layout.addWidget(img_label)
return box, img_label
self.cam_box, self.label1 = create_display("实时画面")
self.result_box, self.label2 = create_display("检测结果")
right_layout.addWidget(self.cam_box)
right_layout.addWidget(self.result_box)
main_layout.addWidget(left_panel)
main_layout.addWidget(right_panel, 1)
self.setCentralWidget(main_widget)
def load_model(self):
self.result = None
model_path, _ = QFileDialog.getOpenFileName(self, "选择模型文件", filter='*.pt')
if model_path:
self.model = YOLO(model_path)
def select_image(self):
self.result = None
image_path, fileType = QFileDialog.getOpenFileName(self, "选择图片文件", filter='*.jpg *.png *.bmp')
if image_path:
img = cv2.imread(image_path)
self.detect_image(img)
def stop_detect(self):
self.result = None
img = cv2.cvtColor(np.zeros((580, 550), np.uint8), cv2.COLOR_BGR2RGB)
img = QImage(img.data, img.shape[1], img.shape[0], QImage.Format_RGB888)
self.label1.setPixmap(QPixmap.fromImage(img))
self.label2.setPixmap(QPixmap.fromImage(img))
self.display_statistics()
def close(self):
exit()
def detect_image(self, img):
if self.model is not None:
frame = img
results = self.model.predict(frame,conf=self.con)
image_rgb = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
height1, width1, channel1 = frame.shape
bytesPerLine1 = 3 * width1
qimage1 = QImage(image_rgb.data, width1, height1, bytesPerLine1, QImage.Format_RGB888)
pixmap1 = QPixmap.fromImage(qimage1)
self.label1.setPixmap(pixmap1.scaled(self.label1.size(), Qt.AspectRatioMode.IgnoreAspectRatio))
annotated_image = results[0].plot()
annotated_image = cv2.cvtColor(annotated_image, cv2.COLOR_BGR2RGB) # 转换为 RGB
height2, width2, channel2 = annotated_image.shape
bytesPerLine2 = 3 * width2
qimage2 = QImage(annotated_image.data, width2, height2, bytesPerLine2, QImage.Format_RGB888)
pixmap2 = QPixmap.fromImage(qimage2)
pixmap2 = pixmap2.scaled(self.label2.size(), Qt.AspectRatioMode.IgnoreAspectRatio)
self.result = results
self.label2.setPixmap(pixmap2)
self.display_statistics()
def stat(self):
detected_classes = []
target_range = {0, 1} #修改为自己的类别
if self.result == None:
return None
for r in self.result:
classes = r.boxes.cls.cpu().numpy().astype(int).tolist()
# 筛选出在目标范围内的类别,并去重
detected_classes.extend([cls for cls in classes if cls in target_range])
class_counts = Counter(detected_classes)
class_counts_dic = dict(class_counts)
return class_counts_dic
def display_statistics(self):
class_counts = self.stat()
if class_counts == None:
self.output_text.setText('')
return
#修改class_labels为自己的类别对应关系,可中文
class_labels = {
0: "helmet", 1: "vest"
}
# 构建输出字符串
output_string = ""
for class_id, count in class_counts.items():
label = class_labels.get(class_id, f"类别{class_id}") # 如果没有找到标签,则使用默认标签
output_string += f"{label}: {count} 个\n"
self.output_text.setText(output_string)
def closeEvent(self, event: QEvent):
event.accept()
if __name__ == '__main__':
app = QApplication(sys.argv)
window = MainWindow()
window.show()
sys.exit(app.exec())
自己用需要修改自己数据集对应类别。
需要完整功能可私聊或通过公众号购买成品。有其他需求及想要定制的可以私信或通过公众号联系我,遇到报错可以在评论区交流,关注公众号获取更多资源~