diff --git a/Wpf_AiSportsMicrospace/Common/SportOperate.cs b/Wpf_AiSportsMicrospace/Common/SportOperate.cs index 13afba2..3335a80 100644 --- a/Wpf_AiSportsMicrospace/Common/SportOperate.cs +++ b/Wpf_AiSportsMicrospace/Common/SportOperate.cs @@ -5,6 +5,7 @@ using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows; +using Wpf_AiSportsMicrospace.Enum; using Yztob.AiSports.Inferences.Abstractions; using Yztob.AiSports.Inferences.Things; using Yztob.AiSports.Postures; @@ -21,16 +22,17 @@ namespace Wpf_AiSportsMicrospace.Common /// public class SportOperate { - private DateTime _lastSlideTime = DateTime.MinValue; - IPointTracker _leftTracker; IPointTracker _rightTracker; IPointTracker _leftElbow; IPointTracker _rightElbow; - WebcamClient _webcamClient; - private Point? _lastWrist = null; // 上一帧的手腕位置 - private DateTime _lastActionTime = DateTime.MinValue; // 上次动作时间 - private readonly TimeSpan _cooldown = TimeSpan.FromMilliseconds(800); // 动作冷却,避免误触发 + WebcamClient _webcamClient; + + private Point? _lastLeftWrist = null; + private Point? _lastRightWrist = null; + private DateTime _lastActionTime = DateTime.MinValue; + // 记录举手开始时间 + private DateTime? _raiseStartTime = null; public SportOperate() @@ -52,88 +54,120 @@ namespace Wpf_AiSportsMicrospace.Common return _webcamClient; } - /// - /// 验证挥手动作[0:无效,1:左,2:右] - /// - /// public int VerifyWavingAction(Human human) { - var leftResult = _leftTracker.Tracking(human); - var rightResult = _rightTracker.Tracking(human); - var leftElbowResult = _leftElbow.Tracking(human); - var rightElbowResult = _rightElbow.Tracking(human); + var leftWrist = human.Keypoints.FirstOrDefault(x => x.Name == "left_wrist"); + var leftElbow = human.Keypoints.FirstOrDefault(x => x.Name == "left_elbow"); + var rightWrist = human.Keypoints.FirstOrDefault(x => x.Name == "right_wrist"); + var rightElbow = human.Keypoints.FirstOrDefault(x => x.Name == "right_elbow"); - // 节流:每 300ms 最多更新一次 UI - //if ((DateTime.Now - _lastSlideTime).TotalMilliseconds < 500) return 0; - //_lastSlideTime = DateTime.Now; - - // 根据手势结果选择滑动方向 - if (leftResult != 0 && leftElbowResult != 0) return 1; - if (rightResult != 0 & rightElbowResult != 0) + // 左手逻辑 + if (leftWrist != null && leftElbow != null) { - var wrist = human.Keypoints.Where(x => x.Name == "right_wrist").FirstOrDefault(); - var elbow = human.Keypoints.Where(x => x.Name == "right_elbow").FirstOrDefault(); - if (wrist == null || elbow == null) return 0; - return RecognizeRightHandGesture(new Point(wrist.X, wrist.Y), new Point(elbow.X, elbow.Y)); + var result = RecognizeLeftHandGesture( + new Point(leftWrist.X, leftWrist.Y), + new Point(leftElbow.X, leftElbow.Y)); + if (result != 0) return result; } - return 0; - } - - public int VerifyLiftHandAction(Human human) - { - var rightResult = _rightTracker.Tracking(human); - var rightElbowResult = _rightElbow.Tracking(human); - - // 节流:每 300ms 最多更新一次 UI - if ((DateTime.Now - _lastSlideTime).TotalMilliseconds < 500) return 0; - _lastSlideTime = DateTime.Now; - - // 根据手势结果选择滑动方向 - if (rightResult != 0 & rightElbowResult != 0) return 1; + // 右手逻辑 + if (rightWrist != null && rightElbow != null) + { + var result = RecognizeRightHandGesture( + new Point(rightWrist.X, rightWrist.Y), + new Point(rightElbow.X, rightElbow.Y)); + if (result != 0) return result; + } return 0; } - public int RecognizeRightHandGesture(Point wrist, Point elbow) + /// + /// 识别左手动作(左→右为 1) + /// + public int RecognizeLeftHandGesture(Point wrist, Point elbow) { - // 判断举手:手腕在肘部之上,且垂直距离超过一定阈值 - if (wrist.Y + 40 < elbow.Y) // 手腕比肘高 40 像素以上 + if (_lastLeftWrist != null) { - if (CheckCooldown()) - return 3; // 举手 - } + double dx = wrist.X - _lastLeftWrist.Value.X; + double dy = Math.Abs(wrist.Y - _lastLeftWrist.Value.Y); - // 需要至少两帧来判断水平移动方向 - if (_lastWrist != null) - { - double dx = wrist.X - _lastWrist.Value.X; // 水平位移 - double dy = Math.Abs(wrist.Y - _lastWrist.Value.Y); - - // 要求水平位移明显大于垂直位移,且超过一定阈值 if (Math.Abs(dx) > 30 && dy < 40) { if (CheckCooldown()) { if (dx > 0) - return 2; // 右挥手 - else - return 1; // 左挥手 + return 1; // 左手往右挥 } } } - _lastWrist = wrist; + _lastLeftWrist = wrist; return 0; } - private bool CheckCooldown() + + /// + /// 识别右手动作(右→左为 2,举手为 3) + /// + + public int RecognizeRightHandGesture(Point wrist, Point elbow) { - if (DateTime.Now - _lastActionTime > _cooldown) + // 判断手腕是否在肘部上方,并且达到高度阈值 + //if (wrist.Y + 40 < elbow.Y && wrist.Y < minRaiseHeight) + if (wrist.Y + 40 < elbow.Y) { - _lastActionTime = DateTime.Now; - return true; + if (_raiseStartTime == null) + { + _raiseStartTime = DateTime.Now; + return (int)WavingAction.FirstHand; + } + + var duration = DateTime.Now - _raiseStartTime.Value; + if (duration.TotalSeconds >= 3) + { + _raiseStartTime = null; + return (int)WavingAction.RaiseHand; // 举手完成 + } + else + { + return (int)WavingAction.Raising; // 举手中 + } } - return false; + else + { + _raiseStartTime = null; + } + + // 挥手逻辑 + if (_lastRightWrist != null) + { + double dx = wrist.X - _lastRightWrist.Value.X; + double dy = Math.Abs(wrist.Y - _lastRightWrist.Value.Y); + + if (Math.Abs(dx) > 30 && dy < 40) + { + if (CheckCooldown()) + { + if (dx < 0) + return 2; // 右手往左挥 + } + } + } + + _lastRightWrist = wrist; + return 0; + } + + /// + /// 冷却防抖(避免重复触发) + /// + private bool CheckCooldown(int cooldownMs = 500) + { + if ((DateTime.Now - _lastActionTime).TotalMilliseconds < cooldownMs) + return false; + + _lastActionTime = DateTime.Now; + return true; } } } diff --git a/Wpf_AiSportsMicrospace/Enum/WavingAction.cs b/Wpf_AiSportsMicrospace/Enum/WavingAction.cs new file mode 100644 index 0000000..323e2fd --- /dev/null +++ b/Wpf_AiSportsMicrospace/Enum/WavingAction.cs @@ -0,0 +1,19 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Wpf_AiSportsMicrospace.Enum +{ + public enum WavingAction + { + None = 0, // 没动作 + LeftWave = 1, // 左挥手 + RightWave = 2, // 右挥手 + RaiseHand = 3, // 举手完成 + Raising = 4, // 举手中,未完成 + FirstHand = 5,//首次举手 + } +} + diff --git a/Wpf_AiSportsMicrospace/Home.xaml.cs b/Wpf_AiSportsMicrospace/Home.xaml.cs index e1afa20..2be5cdd 100644 --- a/Wpf_AiSportsMicrospace/Home.xaml.cs +++ b/Wpf_AiSportsMicrospace/Home.xaml.cs @@ -5,6 +5,7 @@ using System.Collections.Concurrent; using System.Collections.Generic; using System.Collections.ObjectModel; using System.ComponentModel; +using System.Diagnostics; using System.IO; using System.Linq; using System.Text; @@ -14,6 +15,7 @@ using System.Windows.Controls; using System.Windows.Media; using System.Windows.Media.Animation; using Wpf_AiSportsMicrospace.Common; +using Wpf_AiSportsMicrospace.Enum; using Wpf_AiSportsMicrospace.MyUserControl; using Wpf_AiSportsMicrospace.Views; using Yunzhi.Database; @@ -33,11 +35,6 @@ namespace Wpf_AiSportsMicrospace public partial class Home : Window { private IHumanPredictor _humanPredictor; - private IObjectDetector _objectDetector; - private HumanGraphicsRenderer _humanGraphicsRenderer; - private readonly List _sports; - private SportBase _sport; - private readonly SportDetectionQueue _detectQueue; private WebcamClient _webcamClient; private ConcurrentQueue _frameQueue = new(); private CancellationTokenSource _cts = new(); @@ -47,12 +44,6 @@ namespace Wpf_AiSportsMicrospace { InitializeComponent(); _humanPredictor = HumanPredictorFactory.Create(HumanPredictorType.SingleHigh); - _objectDetector = ObjectDetectorFactory.CreateSportGoodsDetector(); - _humanGraphicsRenderer = new HumanGraphicsRenderer(); - _humanGraphicsRenderer.DrawLabel = false; - - //_sports = SportBase.GetSports(); - //_detectQueue = new SportDetectionQueue(); string projectRoot = Path.Combine(AppContext.BaseDirectory, @"..\..\.."); string albumPath = Path.Combine(projectRoot, "Resources", "Img", "Album"); @@ -73,7 +64,7 @@ namespace Wpf_AiSportsMicrospace coverFlow.SelectedIndex = 2; // 监听进度条完成事件 - coverFlow.ProgressCompleted += CoverFlow_ProgressCompleted; + //coverFlow.ProgressCompleted += CoverFlow_ProgressCompleted; } private void Window_Loaded(object sender, RoutedEventArgs e) @@ -93,36 +84,35 @@ namespace Wpf_AiSportsMicrospace private void CoverFlow_ProgressCompleted(CoverFlowItem item) { - // 根据不同图片跳转不同页面 - if (item.ImageUri.ToString().EndsWith("1.jpg")) + // 停止抽帧线程/释放资源 + try { - // 跳转到页面1 - Dispatcher.BeginInvoke(new Action(() => - { - GroupJumpRope groupJumpRope = new GroupJumpRope(); - groupJumpRope.Owner = Application.Current.MainWindow; - groupJumpRope.Show(); - })); + _webcamClient?.StopExtract(); // 停止抽帧 + _webcamClient = null; // 释放引用 } - else if (item.ImageUri.ToString().EndsWith("2.jpg")) + catch (Exception ex) { - // 跳转到页面2 - Dispatcher.BeginInvoke(new Action(() => - { - GroupJumpRope groupJumpRope = new GroupJumpRope(); - groupJumpRope.Owner = Application.Current.MainWindow; - groupJumpRope.Show(); - })); + Debug.WriteLine($"停止抽帧异常: {ex.Message}"); } - else if (item.ImageUri.ToString().EndsWith("3.jpg")) + + // 根据图片跳转新窗口 + string uri = item.ImageUri.ToString(); + Window newWindow = null; + + if (uri.EndsWith("1.jpg")) + newWindow = new GroupJumpRope(); + else if (uri.EndsWith("2.jpg")) + newWindow = new GroupJumpRope(); + else if (uri.EndsWith("3.jpg")) + newWindow = new GroupJumpRope(); + + if (newWindow != null) { - // 跳转到页面3 - Dispatcher.BeginInvoke(new Action(() => + Dispatcher.BeginInvoke(() => { - GroupJumpRope groupJumpRope = new GroupJumpRope(); - groupJumpRope.Owner = Application.Current.MainWindow; - groupJumpRope.Show(); - })); + newWindow.Show(); // 先显示新窗口 + this.Close(); // 再关闭当前窗口 + }); } } @@ -169,31 +159,27 @@ namespace Wpf_AiSportsMicrospace //检测挥手动作 var wavingaction = _sportOperate.VerifyWavingAction(human); - switch (wavingaction) { - case 1: + case (int)WavingAction.LeftWave: // 1 Dispatcher.BeginInvoke(() => coverFlow.SlideLeft()); break; - case 2: + case (int)WavingAction.RightWave: // 2 Dispatcher.BeginInvoke(() => coverFlow.SlideRight()); break; - case 3: + case (int)WavingAction.RaiseHand: // 3 举手完成 + coverFlow.ProgressCompleted += CoverFlow_ProgressCompleted; + break; + case (int)WavingAction.Raising: // 4 举手中 + break; + case 5: // 如果还有其他动作 Dispatcher.BeginInvoke(() => coverFlow.StartSelectedProgress()); break; default: + Dispatcher.BeginInvoke(() => coverFlow.CancelSelectedProgress()); break; } - ////检测举手动作 - //var liftHandAction = _sportOperate.VerifyLiftHandAction(human); - - //// 根据手势结果 - //if (liftHandAction == 1) - //{ - // // 启动进度条动画; - // Dispatcher.BeginInvoke(() => coverFlow.StartSelectedProgress()); - //} } catch (Exception ex) { diff --git a/Wpf_AiSportsMicrospace/MyUserControl/CoverFlowControl1.xaml.cs b/Wpf_AiSportsMicrospace/MyUserControl/CoverFlowControl1.xaml.cs index b08cd03..2f791fa 100644 --- a/Wpf_AiSportsMicrospace/MyUserControl/CoverFlowControl1.xaml.cs +++ b/Wpf_AiSportsMicrospace/MyUserControl/CoverFlowControl1.xaml.cs @@ -81,11 +81,24 @@ namespace Wpf_AiSportsMicrospace.MyUserControl { if (SelectedIndex >= 0 && SelectedIndex < Images.Count) { - var current = Images[SelectedIndex]; + var current = Images[SelectedIndex]; + if (current.Progress >= 1) return; // 已完成,直接返回,不再启动 StartProgress(current); } } + public void CancelSelectedProgress() + { + if (SelectedIndex >= 0 && SelectedIndex < Images.Count) + { + var current = Images[SelectedIndex]; + // 停止动画/计时 + StopProgress(); + + // 清零当前的进度值 + current.Progress = 0; + } + } public void StartProgress(CoverFlowItem item) { StopProgress(); @@ -257,7 +270,15 @@ namespace Wpf_AiSportsMicrospace.MyUserControl return null; } - public void SlideLeft() => SelectedIndex++; - public void SlideRight() => SelectedIndex--; + public void SlideLeft() + { + StopProgress(); + SelectedIndex++; + } + public void SlideRight() + { + StopProgress(); + SelectedIndex--; + } } }