2025.3.6
昨天课上检查了外包杯的第一阶段,在第一阶段里我们组只是完成了一些基础的页面部分,外包杯赛题如下:
【A01】基于百度飞桨和文心大模型的智能乒乓球运动分析与可视化系统设计【百度】 发布时间: 2024-12-12 16:02:30 1.命题方向 智能计算 2.题目类别 应用类 3.题目名称 基于百度飞桨和文心大模型的智能乒乓球运动分析与可视化系统设计 4.背景说明 【整体背景】 随着人工智能技术的快速发展,AI在体育领域的应用逐步拓展,不仅在专业训练、技术分析、战术制定等方面为运动员提供支持,还为业余爱好者的运动体验带来了显著提升。通过视觉识别、动作分析和跨模态整合研究,AI技术能够帮助运动员提升技术细节、减少训练中的错误动作和运动损伤,同时,也能增强乒乓球运动的互动性和趣味性,提升业余爱好者的运动体验。 在诸多运动中,乒乓球运动对运动员的技术细腻度和反应速度有较高要求,如何实现精细化的运动员位置与动作分析、球的轨迹追踪,以及结合大模型技术实现比赛技战术的智能分析等要求,对智能系统设计者与开发者,都提出了新的挑战。 基于此背景,我们聚焦乒乓球运动这一“国球”,希望选手设计一个乒乓球运动分析与可视化系统,通过深度学习、大模型、可视化和软件工程技术,实现对乒乓球运动员位置/动作的精准识别、实时反馈和信息可视化展示。 【公司背景】 百度是拥有强大互联网基础的领先AI公司,是全球为数不多的提供AI芯片、软件架构和应用程序等全栈AI技术的公司之一,被国际机构评为全球四大AI公司之一。百度以“用科技让复杂的世界更简单”为使命,坚持技术创新,致力于“成为最懂用户,并能帮助人们成长的全球顶级高科技公司”。百度以技术创新为信仰,在创新投入、研发布局、人才引进方面均走在国际前列。2020年,百度核心研发费用占收入比例达21.4%,研发投入强度位于中国大型科技互联网公司前列。百度全球AI专利申请量已超过1万件,其中中国专利9000多件,位列中国第一,并在深度学习技术、智能语音、自然语言处理、自动驾驶、知识图谱、智能推荐等多个领域排名国内第一。 【业务背景】 飞桨(PaddlePaddle)是由百度研发的开源深度学习平台,是中国首个自主研发、功能丰富、开源开放的产业级深度学习平台。 文心(ERNIE)是百度自主研发的产业级知识增强大模型,以创新性的知识增强技术为核心,从单模态大模型到跨模态、从通用基础大模型到跨领域、跨行业持续创新突破,构建了模型层、工具与平台层,大幅降低人工智能开发和应用门槛,加快人工智能大规模产业化进程并拓展人工智能技术边界。2024年8月22日消息,文心大模型日均调用量超6亿次,日均处理Tokens文本约1万亿,均为国内最高。 近些年来,百度通过AI技术为多个体育国家队提供科学训练支持。早在2019年,百度就和中国国家跳水队开展技术合作,研发智能跳水辅助训练系统,为运动员科学训练提供技术支撑。在游泳、田径、攀岩、体操、蹦床等领域,也都有百度AI技术的身影。除了赛场上,更多的体育+AI技术也在走进寻常百姓家,让更多人享受到科技带来的帮助,促进全民健身,助力健康中国、体育强国。 5.项目说明 【问题说明】 (1)动作姿势识别方面:乒乓球运动员的位置移动灵活,动作快速且细节多变,对系统是否能准确识别人物位置、球拍角度、手臂姿势和击球时的姿态等都提出了挑战。 (2)可视分析方面:基于纯视觉识别准确跟踪乒乓球和运动员动作,生成连续帧的可视化效果,展示各类重要的轨迹、运动标签和属性,增强可视化分析的直观性。 (3)运动效果分析方面:对用户的训练/运动进展,生成可视化报告并提供持续的技术改进建议。 【用户期望】 面向体育领域, 我们希望选手能够基于飞桨和文心大模型的能力,发挥其在感知、理解和生成等方面的能力优势,打造出面向乒乓球运动场景的运动分析与可视化系统设计。 6.任务要求 【开发说明】 本命题产品要求选手深入理解乒乓球运动场景,发挥纯视觉感知和大语言模型的技术优势,打造可以云端或本地部署的软件平台。 【技术要求与指标】 (1)视觉感知技术的使用要求: ①非接触式的纯视觉感知技术:不得使用非视觉,如穿戴式动作捕捉系统等接触式数据采集系统; ②深度学习框架要求:涉及到深度学习模型的训练与部署部分,要求使用百度飞桨PaddlePaddle深度学习平台; ③算法能力要求:百度飞桨会围绕乒乓球动作标签识别,提供可用于训练的数据集、基线和算法评测系统,助力选手构建模型并进行算法的自动化评测; ④部署要求:要求模型能够部署在云端或设备终端。 (2)大模型技术的使用要求:要求通过百度千帆进行文心大模型的prompt设计、微调和部署调用。 (3)可视分析要求:根据乒乓球和运动员动作,生成连续帧的可视化效果,展示各类重要的轨迹、运动标签和属性,增强可视化分析的直观性。 (4)外部数据的融合要求:除了利用感知结果与大模型本身的知识,还可以充分利用外部运动学、营养学和医学等相关数据,进行微调或RAG的设计,增强分析或问答结果的丰富性。 【提交材料】 (1)项目概要介绍; (2)项目简介 PPT; (3)项目详细方案; (4)项目演示视频; (5)企业要求提交的材料: ①产品使用手册:包括产品功能架构、使用流程图和典型学习示例; ②产品交互演示:对产品的交互过程进行录制,能够在实际乒乓球运动场中试验更佳; ③项目的详细分工及过程文档:对团队成员的角色、分工、排期和过程进行记录。 (6)团队自愿提交的其他补充材料。 【任务清单】 包括但不限于以下功能: (1)乒乓球轨迹及运动员动作识别与分析; (2)动作可视化分析与数据展示; (3)训练指标数据的可视化与智能分析; (4)用户数据的系统化管理; (5)个性化训练建议报告生成; (6)跨平台支持,手机端和电脑端等多端配合; (7)实时采集与分析(可选); (8)其他拓展功能和创新方向,如软硬一体解决方案。 【开发工具与数据接口】 大模型能力调用指定平台:要求通过千帆SDK进行文心大模型的调用。 深度学习平台:PaddlePaddle 7. 其他 数据集和评测系统将在2025年1月建设完毕并进行开放。 8. 参考信息 飞桨:https://www.paddlepaddle.org.cn/ 千帆:https://qianfan.cloud.baidu.com/ 9. 评分要点 赛题评分要点见附件一:A 类企业命题初赛统一评分标准。 注:部分赛题会提供数据或其他资料,除《赛题手册》中已提供的外,后续如有企业提供均更新在该链接内:http://www.fwwb.org.cn/news/show/535
对于此题目我们的最初想法是调用官方指定的大模型飞桨和千帆来完成对乒乓球运动轨迹进行预测、对运动员进行动作分析等内容,我们完成了前端程序页面的搭建,我们将需要展示的内容框放置在了页面上,例如视频的播放框、分析内容、建议展示框等等,具体代码和页面如下:
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.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())


浙公网安备 33010602011771号