2025.2.24

今天对页面进行修改并且加上一些实际功能:具体页面如下:

 代码如下:

import os
import shutil
import sys
import cv2
import mediapipe as mp
import numpy as np
from datetime import datetime
from PySide6.QtCore import Qt, QUrl, QTimer, QThread, Signal
from PySide6.QtGui import QImage, QPixmap
from PySide6.QtWidgets import (
    QApplication, QMainWindow, QPushButton, QVBoxLayout, QHBoxLayout,
    QWidget, QComboBox, QLabel, QFileDialog, QFrame, QTextEdit,
    QInputDialog, QMessageBox
)
from PySide6.QtMultimedia import QMediaPlayer
from docx import Document

# MediaPipe 初始化
mp_pose = mp.solutions.pose
mp_drawing = mp.solutions.drawing_utils


class VideoProcessor(QThread):
    frame_processed = Signal(QImage)
    finished = Signal()

    def __init__(self, video_path):
        super().__init__()
        self.video_path = video_path
        self._is_running = True
        self._is_paused = False
        self.cap = None

    def run(self):
        self.cap = cv2.VideoCapture(self.video_path)
        if not self.cap.isOpened():
            print("Error: Could not open video.")
            self.finished.emit()
            return

        pose = mp_pose.Pose(
            min_detection_confidence=0.5,
            min_tracking_confidence=0.5
        )

        while self._is_running and self.cap.isOpened():
            if not self._is_paused:
                ret, frame = self.cap.read()
                if not ret:
                    break

                # 关键点检测
                results = pose.process(cv2.cvtColor(frame, cv2.COLOR_BGR2RGB))
                if results.pose_landmarks:
                    mp_drawing.draw_landmarks(
                        frame,
                        results.pose_landmarks,
                        mp_pose.POSE_CONNECTIONS,
                        mp_drawing.DrawingSpec(color=(245, 117, 66), thickness=2, circle_radius=2),  # 关键点样式
                        mp_drawing.DrawingSpec(color=(245, 66, 230), thickness=2)  # 连接线样式
                    )

                # 转换图像格式
                rgb_image = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
                h, w, ch = rgb_image.shape
                bytes_per_line = ch * w
                qt_image = QImage(rgb_image.data, w, h, bytes_per_line, QImage.Format.Format_RGB888)
                self.frame_processed.emit(qt_image)

        self.cap.release()
        self.finished.emit()

    def stop(self):
        self._is_running = False
        if self.cap:
            self.cap.release()

    def toggle_pause(self):
        self._is_paused = not self._is_paused


class VideoPlayer(QLabel):
    def __init__(self):
        super().__init__()
        self.setAlignment(Qt.AlignmentFlag.AlignCenter)
        self.setStyleSheet("background-color: black;")
        self.processor = None

    def openFile(self, filename):
        if self.processor:
            self.processor.stop()
            self.processor.wait()  # 确保线程完全停止

        self.processor = VideoProcessor(filename)
        self.processor.frame_processed.connect(self.update_frame)
        self.processor.finished.connect(self.on_processing_finished)
        self.processor.start()

    def update_frame(self, image):
        self.setPixmap(QPixmap.fromImage(image).scaled(
            self.width(), self.height(),
            Qt.AspectRatioMode.KeepAspectRatio
        ))

    def playPause(self):
        if self.processor:
            self.processor.toggle_pause()

    def stop(self):
        if self.processor:
            self.processor.stop()
            self.processor.wait()  # 确保线程完全停止

    def on_processing_finished(self):
        self.setPixmap(QPixmap())  # 清空播放区域


