2025.2.7
今天继续页面优化,在昨天的基础上加上了关闭摄像头,暂停和继续摄像头的功能,页面如下:

开启摄像头后页面如下:

可点击关闭摄像头将摄像头关闭,或者点击暂停暂时停止拍摄。
代码如下:
from PySide6 import QtWidgets, QtCore, QtGui import cv2, os, time, random from threading import Thread, Lock from ultralytics import YOLO os.environ['YOLO_VERBOSE'] = 'False' class MainWindow(QtWidgets.QMainWindow): update_signal = QtCore.Signal(QtGui.QImage) def __init__(self): super().__init__() self.setup_ui() self.setup_video() self.analysis_results = [] self.current_detections = [] self.current_frame = None self.lock = Lock() self.video_paused = False self.is_video_file = False self.scale_x = 1.0 # 新增缩放比例参数 self.scale_y = 1.0 def setup_ui(self): self.setWindowTitle("智能分析系统") self.resize(1200, 800) self.setStyleSheet("background-color: #F0F0F0;") central_widget = QtWidgets.QWidget() self.setCentralWidget(central_widget) main_layout = QtWidgets.QVBoxLayout(central_widget) main_layout.setContentsMargins(10, 10, 10, 10) main_layout.setSpacing(10) # 顶部切换按钮 btn_style = """ QPushButton { background-color: #87CEFA; color: black; border: none; padding: 8px 16px; border-radius: 4px; min-width: 120px; min-height: 35px; font-size: 14px; } QPushButton:disabled { background-color: #D3D3D3; color: gray; } """ self.analysis_btn = QtWidgets.QPushButton("数据分析") self.status_btn = QtWidgets.QPushButton("今日情况") self.analysis_btn.setStyleSheet(btn_style) self.status_btn.setStyleSheet(btn_style.replace("#87CEFA", "#D3D3D3").replace("black", "gray")) top_layout = QtWidgets.QHBoxLayout() top_layout.addWidget(self.analysis_btn) top_layout.addWidget(self.status_btn) top_layout.setSpacing(10) main_layout.addLayout(top_layout) # 页面堆栈 self.stacked_pages = QtWidgets.QStackedWidget() main_layout.addWidget(self.stacked_pages) self.setup_analysis_page() self.setup_status_page() self.analysis_btn.clicked.connect(self.switch_to_analysis) self.status_btn.clicked.connect(self.switch_to_status) self.switch_to_analysis() def switch_to_analysis(self): self.stacked_pages.setCurrentIndex(0) self.analysis_btn.setDisabled(True) self.status_btn.setDisabled(False) self.analysis_btn.setStyleSheet("background-color: #87CEFA; color: black;") self.status_btn.setStyleSheet("background-color: #D3D3D3; color: gray;") def switch_to_status(self): self.stacked_pages.setCurrentIndex(1) self.status_btn.setDisabled(True) self.analysis_btn.setDisabled(False) self.status_btn.setStyleSheet("background-color: #87CEFA; color: black;") self.analysis_btn.setStyleSheet("background-color: #D3D3D3; color: gray;") def setup_analysis_page(self): page = QtWidgets.QWidget() layout = QtWidgets.QHBoxLayout(page) layout.setContentsMargins(5, 5, 5, 5) layout.setSpacing(15) # 左侧视频区域 left_panel = QtWidgets.QWidget() left_layout = QtWidgets.QVBoxLayout(left_panel) left_layout.setSpacing(10) self.video_label = QtWidgets.QLabel() self.video_label.setMinimumSize(640, 480) self.video_label.setStyleSheet(""" border: 2px solid #666; background-color: #000000; border-radius: 5px; """) self.video_label.setAlignment(QtCore.Qt.AlignCenter) # 图片居中显示 self.cam_btn = QtWidgets.QPushButton("暂停/继续") # self.cam_btn.clicked.connect(self.toggle_pause()) left_layout.addWidget(self.video_label) left_layout.addWidget(self.cam_btn) # 右侧信息面板 right_panel = QtWidgets.QWidget() right_layout = QtWidgets.QVBoxLayout(right_panel) right_layout.setSpacing(15) # 按钮样式 btn_style = """ QPushButton { background-color: #4CAF50; color: white; border: none; padding: 8px 16px; border-radius: 4px; min-width: 150px; min-height: 35px; font-size: 14px; } QPushButton:hover { background-color: #45a049; } QPushButton:pressed { background-color: #3d8b40; } """ # 摄像头开启按钮 self.start_cam_btn = QtWidgets.QPushButton("开启摄像头") self.start_cam_btn.setStyleSheet(btn_style) right_layout.addWidget(self.start_cam_btn) # 新增导入按钮 self.import_image_btn = QtWidgets.QPushButton("导入图片") self.import_image_btn.setStyleSheet(btn_style) self.import_btn = QtWidgets.QPushButton("导入视频") self.import_btn.setStyleSheet(btn_style) right_layout.addWidget(self.import_image_btn) right_layout.addWidget(self.import_btn) # 检测信息组 info_group = QtWidgets.QGroupBox("检测信息") info_group.setStyleSheet(""" QGroupBox { font-size: 14px; border: 1px solid #666; border-radius: 5px; margin-top: 10px; } QGroupBox::title { subcontrol-origin: margin; left: 10px; padding: 0 3px; } """) info_layout = QtWidgets.QFormLayout(info_group) info_layout.setVerticalSpacing(10) info_layout.setHorizontalSpacing(15) self.confidence_label = QtWidgets.QLabel("0%") self.target_count_label = QtWidgets.QLabel("0") self.x_label = QtWidgets.QLabel("0") self.y_label = QtWidgets.QLabel("0") self.target_select = QtWidgets.QComboBox() # 统一标签样式 label_style = "font-size: 14px; min-width: 80px;" for label in [self.confidence_label, self.target_count_label, self.x_label, self.y_label]: label.setStyleSheet(label_style) info_layout.addRow("置信度:", self.confidence_label) info_layout.addRow("目标数量:", self.target_count_label) info_layout.addRow("X坐标:", self.x_label) info_layout.addRow("Y坐标:", self.y_label) info_layout.addRow("目标选择:", self.target_select) self.save_btn = QtWidgets.QPushButton("保存结果") self.save_btn.setStyleSheet(btn_style) right_layout.addWidget(info_group) right_layout.addWidget(self.save_btn) right_layout.addStretch() layout.addWidget(left_panel) layout.addWidget(right_panel) self.stacked_pages.addWidget(page) def setup_status_page(self): page = QtWidgets.QWidget() layout = QtWidgets.QVBoxLayout(page) # 成熟度展示 maturity_group = QtWidgets.QGroupBox("今日成熟度分布") grid = QtWidgets.QGridLayout(maturity_group) maturities = ["60%", "55%", "65%", "57%"] for i, mat in enumerate(maturities): label = QtWidgets.QLabel(f"成熟度:{mat}") grid.addWidget(label, i // 2, i % 2) layout.addWidget(maturity_group) self.stacked_pages.addWidget(page) def setup_video(self): self.model = YOLO('yolov8n.pt') self.frame_queue = [] self.cap = None self.video_timer = QtCore.QTimer() self.video_timer.timeout.connect(self.update_frame) self.update_signal.connect(self.update_image) self.cam_btn.setStyleSheet(""" QPushButton { background-color: #4CAF50; color: white; min-width: 150px; min-height: 35px; } """) self.start_cam_btn.clicked.connect(self.start_camera) self.cam_btn.clicked.connect(self.toggle_pause) self.save_btn.clicked.connect(self.save_results) self.import_btn.clicked.connect(self.import_video) self.import_image_btn.clicked.connect(self.import_image) self.target_select.currentTextChanged.connect(self.update_selection) Thread(target=self.process_frames, daemon=True).start() def start_camera(self): """开启或关闭摄像头""" if self.start_cam_btn.text() == "开启摄像头": if self.cap is None or not self.cap.isOpened(): self.cap = cv2.VideoCapture(0, cv2.CAP_DSHOW) if self.cap.isOpened(): self.video_timer.start(30) self.cam_btn.setText("暂停") self.start_cam_btn.setText("关闭摄像头") self.video_paused = False # self.start_cam_btn.setDisabled(True) elif self.start_cam_btn.text() == "关闭摄像头": self.close_camera() def close_camera(self): """关闭摄像头""" if self.cap: self.cap.release() self.cap = None self.video_timer.stop() self.start_cam_btn.setText("开启摄像头") self.cam_btn.setText("开启摄像头") self.start_cam_btn.setDisabled(False) def toggle_pause(self): """暂停或继续""" if self.cam_btn.text() == "暂停": self.video_timer.stop() self.cam_btn.setText("继续") self.video_paused = True elif self.cam_btn.text() == "继续": self.video_timer.start(30) self.cam_btn.setText("暂停") self.video_paused = False def import_video(self): if self.cap: self.close_camera() path, _ = QtWidgets.QFileDialog.getOpenFileName( self, "选择视频文件", "", "视频文件 (*.mp4 *.avi)" ) if path: self.cap = cv2.VideoCapture(path) if self.cap.isOpened(): self.video_timer.start(30) self.cam_btn.setText("暂停") self.start_cam_btn.setDisabled(True) def update_image(self, q_img): """更新视频标签显示的图像""" self.video_label.setPixmap(QtGui.QPixmap.fromImage(q_img)) def import_image(self): if self.cap: self.close_camera() path, _ = QtWidgets.QFileDialog.getOpenFileName( self, "选择图片文件", "", "图片文件 (*.jpg *.png *.bmp)" ) if path: self.video_paused = True if self.video_timer.isActive(): self.video_timer.stop() self.cam_btn.setText("继续") image = cv2.imread(path) frame = cv2.cvtColor(image, cv2.COLOR_BGR2RGB) # 调整图片显示大小 h, w, _ = frame.shape aspect_ratio = w / h label_width = self.video_label.width() label_height = self.video_label.height() if aspect_ratio > 1: new_w = label_width new_h = int(new_w / aspect_ratio) else: new_h = label_height new_w = int(new_h * aspect_ratio) frame = cv2.resize(frame, (new_w, new_h)) self.current_frame = frame.copy() self.scale_x = 1.0 # 图片已缩放,比例设为1 self.scale_y = 1.0 results = self.model(frame)[0] self.current_detections = results # 更新检测信息 if len(results) > 0: confidences = results.boxes.conf.cpu().numpy() avg_conf = confidences.mean() if len(confidences) > 0 else 0 self.confidence_label.setText(f"{avg_conf:.1%}") self.target_count_label.setText(str(len(results))) self.x_label.setText("N/A") self.y_label.setText("N/A") # 更新下拉框 self.target_select.clear() self.target_select.addItem("全部") for i in range(1, len(results) + 1): self.target_select.addItem(str(i)) # 显示检测结果 annotated = results.plot() annotated = cv2.cvtColor(annotated, cv2.COLOR_BGR2RGB) q_img = QtGui.QImage(annotated.data, annotated.shape[1], annotated.shape[0], annotated.shape[2] * annotated.shape[1], QtGui.QImage.Format_RGB888) self.video_label.setPixmap(QtGui.QPixmap.fromImage(q_img)) def update_frame(self): if self.cap and self.cap.isOpened(): ret, frame = self.cap.read() if ret: frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB) with self.lock: self.frame_queue.append(frame) self.current_frame = frame.copy() def process_frames(self): while True: if self.frame_queue: with self.lock: frame = self.frame_queue.pop(0) # 计算缩放比例 original_h, original_w = frame.shape[:2] label_width = self.video_label.width() label_height = self.video_label.height() aspect_ratio = original_w / original_h if aspect_ratio > 1: new_w = label_width new_h = int(new_w / aspect_ratio) else: new_h = label_height new_w = int(new_h * aspect_ratio) self.scale_x = new_w / original_w self.scale_y = new_h / original_h # 处理原始尺寸帧 frame_rgb = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB) results = self.model(frame_rgb)[0] self.current_detections = results # 生成缩放后的显示帧 scaled_frame = cv2.resize(frame_rgb, (new_w, new_h)) self.current_frame = scaled_frame.copy() # 更新置信度显示 confidences = results.boxes.conf.cpu().numpy() avg_conf = confidences.mean() if len(confidences) > 0 else 0 self.confidence_label.setText(f"{avg_conf:.1%}") self.target_count_label.setText(str(len(results))) self.x_label.setText("N/A") self.y_label.setText("N/A") # 绘制检测结果 annotated = results.plot() annotated = cv2.cvtColor(annotated, cv2.COLOR_BGR2RGB) annotated = cv2.resize(annotated, (new_w, new_h)) q_img = QtGui.QImage(annotated.data, new_w, new_h, annotated.shape[2] * new_w, QtGui.QImage.Format_RGB888) self.update_signal.emit(q_img) def update_selection(self): if self.video_paused and self.current_frame is not None and self.current_detections: selected = self.target_select.currentText() frame = self.current_frame.copy() total_conf = [] for i, detection in enumerate(self.current_detections): # 获取原始检测框坐标 box = detection.boxes.xyxy[0].cpu().numpy().astype(int) # 应用缩放比例 scaled_box = [ int(box[0] * self.scale_x), int(box[1] * self.scale_y), int(box[2] * self.scale_x), int(box[3] * self.scale_y) ] conf = detection.boxes.conf.item() cls_id = detection.boxes.cls.item() class_name = self.model.names[int(cls_id)] label = f"{class_name} {conf:.2f}" # 判断是否选中该目标 if selected == "全部": color = (0, 0, 255) # 红色显示所有目标 thickness = 2 cv2.rectangle(frame, (scaled_box[0], scaled_box[1]), (scaled_box[2], scaled_box[3]), color, thickness) cv2.putText(frame, label, (scaled_box[0], scaled_box[1] - 10), cv2.FONT_HERSHEY_SIMPLEX, 0.5, color, thickness) total_conf.append(conf) elif selected == str(i + 1): color = (0, 255, 0) # 绿色高亮选中目标 thickness = 2 cv2.rectangle(frame, (scaled_box[0], scaled_box[1]), (scaled_box[2], scaled_box[3]), color, thickness) cv2.putText(frame, label, (scaled_box[0], scaled_box[1] - 10), cv2.FONT_HERSHEY_SIMPLEX, 0.5, color, thickness) total_conf.append(conf) # 更新坐标信息 x_center = (scaled_box[0] + scaled_box[2]) // 2 y_center = (scaled_box[1] + scaled_box[3]) // 2 self.x_label.setText(str(x_center)) self.y_label.setText(str(y_center)) if selected == "全部": avg_conf = sum(total_conf) / len(total_conf) if total_conf else 0 self.confidence_label.setText(f"{avg_conf:.1%}") self.x_label.setText("N/A") self.y_label.setText("N/A") elif total_conf: self.confidence_label.setText(f"{total_conf[0]:.1%}") # 显示处理后的帧 h, w, ch = frame.shape q_img = QtGui.QImage(frame.data, w, h, ch * w, QtGui.QImage.Format_RGB888) self.video_label.setPixmap(QtGui.QPixmap.fromImage(q_img)) def save_results(self): # 保存结果 if self.video_label.pixmap(): save_dir = os.path.join("result", time.strftime("%Y%m%d_%H%M%S")) os.makedirs(save_dir, exist_ok=True) # 保存图片 image_path = os.path.join(save_dir, "capture.jpg") self.video_label.pixmap().save(image_path) # 保存信息 info_path = os.path.join(save_dir, "info.txt") with open(info_path, "w") as f: f.write(f"置信度: {self.confidence_label.text()}\n") f.write(f"目标数量: {self.target_count_label.text()}\n") f.write(f"X坐标: {self.x_label.text()}\n") f.write(f"Y坐标: {self.y_label.text()}\n") QtWidgets.QMessageBox.information(self, "保存成功", f"数据已保存至: {save_dir}") if __name__ == "__main__": app = QtWidgets.QApplication() window = MainWindow() window.show() app.exec()
 
                    
                     
                    
                 
                    
                
 
                
            
         
         浙公网安备 33010602011771号
浙公网安备 33010602011771号