|
|
# -*- coding: utf-8 -*-
|
|
|
# @Author : pan
|
|
|
# @Description : python main.py
|
|
|
# @Date : 2023年7月27日10:46:25
|
|
|
|
|
|
from PySide6.QtWidgets import QApplication, QMainWindow, QFileDialog, QMenu
|
|
|
from PySide6.QtGui import QImage, QPixmap, QColor,QCursor
|
|
|
from PySide6.QtCore import QTimer, QThread, Signal, QObject, Qt
|
|
|
|
|
|
from ui.main_window import Ui_MainWindow
|
|
|
from ui.pop.pop_box import MessageBox
|
|
|
from ui.ui_function import *
|
|
|
from ui.toast.toast import DialogOver
|
|
|
from ui.dialog.rtsp_win import Window
|
|
|
from ui.dialog.id_win import id_Window
|
|
|
|
|
|
from utils.main_utils import check_url, check_path
|
|
|
from utils.AtestCamera import Camera
|
|
|
from classes.yolo import YoloPredictor
|
|
|
from classes.main_config import MainConfig
|
|
|
from classes.car_chart import WorkerThread
|
|
|
|
|
|
from PIL import Image
|
|
|
import numpy as np
|
|
|
import supervision as sv
|
|
|
import subprocess
|
|
|
import sys
|
|
|
import cv2
|
|
|
import os
|
|
|
import datetime
|
|
|
|
|
|
|
|
|
class MainWindow(QMainWindow, Ui_MainWindow):
|
|
|
|
|
|
# 主窗口向yolo实例发送执行信号
|
|
|
main2yolo_begin_sgl = Signal()
|
|
|
|
|
|
# 主函数
|
|
|
def __init__(self, parent=None):
|
|
|
super(MainWindow, self).__init__()
|
|
|
|
|
|
self.setupUi(self)
|
|
|
# 背景设置为半透明 & 无边框窗口
|
|
|
self.setAttribute(Qt.WA_TranslucentBackground)
|
|
|
self.setWindowFlags(Qt.FramelessWindowHint)
|
|
|
|
|
|
# UI动作(动效)
|
|
|
UIFuncitons.uiDefinitions(self)
|
|
|
|
|
|
# 变量设置
|
|
|
self.car_threshold = 0 # 车辆阈值
|
|
|
self.web_flag = True # 可以开启
|
|
|
self.server_process = None # 服务器进程
|
|
|
self.image_id = 0 # 图片 id
|
|
|
self.txt_id = 0 # 标签 id
|
|
|
|
|
|
# 设置阴影
|
|
|
UIFuncitons.shadow_style(self, self.Class_QF, QColor(162,129,247))
|
|
|
UIFuncitons.shadow_style(self, self.Target_QF, QColor(251, 157, 139))
|
|
|
UIFuncitons.shadow_style(self, self.Fps_QF, QColor(170, 128, 213))
|
|
|
UIFuncitons.shadow_style(self, self.Model_QF, QColor(64, 186, 193))
|
|
|
|
|
|
# 设置 为 -- (好看)
|
|
|
self.Class_num.setText('--')
|
|
|
self.Target_num.setText('--')
|
|
|
self.fps_label.setText('--')
|
|
|
|
|
|
# 计时器:每2秒监视模型文件更改一次
|
|
|
self.Qtimer_ModelBox = QTimer(self)
|
|
|
self.Qtimer_ModelBox.timeout.connect(self.ModelBoxRefre)
|
|
|
self.Qtimer_ModelBox.start(2000)
|
|
|
|
|
|
|
|
|
self.yolo_init() # 实例化YOLO
|
|
|
self.model_bind() # 预测参数-数值变动绑定 主页面
|
|
|
# 主要功能绑定 @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
|
|
|
self.main_function_bind()
|
|
|
|
|
|
self.load_config() # 配置加载
|
|
|
self.model_load() # 模型加载
|
|
|
|
|
|
# 画图线程
|
|
|
self.is_draw_thread = False
|
|
|
self.draw_thread = WorkerThread()
|
|
|
|
|
|
self.show_status('欢迎使用智能车流分析系统!')
|
|
|
|
|
|
# 实例化YOLO
|
|
|
def yolo_init(self):
|
|
|
# Yolo-v8 thread
|
|
|
self.yolo_predict = YoloPredictor() #实例化yolo检测
|
|
|
self.select_model = self.model_box.currentText()
|
|
|
self.yolo_thread = QThread()
|
|
|
|
|
|
# 显示预测视频(左,右)
|
|
|
self.yolo_predict.yolo2main_trail_img.connect(lambda x: self.show_image(x, self.pre_video))
|
|
|
self.yolo_predict.yolo2main_box_img.connect(lambda x: self.show_image(x, self.res_video))
|
|
|
|
|
|
# 输出信息、FPS、类数、总数
|
|
|
self.yolo_predict.yolo2main_status_msg.connect(lambda x: self.show_status(x))
|
|
|
self.yolo_predict.yolo2main_fps.connect(lambda x: self.fps_label.setText(x))
|
|
|
self.yolo_predict.yolo2main_class_num.connect(lambda x:self.Class_num.setText(str(x)))
|
|
|
self.yolo_predict.yolo2main_progress.connect(lambda x: self.progress_bar.setValue(x))
|
|
|
|
|
|
# 移动线程里面去(通过main2yolo_begin_sgl信号控制-先要开启yolo_thread线程,才能启动yolo_predict的run方法)
|
|
|
self.yolo_predict.moveToThread(self.yolo_thread)
|
|
|
self.main2yolo_begin_sgl.connect(self.yolo_predict.run)
|
|
|
|
|
|
# 显示总车流量
|
|
|
self.yolo_predict.yolo2main_target_num.connect(lambda x:self.Target_setText(x))
|
|
|
|
|
|
# 模型加载
|
|
|
def model_load(self):
|
|
|
# 创建模型文件夹
|
|
|
check_path(self.config.models_path)
|
|
|
self.model_box.clear()
|
|
|
self.pt_list = os.listdir(f'./{self.config.models_path}')
|
|
|
self.pt_list = [file for file in self.pt_list if file.endswith('.pt') or file.endswith('.engine')]
|
|
|
self.pt_list.sort(key=lambda x: os.path.getsize(f'./{self.config.models_path}/' + x)) #按文件大小排序
|
|
|
self.model_box.clear()
|
|
|
self.model_box.addItems(self.pt_list)
|
|
|
|
|
|
# 主页面各功能绑定
|
|
|
def main_function_bind(self):
|
|
|
self.src_file_button.clicked.connect(self.open_src_file) # 打开文件夹
|
|
|
self.src_cam_button.clicked.connect(self.camera_select) # 摄像头
|
|
|
self.src_rtsp_button.clicked.connect(self.rtsp_seletction) # RTPS
|
|
|
self.src_graph_button.clicked.connect(self.show_traffic_graph) # 流量图
|
|
|
self.src_lock_button.clicked.connect(self.lock_id_selection) # 单目追踪
|
|
|
self.src_web_button.clicked.connect(self.web_back_end) # 后端开启与关闭
|
|
|
|
|
|
self.run_button.clicked.connect(self.run_or_continue) # 开始
|
|
|
self.stop_button.clicked.connect(self.stop) # 终止
|
|
|
|
|
|
self.save_res_button.toggled.connect(self.is_save_res) # 是否 保存 视频
|
|
|
self.save_txt_button.toggled.connect(self.is_save_txt) # 是否 保存 标签
|
|
|
self.show_labels_checkbox.toggled.connect(self.is_show_labels) # 是否 显示标签
|
|
|
self.show_trace_checkbox.toggled.connect(self.is_show_trace) # 是否 显示轨迹
|
|
|
|
|
|
self.ToggleBotton.clicked.connect(lambda: UIFuncitons.toggleMenu(self, True))
|
|
|
self.settings_button.clicked.connect(lambda: UIFuncitons.settingBox(self, True))
|
|
|
|
|
|
# 模型参数绑定
|
|
|
def model_bind(self):
|
|
|
self.model_box.currentTextChanged.connect(self.change_model)
|
|
|
self.iou_spinbox.valueChanged.connect(lambda x:self.change_val(x, 'iou_spinbox')) # iou box
|
|
|
self.iou_slider.valueChanged.connect(lambda x:self.change_val(x, 'iou_slider')) # iou scroll bar
|
|
|
|
|
|
self.conf_spinbox.valueChanged.connect(lambda x:self.change_val(x, 'conf_spinbox')) # conf box
|
|
|
self.conf_slider.valueChanged.connect(lambda x:self.change_val(x, 'conf_slider')) # conf scroll bar
|
|
|
|
|
|
self.speed_spinbox.valueChanged.connect(lambda x:self.change_val(x, 'speed_spinbox'))# speed box
|
|
|
self.speed_slider.valueChanged.connect(lambda x:self.change_val(x, 'speed_slider')) # speed scroll bar
|
|
|
|
|
|
self.speed_sss.valueChanged.connect(lambda x: self.change_val(x, 'speed_sss')) # speed box
|
|
|
self.speed_nnn.valueChanged.connect(lambda x:self.change_val(x, 'speed_nnn')) # speed scroll bar
|
|
|
|
|
|
# JSON配置文件初始化
|
|
|
def load_config(self):
|
|
|
self.config = MainConfig("./config/config.json")
|
|
|
|
|
|
self.save_res_button.setChecked(self.config.save_res)
|
|
|
self.save_txt_button.setChecked(self.config.save_txt)
|
|
|
|
|
|
self.iou_slider.setValue(self.config.iou * 100)
|
|
|
self.conf_slider.setValue(self.config.conf * 100)
|
|
|
self.speed_slider.setValue(self.config.rate)
|
|
|
self.speed_sss.setValue(self.config.car_threshold)
|
|
|
|
|
|
self.yolo_predict.save_txt = self.config.save_txt # 保存标签
|
|
|
self.yolo_predict.save_res = self.config.save_res # 保存结果
|
|
|
self.yolo_predict.save_txt_path = self.config.save_txt_path
|
|
|
self.yolo_predict.save_res_path = self.config.save_res_path
|
|
|
self.yolo_predict.new_model_name = f"./{self.config.models_path}/%s" % self.select_model
|
|
|
|
|
|
self.yolo_predict.show_trace = self.config.show_trace # 轨迹
|
|
|
self.show_trace_checkbox.setChecked(self.config.show_trace) # 轨迹
|
|
|
self.yolo_predict.show_labels = self.config.show_labels # 标签
|
|
|
self.show_labels_checkbox.setChecked(self.config.show_labels) # 标签
|
|
|
|
|
|
self.open_fold = self.config.open_fold
|
|
|
self.rtsp_ip = self.config.rtsp_ip
|
|
|
self.car_id = self.config.car_id
|
|
|
|
|
|
self.run_button.setChecked(False)
|
|
|
|
|
|
# 如果超出了车流阈值,那么就变红!!
|
|
|
def Target_setText(self, num):
|
|
|
num = str(num)
|
|
|
self.Target_num.setText(num)
|
|
|
self.char_label.setText(f"当前车流量: {num}")
|
|
|
if (int(num) > int(self.car_threshold)):
|
|
|
self.char_label.setStyleSheet("color: red;")
|
|
|
else:
|
|
|
self.char_label.setStyleSheet("color: green;")
|
|
|
|
|
|
|
|
|
#主窗口显示轨迹图像和检测图像 (缩放在这里)
|
|
|
@staticmethod
|
|
|
def show_image(img_src, label):
|
|
|
try:
|
|
|
# 检查图像的通道数,确定图像是否为彩色图像
|
|
|
if len(img_src.shape) == 3:
|
|
|
ih, iw, _ = img_src.shape
|
|
|
if len(img_src.shape) == 2:
|
|
|
ih, iw = img_src.shape
|
|
|
|
|
|
# 根据标签窗口的大小调整图像的大小
|
|
|
w = label.geometry().width()
|
|
|
h = label.geometry().height()
|
|
|
|
|
|
# 根据图像宽高比例进行缩放
|
|
|
if iw / w > ih / h:
|
|
|
scal = w / iw
|
|
|
nw = w
|
|
|
nh = int(scal * ih)
|
|
|
img_src_ = cv2.resize(img_src, (nw, nh))
|
|
|
|
|
|
else:
|
|
|
scal = h / ih
|
|
|
nw = int(scal * iw)
|
|
|
nh = h
|
|
|
img_src_ = cv2.resize(img_src, (nw, nh))
|
|
|
|
|
|
# 将OpenCV图像从BGR格式转换为RGB格式,并创建QImage对象
|
|
|
frame = cv2.cvtColor(img_src_, cv2.COLOR_BGR2RGB)
|
|
|
img = QImage(frame.data, frame.shape[1], frame.shape[0], frame.shape[2] * frame.shape[1],
|
|
|
QImage.Format_RGB888)
|
|
|
|
|
|
# 在标签窗口中显示图像
|
|
|
label.setPixmap(QPixmap.fromImage(img))
|
|
|
|
|
|
except Exception as e:
|
|
|
print(repr(e))
|
|
|
|
|
|
#控制开始|暂停
|
|
|
def run_or_continue(self):
|
|
|
# 检测是否有模型
|
|
|
if self.yolo_predict.new_model_name == '' or self.yolo_predict.new_model_name == None:
|
|
|
DialogOver(parent=self, text="请检测模型", title="运行失败", flags="danger")
|
|
|
self.run_button.setChecked(False)
|
|
|
return
|
|
|
# 检测输入源
|
|
|
if self.yolo_predict.source == '' or self.yolo_predict.source == None:
|
|
|
self.show_status('请在检测前选择输入源...')
|
|
|
self.run_button.setChecked(False)
|
|
|
DialogOver(parent=self, text="请检测输入源", title="运行失败", flags="danger")
|
|
|
return
|
|
|
|
|
|
self.yolo_predict.stop_dtc = False # 线程开始
|
|
|
|
|
|
# 开始
|
|
|
if self.run_button.isChecked():
|
|
|
|
|
|
# 图片预测
|
|
|
file_extension = self.yolo_predict.source[-3:].lower()
|
|
|
if file_extension == 'png' or file_extension == 'jpg':
|
|
|
self.img_predict()
|
|
|
return
|
|
|
|
|
|
# 视频预测
|
|
|
DialogOver(parent=self, text="开始检测...", title="运行成功", flags="success")
|
|
|
self.run_button.setChecked(True)
|
|
|
|
|
|
self.draw_thread.run_continue() # 折线图开始
|
|
|
|
|
|
# 不可再改变设置(config动态调整 关闭)
|
|
|
self.save_txt_button.setEnabled(False)
|
|
|
self.save_res_button.setEnabled(False)
|
|
|
self.conf_slider.setEnabled(False)
|
|
|
self.iou_slider.setEnabled(False)
|
|
|
self.speed_slider.setEnabled(False)
|
|
|
|
|
|
self.show_status('检测中...')
|
|
|
if '0' in self.yolo_predict.source or 'rtsp' in self.yolo_predict.source:
|
|
|
self.progress_bar.setFormat('实时视频流检测中...')
|
|
|
if 'avi' in self.yolo_predict.source or 'mp4' in self.yolo_predict.source:
|
|
|
self.progress_bar.setFormat("当前检测进度:%p%")
|
|
|
self.yolo_predict.continue_dtc = True
|
|
|
# 开始检测
|
|
|
if not self.yolo_thread.isRunning():
|
|
|
self.yolo_thread.start()
|
|
|
self.main2yolo_begin_sgl.emit()
|
|
|
# 暂停
|
|
|
else:
|
|
|
self.draw_thread.pause() # 折线图暂停
|
|
|
self.yolo_predict.continue_dtc = False
|
|
|
self.show_status("暂停...")
|
|
|
DialogOver(parent=self, text="已暂停检测", title="运行暂停", flags="warning")
|
|
|
self.run_button.setChecked(False)
|
|
|
|
|
|
# 显示状态 (底部栏
|
|
|
def show_status(self, msg):
|
|
|
self.status_bar.setText(msg) # 显示输出
|
|
|
if msg == '检测完成':
|
|
|
self.save_res_button.setEnabled(True)
|
|
|
self.save_txt_button.setEnabled(True)
|
|
|
self.run_button.setChecked(False)
|
|
|
self.progress_bar.setValue(0)
|
|
|
# 终止yolo线程
|
|
|
if self.yolo_thread.isRunning():
|
|
|
self.yolo_thread.quit()
|
|
|
# 修改画图线程状态
|
|
|
self.draw_thread.stop()
|
|
|
self.is_draw_thread = False
|
|
|
|
|
|
|
|
|
elif msg == '检测终止':
|
|
|
self.save_res_button.setEnabled(True)
|
|
|
self.save_txt_button.setEnabled(True)
|
|
|
self.run_button.setChecked(False)
|
|
|
self.progress_bar.setValue(0)
|
|
|
# 终止yolo线程
|
|
|
if self.yolo_thread.isRunning():
|
|
|
self.yolo_thread.quit()
|
|
|
# 修改画图线程状态
|
|
|
self.draw_thread.stop()
|
|
|
self.is_draw_thread = False
|
|
|
|
|
|
self.pre_video.clear()
|
|
|
self.res_video.clear()
|
|
|
self.Class_num.setText('--')
|
|
|
self.Target_num.setText('--')
|
|
|
self.fps_label.setText('--')
|
|
|
|
|
|
# 打开文件
|
|
|
def open_src_file(self):
|
|
|
|
|
|
name, _ = QFileDialog.getOpenFileName(self, 'Video/image', self.open_fold, "Pic File(*.mp4 *.mkv *.avi *.flv *.jpg *.png)")
|
|
|
if name:
|
|
|
self.yolo_predict.source = name
|
|
|
self.show_status('加载文件:{}'.format(os.path.basename(name)))
|
|
|
self.open_fold = os.path.dirname(name)
|
|
|
# 终止事件
|
|
|
self.stop()
|
|
|
DialogOver(parent=self, text=f"文件路径: {name}", title="加载成功", flags="success")
|
|
|
|
|
|
# camera选择
|
|
|
def camera_select(self):
|
|
|
#try:
|
|
|
# 关闭YOLO线程
|
|
|
self.stop()
|
|
|
#获取本地摄像头数量
|
|
|
_, cams = Camera().get_cam_num()
|
|
|
popMenu = QMenu()
|
|
|
popMenu.setFixedWidth(self.src_cam_button.width())
|
|
|
popMenu.setStyleSheet('''
|
|
|
QMenu {
|
|
|
font-size: 20px;
|
|
|
font-family: "Microsoft YaHei UI";
|
|
|
font-weight: light;
|
|
|
color:white;
|
|
|
padding-left: 5px;
|
|
|
padding-right: 5px;
|
|
|
padding-top: 4px;
|
|
|
padding-bottom: 4px;
|
|
|
border-style: solid;
|
|
|
border-width: 0px;
|
|
|
border-color: rgba(255, 212, 255, 255);
|
|
|
border-radius: 3px;
|
|
|
background-color: rgba(16,155,226,50);
|
|
|
}
|
|
|
''')
|
|
|
|
|
|
for cam in cams:
|
|
|
exec("action_%s = QAction('%s 号摄像头')" % (cam, cam))
|
|
|
exec("popMenu.addAction(action_%s)" % cam)
|
|
|
pos = QCursor.pos()
|
|
|
action = popMenu.exec(pos)
|
|
|
|
|
|
# 设置摄像头来源
|
|
|
if action:
|
|
|
str_temp = ''
|
|
|
selected_stream_source = str_temp.join(filter(str.isdigit, action.text())) #获取摄像头号,去除非数字字符
|
|
|
self.yolo_predict.source = selected_stream_source
|
|
|
self.show_status(f'摄像头设备:{action.text()}')
|
|
|
DialogOver(parent=self, text=f"当前摄像头为: {action.text()}", title="摄像头选择成功", flags="success")
|
|
|
|
|
|
# 1、选择rtsp
|
|
|
def rtsp_seletction(self):
|
|
|
self.rtsp_window = Window()
|
|
|
self.rtsp_window.rtspEdit.setText(self.rtsp_ip)
|
|
|
self.rtsp_window.show()
|
|
|
# 如果点击则加载RTSP
|
|
|
self.rtsp_window.rtspButton.clicked.connect(lambda: self.load_rtsp(self.rtsp_window.rtspEdit.text()))
|
|
|
|
|
|
# 2、加载RTSP
|
|
|
def load_rtsp(self, ip):
|
|
|
|
|
|
MessageBox(self.close_button, title='提示', text='加载 rtsp...', time=1000, auto=True).exec()
|
|
|
self.stop() # 关闭YOLO线程
|
|
|
|
|
|
self.yolo_predict.source = ip
|
|
|
self.rtsp_ip = ip # 写会ip
|
|
|
self.rtsp_window.close()
|
|
|
|
|
|
#状态显示
|
|
|
self.show_status(f'加载rtsp地址:{ip}')
|
|
|
DialogOver(parent=self, text=f"rtsp地址为: {ip}", title="RTSP加载成功", flags="success")
|
|
|
|
|
|
# 1、设置 单目标
|
|
|
def lock_id_selection(self):
|
|
|
self.yolo_predict.lock_id = None
|
|
|
self.id_window = id_Window()
|
|
|
self.id_window.idEdit.setText(str(self.car_id))
|
|
|
self.id_window.show()
|
|
|
self.id_window.idButton.clicked.connect(lambda: self.set_lock_id(self.id_window.idEdit.text()))
|
|
|
|
|
|
# 2、设置 单目标 ID
|
|
|
def set_lock_id(self,lock_id):
|
|
|
self.yolo_predict.lock_id = None
|
|
|
self.yolo_predict.lock_id = lock_id
|
|
|
self.car_id = lock_id # 写回lock_id
|
|
|
self.show_status('加载ID:{}'.format(lock_id))
|
|
|
self.id_window.close()
|
|
|
|
|
|
#加载流量折线图
|
|
|
def show_traffic_graph(self):
|
|
|
# 没有开始检测
|
|
|
if not self.run_button.isChecked():
|
|
|
DialogOver(parent=self, text="请先开始目标检测!", title="开启失败", flags="danger")
|
|
|
return
|
|
|
|
|
|
# 避免重复开启
|
|
|
if self.is_draw_thread:
|
|
|
DialogOver(parent=self, text="流量图已经开启啦!", title="不能重复开启", flags="danger")
|
|
|
return
|
|
|
self.draw_thread.start()
|
|
|
self.is_draw_thread = True
|
|
|
|
|
|
# 开启Web后端
|
|
|
def web_back_end(self):
|
|
|
# 终止事件
|
|
|
self.stop()
|
|
|
|
|
|
base_dir = os.path.dirname(os.path.abspath(__file__))
|
|
|
flask_app_path = os.path.join(base_dir, 'app.py')
|
|
|
|
|
|
# True是可以开启
|
|
|
if (self.web_flag):
|
|
|
self.server_process = subprocess.Popen(['python', flask_app_path])
|
|
|
MessageBox(self.close_button, title='提示', text='正在开启网页端...', time=2000, auto=True).exec()
|
|
|
|
|
|
# 检查子进程是否启动
|
|
|
try:
|
|
|
if self.server_process.pid is not None:
|
|
|
self.src_web_button.setText("关闭网页端")
|
|
|
self.web_flag = False
|
|
|
DialogOver(parent=self, text="网页端已开启", title="开启成功", flags="success")
|
|
|
except Exception as e:
|
|
|
DialogOver(parent=self, text=str(e), title="开启失败", flags="danger")
|
|
|
|
|
|
|
|
|
# False是关闭后端
|
|
|
else:
|
|
|
try:
|
|
|
self.server_process.terminate()
|
|
|
MessageBox(self.close_button, title='提示', text='正在关闭网页端...', time=2000, auto=True).exec()
|
|
|
|
|
|
self.src_web_button.setText("开启网页端")
|
|
|
self.web_flag = True
|
|
|
DialogOver(parent=self, text="网页端已关闭", title="关闭成功", flags="success")
|
|
|
except Exception as e:
|
|
|
DialogOver(parent=self, text=str(e), title="关闭失败", flags="danger")
|
|
|
|
|
|
#保存提示(MP4)
|
|
|
def is_save_res(self):
|
|
|
if self.save_res_button.checkState() == Qt.CheckState.Unchecked:
|
|
|
self.show_status('提示: 监测结果不会被保存')
|
|
|
self.yolo_predict.save_res = False
|
|
|
elif self.save_res_button.checkState() == Qt.CheckState.Checked:
|
|
|
self.show_status('提示: 监测结果将会被保存')
|
|
|
self.yolo_predict.save_res = True
|
|
|
|
|
|
#保存提示(txt)
|
|
|
def is_save_txt(self):
|
|
|
if self.save_txt_button.checkState() == Qt.CheckState.Unchecked:
|
|
|
self.show_status('提示: 标签信息不会被保存')
|
|
|
self.yolo_predict.save_txt = False
|
|
|
elif self.save_txt_button.checkState() == Qt.CheckState.Checked:
|
|
|
self.show_status('提示: 标签信息将会被保存')
|
|
|
self.yolo_predict.save_txt = True
|
|
|
|
|
|
# 是否显示 标签
|
|
|
def is_show_labels(self):
|
|
|
if self.show_labels_checkbox.checkState() == Qt.CheckState.Unchecked:
|
|
|
self.yolo_predict.show_labels = False
|
|
|
self.show_status('提示: 不再显示标签')
|
|
|
elif self.show_labels_checkbox.checkState() == Qt.CheckState.Checked:
|
|
|
self.yolo_predict.show_labels = True
|
|
|
self.show_status('提示: 显示标签')
|
|
|
|
|
|
# 是否显示 轨迹
|
|
|
def is_show_trace(self):
|
|
|
if self.show_trace_checkbox.checkState() == Qt.CheckState.Unchecked:
|
|
|
self.yolo_predict.show_trace = False
|
|
|
self.show_status('提示: 不再显示轨迹')
|
|
|
elif self.show_trace_checkbox.checkState() == Qt.CheckState.Checked:
|
|
|
self.yolo_predict.show_trace = True
|
|
|
self.show_status('提示: 显示轨迹')
|
|
|
|
|
|
#终止事件(按下终止按钮 or 输入源更换的时候)
|
|
|
def stop(self):
|
|
|
try:
|
|
|
# 摄像头释放
|
|
|
self.yolo_predict.release_capture() # 这里是为了终止使用摄像头检测函数的线程,改了yolo源码
|
|
|
# 结束线程
|
|
|
self.yolo_thread.quit()
|
|
|
|
|
|
except:
|
|
|
pass
|
|
|
self.yolo_predict.stop_dtc = True
|
|
|
self.run_button.setChecked(False) #恢复按钮状态
|
|
|
|
|
|
# 终止后才可以修改设置
|
|
|
self.save_res_button.setEnabled(True) #把保存按钮设置为可用
|
|
|
self.save_txt_button.setEnabled(True) #把保存按钮设置为可用
|
|
|
self.iou_slider.setEnabled(True) #把滑块设置为可用
|
|
|
self.conf_slider.setEnabled(True) #把滑块设置为可用
|
|
|
self.speed_slider.setEnabled(True) #把滑块设置为可用
|
|
|
self.pre_video.clear() #清空视频显示
|
|
|
self.res_video.clear() #清空视频显示
|
|
|
self.progress_bar.setValue(0) #进度条清零
|
|
|
self.Class_num.setText('--')
|
|
|
self.Target_num.setText('--')
|
|
|
self.fps_label.setText('--')
|
|
|
|
|
|
#检测参数设置
|
|
|
def change_val(self, x, flag):
|
|
|
# 交互比
|
|
|
if flag == 'iou_spinbox':
|
|
|
self.iou_slider.setValue(int(x*100))
|
|
|
elif flag == 'iou_slider':
|
|
|
self.iou_spinbox.setValue(x/100)
|
|
|
self.show_status('IOU Threshold: %s' % str(x/100))
|
|
|
self.yolo_predict.iou_thres = x/100
|
|
|
# 置信度
|
|
|
elif flag == 'conf_spinbox':
|
|
|
self.conf_slider.setValue(int(x*100))
|
|
|
elif flag == 'conf_slider':
|
|
|
self.conf_spinbox.setValue(x/100)
|
|
|
self.show_status('Conf Threshold: %s' % str(x/100))
|
|
|
self.yolo_predict.conf_thres = x/100
|
|
|
# 延时
|
|
|
elif flag == 'speed_spinbox':
|
|
|
self.speed_slider.setValue(x)
|
|
|
elif flag == 'speed_slider':
|
|
|
self.speed_spinbox.setValue(x)
|
|
|
self.show_status('Delay: %s ms' % str(x))
|
|
|
self.yolo_predict.speed_thres = x # ms
|
|
|
|
|
|
# 车辆数预测警报
|
|
|
elif flag == 'speed_nnn':
|
|
|
self.speed_sss.setValue(x)
|
|
|
elif flag == 'speed_sss':
|
|
|
self.speed_nnn.setValue(x)
|
|
|
self.show_status('流量阈值设置: %s 辆' % str(x))
|
|
|
self.car_threshold = x # ms
|
|
|
|
|
|
#模型更换
|
|
|
def change_model(self,x):
|
|
|
self.select_model = self.model_box.currentText()
|
|
|
self.yolo_predict.new_model_name = f"./{self.config.models_path}/%s" % self.select_model
|
|
|
self.show_status('更改模型:%s' % self.select_model)
|
|
|
self.Model_name.setText(self.select_model)
|
|
|
|
|
|
#循环监测文件夹的文件变化
|
|
|
def ModelBoxRefre(self):
|
|
|
pt_list = os.listdir(f'./{self.config.models_path}')
|
|
|
pt_list = [file for file in pt_list if file.endswith('.pt') or file.endswith('.engine')]
|
|
|
pt_list.sort(key=lambda x: os.path.getsize(f'./{self.config.models_path}/' + x))
|
|
|
#必须排序后再比较,否则列表会一直刷新
|
|
|
if pt_list != self.pt_list:
|
|
|
self.pt_list = pt_list
|
|
|
self.model_box.clear()
|
|
|
self.model_box.addItems(self.pt_list)
|
|
|
|
|
|
#获取鼠标位置(用于按住标题栏拖动窗口)
|
|
|
def mousePressEvent(self, event):
|
|
|
p = event.globalPosition()
|
|
|
globalPos = p.toPoint()
|
|
|
self.dragPos = globalPos
|
|
|
|
|
|
#拖动窗口大小时优化调整
|
|
|
def resizeEvent(self, event):
|
|
|
# Update Size Grips
|
|
|
UIFuncitons.resize_grips(self)
|
|
|
|
|
|
# 退出时退出线程,保存设置
|
|
|
def closeEvent(self, event):
|
|
|
try:
|
|
|
self.stop()
|
|
|
# 检测画图线程
|
|
|
self.draw_thread.close_exec()
|
|
|
# self.draw_thread.deleteLater()
|
|
|
|
|
|
# config.json
|
|
|
self.config.save_res = self.yolo_predict.save_res
|
|
|
self.config.save_txt = self.yolo_predict.save_txt
|
|
|
|
|
|
self.config.show_labels = self.yolo_predict.show_labels
|
|
|
self.config.show_trace = self.yolo_predict.show_trace
|
|
|
|
|
|
self.config.iou = self.yolo_predict.iou_thres
|
|
|
self.config.conf = self.yolo_predict.conf_thres
|
|
|
self.config.rate = self.yolo_predict.speed_thres
|
|
|
|
|
|
self.config.car_threshold = self.car_threshold # 车辆警报 阈值
|
|
|
self.config.rtsp_ip = self.rtsp_ip
|
|
|
self.config.car_id = self.car_id
|
|
|
self.config.open_fold = self.open_fold
|
|
|
|
|
|
self.config.save_config() # 保存设置
|
|
|
|
|
|
# 退出弹窗 2s
|
|
|
MessageBox(
|
|
|
self.close_button, title='Note', text='退出中,请等待...', time=2000, auto=True).exec()
|
|
|
|
|
|
# 检查服务器进程是否启动
|
|
|
if self.server_process is not None:
|
|
|
if self.server_process.pid is not None:
|
|
|
self.server_process.terminate() # 服务器进程关闭
|
|
|
|
|
|
|
|
|
sys.exit(0)
|
|
|
except Exception as e:
|
|
|
print(e)
|
|
|
sys.exit(0)
|
|
|
|
|
|
# 预测图片
|
|
|
def img_predict(self):
|
|
|
|
|
|
if check_url(self.yolo_predict.source):
|
|
|
DialogOver(parent=self, text="目标路径含有中文!", title="程序取消", flags="danger")
|
|
|
return
|
|
|
|
|
|
self.run_button.setChecked(False) # 按钮
|
|
|
# 读取照片
|
|
|
image = cv2.imread(self.yolo_predict.source)
|
|
|
org_img = image.copy()
|
|
|
# 加载模型
|
|
|
model = self.yolo_predict.load_yolo_model()
|
|
|
# 获取数据源
|
|
|
iter_model = iter(model.track(source=image, show=False))
|
|
|
result = next(iter_model) # 这里是检测的核心,
|
|
|
# 如果没有目标
|
|
|
if result.boxes.id is None:
|
|
|
DialogOver(parent=self, text="该图片中没有要检测的目标哟!", title="运行完成", flags="warning")
|
|
|
self.show_image(image, self.pre_video)
|
|
|
self.show_image(image, self.res_video)
|
|
|
self.yolo_predict.source = ''
|
|
|
return
|
|
|
|
|
|
# 如果有目标
|
|
|
detections = sv.Detections.from_yolov8(result)
|
|
|
detections.tracker_id = result.boxes.id.cpu().numpy().astype(int)
|
|
|
# 画标签
|
|
|
labels_write, img_box = self.yolo_predict.creat_labels(detections, image, model)
|
|
|
|
|
|
# 显示信息 —— 类别数 & 总数
|
|
|
self.Class_num.setText(str(self.yolo_predict.get_class_number(detections)))
|
|
|
self.Target_num.setText(str(len(detections.tracker_id)))
|
|
|
# 显示图片
|
|
|
self.show_image(org_img, self.pre_video) # left
|
|
|
self.show_image(img_box, self.res_video) # right
|
|
|
self.yolo_predict.source = ''
|
|
|
DialogOver(parent=self, text="图片检测完成", title="运行成功", flags="success")
|
|
|
|
|
|
# 保存图片
|
|
|
if self.yolo_predict.save_res:
|
|
|
check_path(self.config.save_res_path) # 检查保存路径
|
|
|
# 存在同名文件,自增 self.image_id 直至文件不存在
|
|
|
while os.path.exists(f"{self.config.save_res_path}/image_result_{self.image_id}.jpg"):
|
|
|
self.image_id += 1
|
|
|
# 将 BGR 格式的 frame 转换为 RGB 格式
|
|
|
rgb_frame = cv2.cvtColor(img_box, cv2.COLOR_BGR2RGB)
|
|
|
# 把 rgb_frame 转换为 numpy格式 就行了
|
|
|
numpy_frame = np.array(rgb_frame)
|
|
|
Image.fromarray(numpy_frame).save(f"./{self.config.save_res_path}/image_result_{self.image_id}.jpg")
|
|
|
|
|
|
# 存储labels里的信息
|
|
|
if self.yolo_predict.save_txt:
|
|
|
check_path(self.config.save_txt_path) # 检查保存路径
|
|
|
# 存在同名文件,自增 self.txt_id 直至文件不存在
|
|
|
while os.path.exists(f"{self.config.save_txt_path}/result_{self.txt_id}.jpg"):
|
|
|
self.txt_id += 1
|
|
|
|
|
|
with open(f'{self.config.save_txt_path}/result_{self.txt_id}.txt', 'a') as f:
|
|
|
f.write('当前时刻屏幕信息:' +
|
|
|
str(labels_write) +
|
|
|
f'检测时间: {datetime.datetime.now().strftime("%Y-%m-%d-%H:%M:%S")}' +
|
|
|
f' 路段通过的目标总数: {len(detections.tracker_id)}')
|
|
|
f.write('\n')
|
|
|
return
|
|
|
|
|
|
if __name__ == "__main__":
|
|
|
app = QApplication(sys.argv)
|
|
|
Home = MainWindow()
|
|
|
Home.show()
|
|
|
sys.exit(app.exec())
|