From 589678b324ad23b8dffae133b4ffde68a158ab13 Mon Sep 17 00:00:00 2001 From: tanglong <842690096@qq.com> Date: Sun, 12 Oct 2025 10:20:08 +0800 Subject: [PATCH] tl --- Wpf_AiSportsMicrospace/App.xaml | 2 +- Wpf_AiSportsMicrospace/App.xaml.cs | 6 +- Wpf_AiSportsMicrospace/Common/SportOperate.cs | 21 +- Wpf_AiSportsMicrospace/Views/CenterHome.xaml | 2 +- .../Views/CenterHome.xaml.cs | 116 +++++---- Wpf_AiSportsMicrospace/Views/Home.xaml | 2 +- Wpf_AiSportsMicrospace/Views/Home.xaml.cs | 149 +++++++++--- .../Views/JumpRope/GroupJumpRope.xaml | 2 +- .../Views/JumpRope/GroupJumpRope.xaml.cs | 61 +++-- Wpf_AiSportsMicrospace/Views/Main.xaml.cs | 229 ++++++++++++------ .../Wpf_AiSportsMicrospace.csproj | 2 +- 11 files changed, 411 insertions(+), 181 deletions(-) diff --git a/Wpf_AiSportsMicrospace/App.xaml b/Wpf_AiSportsMicrospace/App.xaml index e299b3f..d73e5b9 100644 --- a/Wpf_AiSportsMicrospace/App.xaml +++ b/Wpf_AiSportsMicrospace/App.xaml @@ -2,7 +2,7 @@ xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="clr-namespace:Wpf_AiSportsMicrospace.Views" - StartupUri="Views/Home.xaml"> + StartupUri="Views/Main.xaml"> diff --git a/Wpf_AiSportsMicrospace/App.xaml.cs b/Wpf_AiSportsMicrospace/App.xaml.cs index c0a947f..1a07e67 100644 --- a/Wpf_AiSportsMicrospace/App.xaml.cs +++ b/Wpf_AiSportsMicrospace/App.xaml.cs @@ -16,9 +16,9 @@ public partial class App : Application base.OnStartup(e); #if DEBUG - // 初始化 DebugTracker - DebugTracker.Enabled = true; - DebugTracker.Channels.Add(new DiagnosisDebugTrackChannel()); + // 初始化 DebugTracker + DebugTracker.Enabled = true; + DebugTracker.Channels.Add(new DiagnosisDebugTrackChannel()); #endif } diff --git a/Wpf_AiSportsMicrospace/Common/SportOperate.cs b/Wpf_AiSportsMicrospace/Common/SportOperate.cs index e308105..d340b89 100644 --- a/Wpf_AiSportsMicrospace/Common/SportOperate.cs +++ b/Wpf_AiSportsMicrospace/Common/SportOperate.cs @@ -54,21 +54,30 @@ namespace Wpf_AiSportsMicrospace.Common public WebcamClient CreateRTSP() { - _webcamClient = WebcamClient.CreateRTSP("192.168.3.64", "admin", "yd708090", 554u); + try + { + _webcamClient = WebcamClient.CreateRTSP("192.168.3.64", "admin", "yd708090", 554u); + } + catch (Exception) + { + HandyControl.Controls.MessageBox.Show("摄像头网络异常,请重新连接!"); + } + return _webcamClient; } public int VerifyWavingAction(Human human) { - - var nose = human.Keypoints.FirstOrDefault(k => k.Name == "nose"); + var nose = human.Keypoints.FirstOrDefault(k => k.Name == "right_ankle"); if (nose == null) - return 0; + return (int)WavingAction.None; // 使用 Canvas 宽度归一化 double xNorm = nose.X / 1920; - if (!(xNorm >= 0.44 && xNorm <= 0.57)) - return 0; + double yNorm = nose.Y / 1080; + + if (!(xNorm >= 0.44 && xNorm <= 0.57 && yNorm >= 0.81)) + return (int)WavingAction.None; var leftWrist = human.Keypoints.FirstOrDefault(x => x.Name == "left_wrist"); var leftElbow = human.Keypoints.FirstOrDefault(x => x.Name == "left_elbow"); diff --git a/Wpf_AiSportsMicrospace/Views/CenterHome.xaml b/Wpf_AiSportsMicrospace/Views/CenterHome.xaml index 78a778d..e53443c 100644 --- a/Wpf_AiSportsMicrospace/Views/CenterHome.xaml +++ b/Wpf_AiSportsMicrospace/Views/CenterHome.xaml @@ -3,7 +3,7 @@ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="clr-namespace:Wpf_AiSportsMicrospace.MyUserControl" xmlns:gif="http://wpfanimatedgif.codeplex.com" - Height="1080" Width="1920" > + Height="1080" Width="1920" Loaded="Window_Loaded" Unloaded="CenterHome_Unloaded" > diff --git a/Wpf_AiSportsMicrospace/Views/CenterHome.xaml.cs b/Wpf_AiSportsMicrospace/Views/CenterHome.xaml.cs index b1947ea..c62678d 100644 --- a/Wpf_AiSportsMicrospace/Views/CenterHome.xaml.cs +++ b/Wpf_AiSportsMicrospace/Views/CenterHome.xaml.cs @@ -19,6 +19,7 @@ using Wpf_AiSportsMicrospace.Enum; using Wpf_AiSportsMicrospace.MyUserControl; using Wpf_AiSportsMicrospace.Views; using Yztob.AiSports.Inferences.Abstractions; +using Yztob.AiSports.Inferences.Things; using Yztob.AiSports.Postures; using Yztob.AiSports.Postures.Abstractions; using Yztob.AiSports.Postures.Sports; @@ -33,10 +34,13 @@ namespace Wpf_AiSportsMicrospace.Views public partial class CenterHome : UserControl { private IHumanPredictor _humanPredictor; + private IObjectDetector _objectDetector; + private HumanGraphicsRenderer _humanGraphicsRenderer; private WebcamClient _webcamClient; private ConcurrentQueue _frameQueue = new(); private CancellationTokenSource _cts = new(); private SportOperate _sportOperate; + private Main _mainWin => Application.Current.MainWindow as Main; public String _nowSelect = "test"; //测试吧:test 游戏吧:play public String NowSelect @@ -55,7 +59,11 @@ namespace Wpf_AiSportsMicrospace.Views public CenterHome() { InitializeComponent(); - _humanPredictor = HumanPredictorFactory.Create(HumanPredictorType.SingleHigh); + //_humanPredictor = HumanPredictorFactory.Create(HumanPredictorType.SingleHigh); + //_humanPredictor = HumanPredictorFactory.Create(HumanPredictorType.MultiMedium); + //_objectDetector = ObjectDetectorFactory.CreateSportGoodsDetector(); + //_humanGraphicsRenderer = new HumanGraphicsRenderer(); + //_humanGraphicsRenderer.DrawLabel = false; //_sports = SportBase.GetSports(); //_detectQueue = new SportDetectionQueue(); @@ -66,36 +74,66 @@ namespace Wpf_AiSportsMicrospace.Views coverFlow.SelectedIndex = 0; InitImg(); + + Loaded += Window_Loaded; + Unloaded += CenterHome_Unloaded; //AnimationBehavior.SetSourceUri(LoadingImage, loadingImage); } private void Window_Loaded(object sender, RoutedEventArgs e) { - _sportOperate = new SportOperate(); - _webcamClient = _sportOperate.CreateRTSP(); - - _webcamClient.OnExtractFrame += frame => - { - if (frame != null) - _frameQueue.Enqueue(frame); - }; - _webcamClient.StartExtract(); - - StartFrameProcessing(); - + _mainWin.HumanFrameUpdated += OnHumanFrameUpdated; Utils.PlayBackgroundMusic("homeprojectselected.mp3"); - - //coverFlow.ProgressCompleted += CoverFlow_ProgressCompleted; } + + private void CenterHome_Unloaded(object sender, RoutedEventArgs e) + { + _mainWin.HumanFrameUpdated -= OnHumanFrameUpdated; + } + + private void OnHumanFrameUpdated(object sender, List humans) + { + var human = humans.FirstOrDefault(); + + if (human == null) return; + + //检测挥手动作 + var wavingaction = _mainWin.SportOperate.VerifyWavingAction(human); + + switch (wavingaction) + { + case (int)WavingAction.LeftWave: // 左手挥动 + coverFlow.SlideRight(); + break; + + case (int)WavingAction.RightWave: // 右手挥动 + coverFlow.SlideLeft(); + break; + + case (int)WavingAction.FirstHand: // 举手开始 + coverFlow.StartSelectedProgress(); + break; + + case (int)WavingAction.Raising: // 举手中,实时更新进度 + break; + + case (int)WavingAction.RaiseHand: // 举手完成 + _cts.Cancel(); + coverFlow.ProgressCompleted += CoverFlow_ProgressCompleted; + break; + + default: // 没有动作 → 取消进度 + coverFlow.CancelSelectedProgress(); + break; + } + } + private void CoverFlow_ProgressCompleted(CoverFlowItem item) { // 停止抽帧线程/释放资源 try { - _cts.Cancel(); // 停止后台处理线程 - _webcamClient?.StopExtract(); // 停止摄像头抽帧 - _webcamClient = null; - + RouterGoNew(); } catch (Exception ex) { @@ -123,35 +161,33 @@ namespace Wpf_AiSportsMicrospace.Views // mainWin.SwitchPage(new GroupJumpRope(), true); //} - RouterGoNew(); } - private void StartFrameProcessing() { - Task.Run(() => + Task.Run(async () => { while (!_cts.Token.IsCancellationRequested) { if (_frameQueue.TryDequeue(out var frame)) { - ProcessFrame(frame); + try + { + ProcessFrame(frame); + } + catch (Exception ex) + { + Console.WriteLine("ProcessFrame error: " + ex.Message); + } } else { - //_webcamClient.OnExtractFrame += frame => - //{ - // if (frame != null) - // _frameQueue.Enqueue(frame); - //}; - //_webcamClient.StartExtract(); - - _webcamClient.StartExtract(); - //Thread.Sleep(5); + await Task.Delay(5, _cts.Token); } } }, _cts.Token); } + private void ProcessFrame(VideoFrame frame) { try @@ -163,13 +199,6 @@ namespace Wpf_AiSportsMicrospace.Views if (humans == null || humans.Count == 0) return; - //var human = humans - // .Where(h => - // h.Keypoints.Any(kp => kp.Name == "left_ankle" && kp.X < 1020 && kp.Y > 900 && kp.Y < 1020) && - // h.Keypoints.Any(kp => kp.Name == "right_ankle" && kp.X > 750 && kp.Y > 900 && kp.Y < 1020) - // ) - // .FirstOrDefault(); - var human = humans.FirstOrDefault(); if (human == null) return; @@ -177,10 +206,6 @@ namespace Wpf_AiSportsMicrospace.Views //检测挥手动作 var wavingaction = _sportOperate.VerifyWavingAction(human); - // 把低 8 位作为动作类型,高 8 位作为进度 - //int actionType = wavingaction & 0xFF; - //int progress = (wavingaction >> 8) & 0xFF; - switch (wavingaction) { case (int)WavingAction.LeftWave: // 左手挥动 @@ -216,7 +241,10 @@ namespace Wpf_AiSportsMicrospace.Views public void Dispose() { - throw new NotImplementedException(); + _cts.Cancel(); + _webcamClient?.StopExtract(); + _webcamClient = null; + _humanPredictor.Dispose(); } public void InitImg() diff --git a/Wpf_AiSportsMicrospace/Views/Home.xaml b/Wpf_AiSportsMicrospace/Views/Home.xaml index ec96960..76580db 100644 --- a/Wpf_AiSportsMicrospace/Views/Home.xaml +++ b/Wpf_AiSportsMicrospace/Views/Home.xaml @@ -3,7 +3,7 @@ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="clr-namespace:Wpf_AiSportsMicrospace.MyUserControl" xmlns:gif="http://wpfanimatedgif.codeplex.com" - Height="1080" Width="1920" Loaded="Window_Loaded"> + Height="1080" Width="1920" Loaded="Window_Loaded" Unloaded="Home_Unloaded"> diff --git a/Wpf_AiSportsMicrospace/Views/Home.xaml.cs b/Wpf_AiSportsMicrospace/Views/Home.xaml.cs index a13c783..ce527e8 100644 --- a/Wpf_AiSportsMicrospace/Views/Home.xaml.cs +++ b/Wpf_AiSportsMicrospace/Views/Home.xaml.cs @@ -20,6 +20,7 @@ using Wpf_AiSportsMicrospace.Enum; using Wpf_AiSportsMicrospace.MyUserControl; using Wpf_AiSportsMicrospace.Views; using Yztob.AiSports.Inferences.Abstractions; +using Yztob.AiSports.Inferences.Things; using Yztob.AiSports.Postures; using Yztob.AiSports.Postures.Abstractions; using Yztob.AiSports.Postures.Sports; @@ -34,16 +35,21 @@ namespace Wpf_AiSportsMicrospace public partial class Home : UserControl { private IHumanPredictor _humanPredictor; + private HumanGraphicsRenderer _humanGraphicsRenderer; private WebcamClient _webcamClient; private ConcurrentQueue _frameQueue = new(); private CancellationTokenSource _cts = new(); private SportOperate _sportOperate; public static Uri loadingImage = new Uri("/Resources/Img/Album/1.gif", UriKind.Relative); + private Main _mainWin => Application.Current.MainWindow as Main; + public Home() { InitializeComponent(); - _humanPredictor = HumanPredictorFactory.Create(HumanPredictorType.SingleHigh); + //_humanPredictor = HumanPredictorFactory.Create(HumanPredictorType.SingleHigh); + //_humanGraphicsRenderer = new HumanGraphicsRenderer(); + //_humanGraphicsRenderer.DrawLabel = false; //_sports = SportBase.GetSports(); //_detectQueue = new SportDetectionQueue(); @@ -64,24 +70,101 @@ namespace Wpf_AiSportsMicrospace // 默认选中第3张 coverFlow.SelectedIndex = 0; - + Loaded += Window_Loaded; + Unloaded += Home_Unloaded; //AnimationBehavior.SetSourceUri(LoadingImage, loadingImage); } + private void Home_Unloaded(object sender, RoutedEventArgs e) + { + _mainWin.HumanFrameUpdated -= OnHumanFrameUpdated; + } + + private void OnHumanFrameUpdated(object sender, List humans) + { + var human = humans.FirstOrDefault(); + + if (human == null) return; + + //检测挥手动作 + var wavingaction = _mainWin.SportOperate.VerifyWavingAction(human); + + // 把低 8 位作为动作类型,高 8 位作为进度 + //int actionType = wavingaction & 0xFF; + //int progress = (wavingaction >> 8) & 0xFF; + + //switch (wavingaction) + //{ + // case (int)WavingAction.LeftWave: // 左手挥动 + // Application.Current.Dispatcher.BeginInvoke(() => coverFlow.SlideRight()); + // break; + + // case (int)WavingAction.RightWave: // 右手挥动 + // Application.Current.Dispatcher.BeginInvoke(() => coverFlow.SlideLeft()); + // break; + + // case (int)WavingAction.FirstHand: // 举手开始 + // Application.Current.Dispatcher.BeginInvoke(() => coverFlow.StartSelectedProgress()); + // break; + + // case (int)WavingAction.Raising: // 举手中,实时更新进度 + // break; + + // case (int)WavingAction.RaiseHand: // 举手完成 + // _cts.Cancel(); + // coverFlow.ProgressCompleted += CoverFlow_ProgressCompleted; + // break; + + // default: // 没有动作 → 取消进度 + // Application.Current.Dispatcher.BeginInvoke(() => coverFlow.CancelSelectedProgress()); + // break; + //} + + switch (wavingaction) + { + case (int)WavingAction.LeftWave: // 左手挥动 + coverFlow.SlideRight(); + break; + + case (int)WavingAction.RightWave: // 右手挥动 + coverFlow.SlideLeft(); + break; + + case (int)WavingAction.FirstHand: // 举手开始 + coverFlow.StartSelectedProgress(); + break; + + case (int)WavingAction.Raising: // 举手中,实时更新进度 + break; + + case (int)WavingAction.RaiseHand: // 举手完成 + _cts.Cancel(); + coverFlow.ProgressCompleted += CoverFlow_ProgressCompleted; + break; + + default: // 没有动作 → 取消进度 + coverFlow.CancelSelectedProgress(); + break; + } + } + + private void Window_Loaded(object sender, RoutedEventArgs e) { - _sportOperate = new SportOperate(); - _webcamClient = _sportOperate.CreateRTSP(); + _mainWin.HumanFrameUpdated += OnHumanFrameUpdated; - _webcamClient.OnExtractFrame += frame => - { - if (frame != null) - _frameQueue.Enqueue(frame); - }; - _webcamClient.StartExtract(); + //_sportOperate = new SportOperate(); + //_webcamClient = _sportOperate.CreateRTSP(); - StartFrameProcessing(); + //_webcamClient.OnExtractFrame += frame => + //{ + // if (frame != null) + // _frameQueue.Enqueue(frame); + //}; + //_webcamClient.StartExtract(); + + //StartFrameProcessing(); //Utils.PlayBackgroundMusic("homeprojectselected.mp3"); @@ -92,10 +175,8 @@ namespace Wpf_AiSportsMicrospace // 停止抽帧线程/释放资源 try { - _cts.Cancel(); // 停止后台处理线程 - _webcamClient?.StopExtract(); // 停止摄像头抽帧 - _webcamClient = null; - + //Dispose(); + RouterGoNew(); } catch (Exception ex) { @@ -115,28 +196,27 @@ namespace Wpf_AiSportsMicrospace //else if (uri.EndsWith("3.jpg")) // newWindow = new GroupJumpRope(); // 找到主窗口,切换内容到 GroupJumpRopePage - - //var mainWin = Application.Current.MainWindow as Main; - //if (mainWin != null) - //{ - // Utils.PlayBackgroundMusic("musicjumprope1.mp3"); - // mainWin.SwitchPage(new GroupJumpRope(), true); - //} - RouterGoNew(); } private void StartFrameProcessing() { - Task.Run(() => + Task.Run(async () => { while (!_cts.Token.IsCancellationRequested) { if (_frameQueue.TryDequeue(out var frame)) { - ProcessFrame(frame); + try + { + ProcessFrame(frame); + } + catch (Exception ex) + { + Console.WriteLine("ProcessFrame error: " + ex.Message); + } } else { - Thread.Sleep(5); + await Task.Delay(5, _cts.Token); } } }, _cts.Token); @@ -173,18 +253,16 @@ namespace Wpf_AiSportsMicrospace switch (wavingaction) { - case 0: // 点位验证失败 - break; case (int)WavingAction.LeftWave: // 左手挥动 - Dispatcher.BeginInvoke(() => coverFlow.SlideRight()); + Application.Current.Dispatcher.BeginInvoke(() => coverFlow.SlideRight()); break; case (int)WavingAction.RightWave: // 右手挥动 - Dispatcher.BeginInvoke(() => coverFlow.SlideLeft()); + Application.Current.Dispatcher.BeginInvoke(() => coverFlow.SlideLeft()); break; case (int)WavingAction.FirstHand: // 举手开始 - Dispatcher.BeginInvoke(() => coverFlow.StartSelectedProgress()); + Application.Current.Dispatcher.BeginInvoke(() => coverFlow.StartSelectedProgress()); break; case (int)WavingAction.Raising: // 举手中,实时更新进度 @@ -196,7 +274,7 @@ namespace Wpf_AiSportsMicrospace break; default: // 没有动作 → 取消进度 - Dispatcher.BeginInvoke(() => coverFlow.CancelSelectedProgress()); + Application.Current.Dispatcher.BeginInvoke(() => coverFlow.CancelSelectedProgress()); break; } } @@ -208,14 +286,17 @@ namespace Wpf_AiSportsMicrospace public void Dispose() { - throw new NotImplementedException(); + _cts.Cancel(); + _webcamClient?.StopExtract(); + _webcamClient = null; + _humanPredictor.Dispose(); } //测试点击 private void GoNew(object sender, MouseButtonEventArgs e) { // 跳转逻辑(如导航到新页面并传递参数) - // 例如:RaiseEvent、调用外部委托、或使用导航框架 + //例如:RaiseEvent、调用外部委托、或使用导航框架 var mainWin = Application.Current.MainWindow as Main; var newPage = new CenterHome { diff --git a/Wpf_AiSportsMicrospace/Views/JumpRope/GroupJumpRope.xaml b/Wpf_AiSportsMicrospace/Views/JumpRope/GroupJumpRope.xaml index 48401f4..959f92a 100644 --- a/Wpf_AiSportsMicrospace/Views/JumpRope/GroupJumpRope.xaml +++ b/Wpf_AiSportsMicrospace/Views/JumpRope/GroupJumpRope.xaml @@ -4,7 +4,7 @@ xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:local="clr-namespace:Wpf_AiSportsMicrospace.Views" - mc:Ignorable="d" Height="1080" Width="1920" Loaded="UserControl_Loaded"> + mc:Ignorable="d" Height="1080" Width="1920" Loaded="UserControl_Loaded" Unloaded="UserControl_Unloaded"> diff --git a/Wpf_AiSportsMicrospace/Views/JumpRope/GroupJumpRope.xaml.cs b/Wpf_AiSportsMicrospace/Views/JumpRope/GroupJumpRope.xaml.cs index 5388495..5c3b07f 100644 --- a/Wpf_AiSportsMicrospace/Views/JumpRope/GroupJumpRope.xaml.cs +++ b/Wpf_AiSportsMicrospace/Views/JumpRope/GroupJumpRope.xaml.cs @@ -1,5 +1,6 @@ using Emgu.CV.Flann; using HandyControl.Controls; +using SharpDX.Direct3D9; using System; using System.Collections.Concurrent; using System.Collections.Generic; @@ -44,23 +45,23 @@ namespace Wpf_AiSportsMicrospace.Views private ConcurrentQueue _frameQueue = new(); private CancellationTokenSource _cts = new(); private SportOperate _sportOperate; - private SportBase _sport; - private readonly SportDetectionQueue _detectQueue; private List sports = new(); private List circleTexts = new(); private double[] circlePositionsX = { 0.07, 0.21, 0.36, 0.50, 0.64, 0.78, 0.92 }; - + private Main _mainWin => Application.Current.MainWindow as Main; ConfigService configService = new ConfigService(); public GroupJumpRope() { InitializeComponent(); - _humanPredictor = HumanPredictorFactory.Create(HumanPredictorType.MultiMedium); - _objectDetector = ObjectDetectorFactory.CreateSportGoodsDetector(); - _humanGraphicsRenderer = new HumanGraphicsRenderer(); - _humanGraphicsRenderer.DrawLabel = false; + //_humanPredictor = HumanPredictorFactory.Create(HumanPredictorType.MultiMedium); + //_objectDetector = ObjectDetectorFactory.CreateSportGoodsDetector(); + //_humanGraphicsRenderer = new HumanGraphicsRenderer(); + //_humanGraphicsRenderer.DrawLabel = false; - _detectQueue = new SportDetectionQueue(); + + Loaded += UserControl_Loaded; + Unloaded += UserControl_Unloaded; } private void UserControl_Loaded(object sender, RoutedEventArgs e) { @@ -68,17 +69,37 @@ namespace Wpf_AiSportsMicrospace.Views DrawCirclesWithText(); - _sportOperate = new SportOperate(); - _webcamClient = _sportOperate.CreateRTSP(); + _mainWin.HumanFrameUpdated += OnHumanFrameUpdated; - _webcamClient.OnExtractFrame += frame => + //_sportOperate = new SportOperate(); + //_webcamClient = _sportOperate.CreateRTSP(); + + //_webcamClient.OnExtractFrame += frame => + //{ + // if (frame != null) + // _frameQueue.Enqueue(frame); + //}; + //_webcamClient.StartExtract(); + + //StartFrameProcessing(); + } + private void UserControl_Unloaded(object sender, RoutedEventArgs e) + { + _mainWin.HumanFrameUpdated -= OnHumanFrameUpdated; + } + + + private void OnHumanFrameUpdated(object sender, List humans) + { + try { - if (frame != null) - _frameQueue.Enqueue(frame); - }; - _webcamClient.StartExtract(); + UpdateCircleCounts(humans); + } + catch (Exception ex) + { + Console.WriteLine("OnFrameExtracted error: " + ex.Message); + } - StartFrameProcessing(); } private void StartFrameProcessing() @@ -93,7 +114,7 @@ namespace Wpf_AiSportsMicrospace.Views } else { - Thread.Sleep(5); // 空队列时避免忙等 + Thread.Sleep(5); } } }, _cts.Token); @@ -124,7 +145,7 @@ namespace Wpf_AiSportsMicrospace.Views foreach (var hu in humans) { - var nose = hu.Keypoints.FirstOrDefault(k => k.Name == "nose"); + var nose = hu.Keypoints.FirstOrDefault(k => k.Name == "right_ankle"); if (nose == null) continue; @@ -197,8 +218,6 @@ namespace Wpf_AiSportsMicrospace.Views sports.Add(sport); } } - - private void UpdateCircleCounts(List humans) { for (int i = 0; i < circlePositionsX.Length; i++) @@ -215,7 +234,6 @@ namespace Wpf_AiSportsMicrospace.Views } } } - private void AddGlowEllipse(double centerX, double centerY, double radius, Canvas canvas) { var ellipse = new Ellipse @@ -361,5 +379,6 @@ namespace Wpf_AiSportsMicrospace.Views Canvas.SetTop(ellipse, centerY - (radius * flattenFactor) / 2); canvas.Children.Add(ellipse); } + } } diff --git a/Wpf_AiSportsMicrospace/Views/Main.xaml.cs b/Wpf_AiSportsMicrospace/Views/Main.xaml.cs index 4d85ae6..d20bcf7 100644 --- a/Wpf_AiSportsMicrospace/Views/Main.xaml.cs +++ b/Wpf_AiSportsMicrospace/Views/Main.xaml.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Concurrent; using System.Collections.Generic; using System.Linq; using System.Text; @@ -12,6 +13,11 @@ using System.Windows.Media; using System.Windows.Media.Animation; using System.Windows.Media.Imaging; using System.Windows.Media.Media3D; +using Wpf_AiSportsMicrospace.Common; +using Yztob.AiSports.Inferences.Abstractions; +using Yztob.AiSports.Inferences.Things; +using Yztob.AiSports.Sensors.Abstractions; +using Yztob.AiSports.Sensors.Things; namespace Wpf_AiSportsMicrospace.Views { @@ -20,13 +26,86 @@ namespace Wpf_AiSportsMicrospace.Views /// public partial class Main : Window { + private IHumanPredictor _humanPredictor; + private HumanGraphicsRenderer _humanGraphicsRenderer; + public SportOperate SportOperate { get; private set; } + public WebcamClient WebcamClient { get; private set; } + private readonly ConcurrentQueue _frameQueue = new(); + private readonly CancellationTokenSource _cts = new(); public Main() { InitializeComponent(); + var options = new InferenceOptions() + { + GpuEnabled = true + }; + Yztob.AiSports.Common.SportAppSettingService.Set("inferences", options); + + _humanPredictor = HumanPredictorFactory.Create(HumanPredictorType.MultiMedium); + _humanGraphicsRenderer = new HumanGraphicsRenderer(); + _humanGraphicsRenderer.DrawLabel = false; + + SportOperate = new SportOperate(); + WebcamClient = SportOperate.CreateRTSP(); + + // 开始抽帧线程 + StartFrameProcessing(); + // 默认显示首页 MainContent.Content = new Home(); } + private void StartFrameProcessing() + { + WebcamClient.OnExtractFrame += frame => + { + if (frame != null) + _frameQueue.Enqueue(frame); + }; + + WebcamClient.StartExtract(); + + Task.Run(() => + { + while (!_cts.Token.IsCancellationRequested) + { + if (_frameQueue.TryDequeue(out var frame)) + { + ProcessFrame(frame); + } + else + { + // 避免 CPU 占满 + Thread.Sleep(5); + } + } + }, _cts.Token); + } + + private void ProcessFrame(VideoFrame frame) + { + try + { + var buffer = frame.GetImageBuffer(ImageFormat.Jpeg).ToArray(); + var humanResult = _humanPredictor.Predicting(buffer, frame.Number); + + var humans = humanResult?.Humans?.ToList(); + if (humans == null || humans.Count == 0) + return; + + // 触发全局事件 + Application.Current.Dispatcher.Invoke(() => + { + HumanFrameUpdated?.Invoke(this, humans); + }); + } + catch (Exception ex) + { + Console.WriteLine("ProcessFrame error: " + ex.Message); + } + } + + public event EventHandler> HumanFrameUpdated; //public void SwitchPage(UserControl newPage, bool fromRight) //{ // if (MainContent.Content == newPage) return; @@ -79,86 +158,100 @@ namespace Wpf_AiSportsMicrospace.Views // 预加载新页面资源 PreloadPageResources(newPage); - if (oldPage != null) - { - MainContent.Content = null; - // 创建容器同时显示新旧页面 - var container = new Grid(); - container.Children.Add(newPage); // 新页面在底层 - container.Children.Add(oldPage); // 旧页面在顶层 + // 确保最终状态 + newPage.RenderTransform = null; + newPage.Opacity = 1; - MainContent.Content = container; + // 切换到只有新页面 + MainContent.Content = newPage; - // 设置新页面初始状态 - 轻微缩放和偏移 - var newTransformGroup = new TransformGroup(); - var newScale = new ScaleTransform(0.95, 0.95); - var newTranslate = new TranslateTransform(fromRight ? 80 : -80, 0); - newTransformGroup.Children.Add(newScale); - newTransformGroup.Children.Add(newTranslate); - newPage.RenderTransformOrigin = new Point(0.5, 0.5); - newPage.RenderTransform = newTransformGroup; - newPage.Opacity = 1; - // 旧页面动画 - 飞出效果 - var oldTransformGroup = new TransformGroup(); - var oldScale = new ScaleTransform(1, 1); - var oldSkew = new SkewTransform(0, 0); - var oldTranslate = new TranslateTransform(0, 0); - oldTransformGroup.Children.Add(oldScale); - oldTransformGroup.Children.Add(oldSkew); - oldTransformGroup.Children.Add(oldTranslate); - oldPage.RenderTransformOrigin = new Point(fromRight ? 1 : 0, 0.5); - oldPage.RenderTransform = oldTransformGroup; - oldPage.Opacity = 1; + // 清理资源 + if (oldPage is IDisposable disposable) + disposable.Dispose(); - // 动画参数 - var duration = TimeSpan.FromMilliseconds(1200); - var easing = new CubicEase { EasingMode = EasingMode.EaseInOut }; + //container.Children.Clear(); - // 旧页面飞出动画 - var oldScaleAnim = new DoubleAnimation(1, 1.1, duration) { EasingFunction = easing }; - var oldSkewAnim = new DoubleAnimation(0, fromRight ? 15 : -15, duration) { EasingFunction = easing }; - var oldTranslateAnim = new DoubleAnimation(0, fromRight ? ActualWidth * 1.1 : -ActualWidth * 1.1, duration) - { - EasingFunction = easing - }; + //if (oldPage != null) + //{ + // MainContent.Content = null; + // // 创建容器同时显示新旧页面 + // var container = new Grid(); + // container.Children.Add(newPage); // 新页面在底层 + // container.Children.Add(oldPage); // 旧页面在顶层 - // 新页面进入动画 - var newScaleAnim = new DoubleAnimation(0.8, 1, duration) { EasingFunction = easing }; - var newTranslateAnim = new DoubleAnimation(fromRight ? -80 : -80, 0, duration) { EasingFunction = easing }; + // MainContent.Content = container; - // 开始动画 - oldScale.BeginAnimation(ScaleTransform.ScaleXProperty, oldScaleAnim); - oldScale.BeginAnimation(ScaleTransform.ScaleYProperty, oldScaleAnim); - oldSkew.BeginAnimation(SkewTransform.AngleYProperty, oldSkewAnim); - oldTranslate.BeginAnimation(TranslateTransform.XProperty, oldTranslateAnim); + // // 设置新页面初始状态 - 轻微缩放和偏移 + // var newTransformGroup = new TransformGroup(); + // var newScale = new ScaleTransform(0.95, 0.95); + // var newTranslate = new TranslateTransform(fromRight ? 80 : -80, 0); + // newTransformGroup.Children.Add(newScale); + // newTransformGroup.Children.Add(newTranslate); + // newPage.RenderTransformOrigin = new Point(0.5, 0.5); + // newPage.RenderTransform = newTransformGroup; + // newPage.Opacity = 1; - newScale.BeginAnimation(ScaleTransform.ScaleXProperty, newScaleAnim); - newScale.BeginAnimation(ScaleTransform.ScaleYProperty, newScaleAnim); - newTranslate.BeginAnimation(TranslateTransform.XProperty, newTranslateAnim); + // // 旧页面动画 - 飞出效果 + // var oldTransformGroup = new TransformGroup(); + // var oldScale = new ScaleTransform(1, 1); + // var oldSkew = new SkewTransform(0, 0); + // var oldTranslate = new TranslateTransform(0, 0); + // oldTransformGroup.Children.Add(oldScale); + // oldTransformGroup.Children.Add(oldSkew); + // oldTransformGroup.Children.Add(oldTranslate); + // oldPage.RenderTransformOrigin = new Point(fromRight ? 1 : 0, 0.5); + // oldPage.RenderTransform = oldTransformGroup; + // oldPage.Opacity = 1; - // 动画完成后清理 - oldTranslateAnim.Completed += (s, e) => - { - // 确保最终状态 - newPage.RenderTransform = null; - newPage.Opacity = 1; + // // 动画参数 + // var duration = TimeSpan.FromMilliseconds(1200); + // var easing = new CubicEase { EasingMode = EasingMode.EaseInOut }; - // 切换到只有新页面 - MainContent.Content = newPage; + // // 旧页面飞出动画 + // var oldScaleAnim = new DoubleAnimation(1, 1.1, duration) { EasingFunction = easing }; + // var oldSkewAnim = new DoubleAnimation(0, fromRight ? 15 : -15, duration) { EasingFunction = easing }; + // var oldTranslateAnim = new DoubleAnimation(0, fromRight ? ActualWidth * 1.1 : -ActualWidth * 1.1, duration) + // { + // EasingFunction = easing + // }; - // 清理资源 - if (oldPage is IDisposable disposable) - disposable.Dispose(); + // // 新页面进入动画 + // var newScaleAnim = new DoubleAnimation(0.8, 1, duration) { EasingFunction = easing }; + // var newTranslateAnim = new DoubleAnimation(fromRight ? -80 : -80, 0, duration) { EasingFunction = easing }; - container.Children.Clear(); - }; - } - else - { - MainContent.Content = newPage; - newPage.Opacity = 1; - } + // // 开始动画 + // oldScale.BeginAnimation(ScaleTransform.ScaleXProperty, oldScaleAnim); + // oldScale.BeginAnimation(ScaleTransform.ScaleYProperty, oldScaleAnim); + // oldSkew.BeginAnimation(SkewTransform.AngleYProperty, oldSkewAnim); + // oldTranslate.BeginAnimation(TranslateTransform.XProperty, oldTranslateAnim); + + // newScale.BeginAnimation(ScaleTransform.ScaleXProperty, newScaleAnim); + // newScale.BeginAnimation(ScaleTransform.ScaleYProperty, newScaleAnim); + // newTranslate.BeginAnimation(TranslateTransform.XProperty, newTranslateAnim); + + // // 动画完成后清理 + // oldTranslateAnim.Completed += (s, e) => + // { + // // 确保最终状态 + // newPage.RenderTransform = null; + // newPage.Opacity = 1; + + // // 切换到只有新页面 + // MainContent.Content = newPage; + + // // 清理资源 + // if (oldPage is IDisposable disposable) + // disposable.Dispose(); + + // container.Children.Clear(); + // }; + //} + //else + //{ + // MainContent.Content = newPage; + // newPage.Opacity = 1; + //} } private void PreloadPageResources(UserControl page) diff --git a/Wpf_AiSportsMicrospace/Wpf_AiSportsMicrospace.csproj b/Wpf_AiSportsMicrospace/Wpf_AiSportsMicrospace.csproj index aa963af..ea0c921 100644 --- a/Wpf_AiSportsMicrospace/Wpf_AiSportsMicrospace.csproj +++ b/Wpf_AiSportsMicrospace/Wpf_AiSportsMicrospace.csproj @@ -64,7 +64,7 @@ - +