class MainApp(QMainWindow):
    def __init__(self):
        super().__init__()
        self.setWindowTitle("人机交互界面")
        self.setGeometry(100, 100, 1700, 880)
        self.video_folder = "videos"
        self.selected_video = None

        # 检查并创建视频目录
        if not os.path.exists(self.video_folder):
            os.makedirs(self.video_folder)

        self.initUI()

        # 设置背景渐变
        self.setStyleSheet("""
            QMainWindow {
                background: linear-gradient(to bottom, #4343FF, #ADADFF);
                background-repeat: no-repeat;
                background-position: center;
                background-size: cover;
            }
        """)

    def initUI(self):
        main_widget = QWidget()
        main_layout = QVBoxLayout()

        # 标题栏
        title_label = QLabel("乒乓球运动预测以及指导建议")
        title_label.setAlignment(Qt.AlignmentFlag.AlignCenter)
        title_label.setStyleSheet("font-size: 30px; font-weight: bold; margin: 15px; color: black;")

        # 顶部布局
        top_layout = QHBoxLayout()
        top_layout.addWidget(title_label)
        top_layout.setContentsMargins(0, 0, 0, 0)

        # 左侧整体布局
        left_layout = QVBoxLayout()
        left_layout.setContentsMargins(5, 5, 5, 5)
        left_layout.setSpacing(10)

        # 中间视频播放区域
        self.video_player = VideoPlayer()

        # 时间显示
        self.time_label = QLabel()
        self.time_label.setAlignment(Qt.AlignmentFlag.AlignCenter)
        self.update_time()
        timer = QTimer(self)
        timer.timeout.connect(self.update_time)
        timer.start(1000)
        left_layout.addWidget(self.time_label)

        # 控制按钮行
        control_layout = QHBoxLayout()
        btn_import = QPushButton("导入视频")
        btn_import.setStyleSheet("""
            QPushButton {
                background-color: #00d4ff;
                color: white;
                border-radius: 10px;
                padding: 10px 20px;
                font-size: 16px;
            }
            QPushButton:hover { background-color: #0056b3; }
            QPushButton:pressed { background-color: #003d7a; }
        """)
        btn_import.clicked.connect(self.import_perspective)

        self.btn_pause = QPushButton("暂停/继续")
        self.btn_pause.setStyleSheet(btn_import.styleSheet())
        self.btn_pause.clicked.connect(self.video_player.playPause)

        btn_detect = QPushButton("检测当前帧")
        btn_detect.setStyleSheet(btn_import.styleSheet())

        control_layout.addWidget(btn_import)
        control_layout.addWidget(self.btn_pause)
        control_layout.addWidget(btn_detect)
        left_layout.addLayout(control_layout)

        # 视频选择下拉框
        self.combo_videos = QComboBox()
        self.combo_videos.currentIndexChanged.connect(self.load_video)
        self.populate_video_list()
        left_layout.addWidget(QLabel("选择视角:"))
        left_layout.addWidget(self.combo_videos)

        # 数据展示框
        data_frame = QFrame()
        data_frame.setStyleSheet("""
            background-color: rgba(192,191,192,100); 
            border-radius: 10px; 
            padding: 15px; 
            color: black;
        """)
        data_layout = QVBoxLayout()
        data_layout.addWidget(QLabel("检测结果", alignment=Qt.AlignmentFlag.AlignCenter))
        data_layout.addWidget(QLabel("X坐标: 0"))
        data_layout.addWidget(QLabel("Y坐标: 0"))
        data_layout.addWidget(QLabel("球拍倾斜角度: 0°"))
        data_layout.addWidget(QLabel("置信度: 0.0"))
        data_layout.addWidget(QLabel("击打成功几率: 0.0"))
        data_frame.setLayout(data_layout)
        left_layout.addWidget(data_frame)

        # 建议框和报告按钮
        self.txt_suggestions = QTextEdit()
        self.txt_suggestions.setPlaceholderText("分析建议将显示在这里...")
        self.txt_suggestions.setStyleSheet("""
            background-color: rgba(255,255,255,180); 
            border: 1px solid #ccc; 
            padding: 10px; 
            border-radius: 5px;
        """)
        left_layout.addWidget(QLabel("分析建议:"))
        left_layout.addWidget(self.txt_suggestions)

        btn_report = QPushButton("生成分析报告")
        btn_report.setStyleSheet(btn_import.styleSheet())
        btn_report.clicked.connect(self.generate_report)
        left_layout.addWidget(btn_report)

        # 右侧视图布局
        right_layout = QVBoxLayout()
        right_layout.setSpacing(10)

        side_view = QLabel("轨迹侧视图")
        side_view.setStyleSheet("""
            background-color: rgba(255,255,255,180);
            border-radius: 10px;
            border: 1px solid #ccc;
            min-height: 300px;
        """)
        top_view = QLabel("轨迹俯视图")
        top_view.setStyleSheet(side_view.styleSheet())

        right_layout.addWidget(side_view)
        right_layout.addWidget(top_view)

        # 主界面布局
        main_content = QHBoxLayout()
        main_content.addLayout(left_layout, 25)
        main_content.addWidget(self.video_player, 45)
        main_content.addLayout(right_layout, 30)

        main_layout.addLayout(top_layout)
        main_layout.addLayout(main_content)

        main_widget.setLayout(main_layout)
        self.setCentralWidget(main_widget)

    def update_time(self):
        current_date = datetime.now().strftime("%Y-%m-%d")
        day_of_week = self.get_day_of_week()
        self.time_label.setText(f"{current_date}\n星期{day_of_week}")

    def get_day_of_week(self):
        day_map = ["", "", "", "", "", "", ""]
        return day_map[datetime.now().weekday()]

    def import_perspective(self):
        file_path, _ = QFileDialog.getOpenFileName(
            self, "选择视频文件", "", "视频文件 (*.mp4 *.avi *.mov)")
        if file_path:
            name, ok = QInputDialog.getText(
                self, "输入视角名称", "请为此视角命名:")
            if ok and name:
                ext = os.path.splitext(file_path)[1]
                save_path = os.path.join(self.video_folder, f"{name}{ext}")
                shutil.copy(file_path, save_path)
                self.populate_video_list()

    def populate_video_list(self):
        self.combo_videos.clear()
        videos = [f for f in os.listdir(self.video_folder)
                  if f.endswith(('.mp4', '.avi', '.mov'))]
        self.combo_videos.addItems(videos)

    def load_video(self):
        if self.combo_videos.count() > 0:
            video_name = self.combo_videos.currentText()
            video_path = os.path.join(self.video_folder, video_name)
            self.video_player.openFile(video_path)

    def generate_report(self):
        doc = Document()
        doc.add_heading('运动分析报告', 0)
        doc.add_paragraph(f"生成时间:{datetime.now().strftime('%Y-%m-%d %H:%M:%S')}")

        data = [
            ("视频文件", self.combo_videos.currentText()),
            ("X坐标", "0"),
            ("Y坐标", "0"),
            ("球拍角度", ""),
            ("置信度", "0.0"),
            ("击打成功几率", "0.0"),
            ("分析建议", self.txt_suggestions.toPlainText())
        ]

        for item in data:
            doc.add_paragraph(f"{item[0]}:{item[1]}")

        report_path = os.path.join(os.getcwd(), "analysis_report.docx")
        doc.save(report_path)

        QMessageBox.information(
            self,
            "报告生成成功",
            f"报告已保存至:\n{report_path}"
        )


if __name__ == "__main__":
    app = QApplication(sys.argv)
    window = MainApp()
    window.show()
    sys.exit(app.exec())

 

posted @ 2025-02-24 21:53  贾贾鱼  阅读(12)  评论(0)    收藏  举报