diff --git a/Wpf_AiSportsMicrospace/Enum/GameState.cs b/Wpf_AiSportsMicrospace/Enum/GameState.cs new file mode 100644 index 0000000..13f2ef6 --- /dev/null +++ b/Wpf_AiSportsMicrospace/Enum/GameState.cs @@ -0,0 +1,13 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace Enum +{ + public enum GameState + { + NotStarted, // 游戏未开始 + Running, // 游戏进行中 + Finished // 游戏完成 + } +} diff --git a/Wpf_AiSportsMicrospace/Views/Home.xaml.cs b/Wpf_AiSportsMicrospace/Views/Home.xaml.cs index 87d334e..e471f96 100644 --- a/Wpf_AiSportsMicrospace/Views/Home.xaml.cs +++ b/Wpf_AiSportsMicrospace/Views/Home.xaml.cs @@ -64,14 +64,15 @@ namespace Wpf_AiSportsMicrospace { _mainWin.HumanFrameUpdated -= OnHumanFrameUpdated; } - + private bool IsGestureActive = true; // 标记手势识别是否有效 private void OnHumanFrameUpdated(object sender, List humans) { - var human = humans.FirstOrDefault(); + if (!IsGestureActive) return; // 手势识别已停止,直接返回 + var human = humans.FirstOrDefault(); if (human == null) return; - //检测挥手动作 + // 检测挥手动作 var wavingaction = _mainWin.SportOperate.VerifyWavingAction(humans); switch (wavingaction) @@ -93,6 +94,9 @@ namespace Wpf_AiSportsMicrospace case (int)WavingAction.RaiseHand: // 举手完成 coverFlow.ProgressCompleted += CoverFlow_ProgressCompleted; + + // 设置标记,停止后续手势识别 + IsGestureActive = false; break; default: // 没有动作 → 取消进度 diff --git a/Wpf_AiSportsMicrospace/Views/JumpRope/GroupJumpRope.xaml.cs b/Wpf_AiSportsMicrospace/Views/JumpRope/GroupJumpRope.xaml.cs index ad54860..ebc0203 100644 --- a/Wpf_AiSportsMicrospace/Views/JumpRope/GroupJumpRope.xaml.cs +++ b/Wpf_AiSportsMicrospace/Views/JumpRope/GroupJumpRope.xaml.cs @@ -1,5 +1,6 @@ using Dto; using Emgu.CV.Flann; +using Enum; using HandyControl.Controls; using SharpDX.Direct3D9; using System; @@ -49,18 +50,20 @@ namespace Wpf_AiSportsMicrospace.Views private MediaPlayer _mediaPlayer = new MediaPlayer(); List RankingItemList = new(); + private readonly SportDetectionQueue _detectQueue; public event PropertyChangedEventHandler PropertyChanged; protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null) { PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); } - private bool IsGameStarted = false; + private GameState _currentGameState = GameState.NotStarted; private readonly object _updateLock = new object(); private readonly Dictionary _jumpStatus = new Dictionary(); private GroupJumpRopeContext _groupJumpRopeContext; public GroupJumpRope() { InitializeComponent(); + _detectQueue = new SportDetectionQueue(); Loaded += UserControl_Loaded; Unloaded += UserControl_Unloaded; _groupJumpRopeContext = new GroupJumpRopeContext(); @@ -163,46 +166,45 @@ namespace Wpf_AiSportsMicrospace.Views { try { - if (!IsGameStarted) + if (humans == null || humans.Count == 0) return; + switch (_currentGameState) { - if (humans == null || humans.Count == 0) return; + case GameState.NotStarted: // 未开始 + int rightWaving = DetectRightHandRaise(humans); + if (rightWaving >= 3) + { + switch (rightWaving) + { + case (int)WavingAction.FirstHand: + StartCountdown(3); + break; + case (int)WavingAction.Raising: + UpdateCountdown(); + break; + case (int)WavingAction.RaiseHand: + FinishCountdown(); + _currentGameState = GameState.Running; // 倒计时完成 → 游戏开始 + break; + } + } + break; - int wavingaction = 0; + case GameState.Running: // 游戏进行中 + UpdateCircleCounts(humans); - wavingaction = DetectRightHandRaise(humans); + // 可选:判断游戏结束条件,将状态切换到 Finished + // if (CheckGameFinished()) _currentGameState = GameState.Finished; + break; - if (wavingaction < 3) - return; - - switch (wavingaction) - { - case (int)WavingAction.FirstHand: - // 第一次举手,初始化倒计时 - StartCountdown(3); - break; - - case (int)WavingAction.Raising: - // 持续倒计时中 - UpdateCountdown(); - break; - - case (int)WavingAction.RaiseHand: - // 举手完成,倒计时结束 - FinishCountdown(); - break; - - default: - // 没检测到动作,重置倒计时显示 - Utils.StopBackgroundMusic(); - countdownText.Text = "3"; - countdownGrid.Visibility = Visibility.Hidden; - break; - } - - } - else - { - UpdateCircleCounts(humans); + case GameState.Finished: // 游戏完成 + int leftWaving = DetectLeftHandRaise(humans); + if (leftWaving == 5) + { + // 举左手逻辑,例如结束动画或退出 + var newPage = new Home(); + _mainWin?.SwitchPageWithMaskAnimation(newPage, true); + } + break; } } catch (Exception ex) @@ -210,6 +212,7 @@ namespace Wpf_AiSportsMicrospace.Views Console.WriteLine("OnFrameExtracted error: " + ex.Message); } } + private int _currentCountdown = 3; private DateTime _lastUpdateTime = DateTime.Now; private void StartCountdown(int start = 3) @@ -246,11 +249,9 @@ namespace Wpf_AiSportsMicrospace.Views { countdownText.Text = "GO!"; countdownGrid.Visibility = Visibility.Hidden; - IsGameStarted = true; // 播放背景音乐(循环) Utils.PlayBackgroundMusic("homeprojectselected1.mp3", true); - // 启动60秒倒计时(独立任务) StartGameCountdown(60); } @@ -273,8 +274,9 @@ namespace Wpf_AiSportsMicrospace.Views RankingItemList = _groupJumpRopeContext.UpdateRankList(); ShowRankingBoard(RankingItemList); - IsGameStarted = false; Utils.StopBackgroundMusic(); + + _currentGameState = GameState.Finished; } private DateTime? _raiseStartTime; @@ -367,6 +369,46 @@ namespace Wpf_AiSportsMicrospace.Views // 没有任何中心区域内的人举手 return (int)WavingAction.None; } + public int DetectLeftHandRaise(List humans) + { + if (humans == null || humans.Count == 0) + return (int)WavingAction.None; + + foreach (var human in humans) + { + if (human?.Keypoints == null) + continue; + + // --- 筛选右脚踝坐标 --- + var rightAnkle = human.Keypoints.FirstOrDefault(k => k.Name == "right_ankle"); + if (rightAnkle == null) + continue; + double xNorm = rightAnkle.X / 1920; + double yNorm = rightAnkle.Y / 1080; + // 仅检测中心区域内的人 + if (!(xNorm >= 0.44 && xNorm <= 0.57 && yNorm >= 0.81)) + continue; + + + // 获取左手关键点 + var leftWrist = human.Keypoints.FirstOrDefault(k => k.Name == "left_wrist"); + var leftElbow = human.Keypoints.FirstOrDefault(k => k.Name == "left_elbow"); + + if (leftWrist == null || leftElbow == null) + continue; + + const double raiseThreshold = 60; // 举手阈值 + + // 判断左手是否举起 + double verticalRise = leftWrist.Y - leftElbow.Y; + if (verticalRise >= raiseThreshold) + { + return (int)WavingAction.RaiseHand; // 一旦检测到左手举起,立即返回 + } + } + + return (int)WavingAction.None; // 没有检测到举手 + } private void ResetRaiseState() { @@ -529,7 +571,8 @@ namespace Wpf_AiSportsMicrospace.Views number3.Text = list[2].Number.ToString(); foreach (var item in list) { - var grid = new Grid { + var grid = new Grid + { Width = 1344, Height = 71, HorizontalAlignment = HorizontalAlignment.Center, @@ -549,8 +592,8 @@ namespace Wpf_AiSportsMicrospace.Views Foreground = new SolidColorBrush((Color)ColorConverter.ConvertFromString("#999999")), HorizontalAlignment = HorizontalAlignment.Left, VerticalAlignment = VerticalAlignment.Center, - TextAlignment= TextAlignment.Center, - Width=200 + TextAlignment = TextAlignment.Center, + Width = 200 }; grid.Children.Add(rankText); diff --git a/Wpf_AiSportsMicrospace/Views/Main.xaml.cs b/Wpf_AiSportsMicrospace/Views/Main.xaml.cs index 660da53..0c62839 100644 --- a/Wpf_AiSportsMicrospace/Views/Main.xaml.cs +++ b/Wpf_AiSportsMicrospace/Views/Main.xaml.cs @@ -30,6 +30,7 @@ namespace Wpf_AiSportsMicrospace.Views private HumanGraphicsRenderer _humanGraphicsRenderer; public SportOperate SportOperate { get; private set; } public WebcamClient WebcamClient { get; private set; } + private readonly SportDetectionQueue _detectQueue; private readonly ConcurrentQueue _frameQueue = new(); private readonly CancellationTokenSource _cts = new(); private DateTime _lastFrameTime = DateTime.Now; @@ -52,6 +53,7 @@ namespace Wpf_AiSportsMicrospace.Views _humanPredictor = HumanPredictorFactory.Create(HumanPredictorType.MultiMedium); _humanGraphicsRenderer = new HumanGraphicsRenderer(); _humanGraphicsRenderer.DrawLabel = false; + _detectQueue = new SportDetectionQueue(); SportOperate = new SportOperate(); WebcamClient = SportOperate.CreateRTSP(); @@ -108,6 +110,10 @@ namespace Wpf_AiSportsMicrospace.Views var humans = humanResult?.Humans?.ToList(); if (humans == null || humans.Count == 0) return; + foreach (var human in humans) + { + _detectQueue.Enqueue(frame.Number, human, null); + } // 触发全局事件 Application.Current.Dispatcher.BeginInvoke(() => @@ -261,7 +267,7 @@ namespace Wpf_AiSportsMicrospace.Views newPage.BeginAnimation(OpacityProperty, fadeAnim); } - public void SwitchPageWithMaskAnimation(UserControl newPage, bool fromRight, double durationSeconds = 1.2) + public void SwitchPageWithMaskAnimation(UserControl newPage, bool fromRight, double durationSeconds = 1.2) { if (MainContent.Content == newPage) return;