From df8b9fc9cd4086bf3fabeb26df7547190106589c Mon Sep 17 00:00:00 2001 From: tanglong <842690096@qq.com> Date: Fri, 13 Mar 2026 13:50:21 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BC=98=E5=8C=96=E6=9B=B4=E6=96=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Wpf_AiSportsMicrospace/Common/SportOperate.cs | 12 +- Wpf_AiSportsMicrospace/Common/Utils.cs | 33 +++- .../SportExpand/Optimizes.cs | 41 ++++ Wpf_AiSportsMicrospace/Views/Home.xaml.cs | 35 +--- .../Views/JumpRope/GroupJumpRope.xaml.cs | 180 ++++++++++-------- .../Views/SquatFu/WhackAMole.xaml.cs | 35 ++-- 6 files changed, 199 insertions(+), 137 deletions(-) create mode 100644 Wpf_AiSportsMicrospace/SportExpand/Optimizes.cs diff --git a/Wpf_AiSportsMicrospace/Common/SportOperate.cs b/Wpf_AiSportsMicrospace/Common/SportOperate.cs index 01ebc37..49c7cb9 100644 --- a/Wpf_AiSportsMicrospace/Common/SportOperate.cs +++ b/Wpf_AiSportsMicrospace/Common/SportOperate.cs @@ -34,7 +34,6 @@ namespace Wpf_AiSportsMicrospace.Common private const int WaveThreshold = 4; WebcamClient _webcamClient; - private DateTime? _raiseStartTime = null; private DateTime? _wristStartTime = null; private bool _firstHandTriggered = false; @@ -75,7 +74,8 @@ namespace Wpf_AiSportsMicrospace.Common /// /// 验证右手动作(右手举手优先,然后左右挥手) /// - public int VerifyRightWavingAction(List humans) + public int + VerifyRightWavingAction(List humans) { if (humans == null || humans.Count == 0) return (int)WavingAction.None; @@ -148,7 +148,6 @@ namespace Wpf_AiSportsMicrospace.Common if (!pass) { - _raiseStartTime = null; _wristStartTime = null; _firstHandTriggered = false; return (int)WavingAction.None; @@ -156,23 +155,20 @@ namespace Wpf_AiSportsMicrospace.Common var now = DateTime.Now; - _raiseStartTime ??= now; _wristStartTime ??= now; double wristElapsed = (now - _wristStartTime.Value).TotalSeconds; - double raiseElapsed = (now - _raiseStartTime.Value).TotalSeconds; // 第一步:触发倒计时 / 举手开始 - if (!_firstHandTriggered && wristElapsed >= 1) + if (!_firstHandTriggered && wristElapsed >= 0) { _firstHandTriggered = true; return (int)WavingAction.FirstHand; } // 第二步:举手完成 - if (raiseElapsed >= 1) + if (wristElapsed >= 3) { - _raiseStartTime = null; _wristStartTime = null; _firstHandTriggered = false; return (int)WavingAction.RaiseHand; diff --git a/Wpf_AiSportsMicrospace/Common/Utils.cs b/Wpf_AiSportsMicrospace/Common/Utils.cs index cd614d2..014089b 100644 --- a/Wpf_AiSportsMicrospace/Common/Utils.cs +++ b/Wpf_AiSportsMicrospace/Common/Utils.cs @@ -1,4 +1,5 @@ -using Common; +using AiSportsMicrospaceDB.Entities; +using Common; using Dto; using SixLabors.ImageSharp; using SixLabors.ImageSharp.Formats.Jpeg; @@ -87,7 +88,7 @@ namespace Wpf_AiSportsMicrospace.Common Application.Current.Dispatcher.Invoke(() => { if (_bgPlayer != null) - + currentTime = _bgPlayer.Position.TotalSeconds; }); return currentTime; @@ -353,5 +354,33 @@ namespace Wpf_AiSportsMicrospace.Common // 转成常用的 XX-XX-XX-XX-XX-XX 格式 return string.Join("-", macBytes.Select(b => b.ToString("X2"))); } + + public static List<(double XNorm, double YNorm)> GetPointsList(int pointCount, GroupPoints cfg) + { + List<(double XNorm, double YNorm)> pointlist = new List<(double XNorm, double YNorm)>(); + + if (cfg == null || pointCount <= 0) + return pointlist; + + // 所有可能的点 + var allPoints = new[] + { + cfg.Point1, cfg.Point2, cfg.Point3, + cfg.Point4, cfg.Point5, cfg.Point6, + cfg.Point7, cfg.Point8, cfg.Point9, cfg.Point10 + }; + + // 取前 pointCount 个点 + foreach (var point in allPoints.Take(pointCount)) + { + if (point != null) + { + pointlist.Add((point.X, point.Y)); + } + } + + return pointlist; + } + } } diff --git a/Wpf_AiSportsMicrospace/SportExpand/Optimizes.cs b/Wpf_AiSportsMicrospace/SportExpand/Optimizes.cs new file mode 100644 index 0000000..e2786ee --- /dev/null +++ b/Wpf_AiSportsMicrospace/SportExpand/Optimizes.cs @@ -0,0 +1,41 @@ +using Newtonsoft.Json; +using System; +using System.Collections.Generic; +using System.Text; +using Yztob.AiSports.Postures.Sports; +using Yztob.AiSports.Postures.Things; + +namespace SportExpand +{ + public class Optimizes + { /// + /// 执行运动微调 + /// + /// 要微调的运动 + public static void Optimizeing(SportBase sport) + { + if (sport == null) + throw new ArgumentNullException(nameof(sport)); + + //获取运动的检测规则,如果使用的是规则检测时提供 + Console.WriteLine(JsonConvert.SerializeObject(sport.Rules)); + //获取运动的检测动作样本姿态,如果使用的是相似度时提供 + Console.WriteLine(JsonConvert.SerializeObject(sport.Samples)); + + switch (sport.Key) + { + case "squat": + var loggicRule = sport.Rules["squat"] as CalcLogicAndRule; + //删除手部姿态检查规则 + loggicRule.Rules.RemoveAt(2); + loggicRule.Rules.RemoveAt(1); + break; + + case "rope-skipping": + var newSport = sport as SportRopeSkipping; + newSport.ShakeRate = 0.02f; + break; + } + } + } +} diff --git a/Wpf_AiSportsMicrospace/Views/Home.xaml.cs b/Wpf_AiSportsMicrospace/Views/Home.xaml.cs index a226a24..d3700fa 100644 --- a/Wpf_AiSportsMicrospace/Views/Home.xaml.cs +++ b/Wpf_AiSportsMicrospace/Views/Home.xaml.cs @@ -57,6 +57,7 @@ namespace Wpf_AiSportsMicrospace ImageUri = GetResourceUri("play_img/play_music.png"), ProgressColor1 = "#e73d42", ProgressColor2 = "#fd8212" + }); coverFlow.Images.Add(new CoverFlowItem { @@ -80,7 +81,7 @@ namespace Wpf_AiSportsMicrospace if (_mainWin.WebcamClient == null) { - //HandyControl.Controls.MessageBox.Show("摄像头网络异常,请重新连接!"); + HandyControl.Controls.MessageBox.Show("摄像头网络异常,请重新连接!"); return; } @@ -89,15 +90,14 @@ namespace Wpf_AiSportsMicrospace } private void Home_Unloaded(object sender, RoutedEventArgs e) { - //_mainWin.HumanFrameUpdated -= OnHumanFrameUpdated; + _mainWin.HumanFrameUpdated -= OnHumanFrameUpdated; } private bool IsGestureActive = true; // 标记手势识别是否有效 private void OnHumanFrameUpdated(object sender, List humans) { if (!IsGestureActive) return; // 手势识别已停止,直接返回 - var human = humans.FirstOrDefault(); - if (human == null) return; + if (humans == null || humans.Count == 0) return; // 检测挥手动作 var wavingaction = _mainWin.SportOperate.VerifyRightWavingAction(humans); @@ -120,9 +120,9 @@ namespace Wpf_AiSportsMicrospace break; case (int)WavingAction.RaiseHand: // 举手完成 - coverFlow.ProgressCompleted += CoverFlow_ProgressCompleted; - - // 设置标记,停止后续手势识别 + // 设置标记,停止后续手势识别 + //coverFlow.ProgressCompleted += CoverFlow_ProgressCompleted; + RouterGoNew(); IsGestureActive = false; break; @@ -132,20 +132,6 @@ namespace Wpf_AiSportsMicrospace } } - private void CoverFlow_ProgressCompleted(CoverFlowItem item) - { - // 停止抽帧线程/释放资源 - try - { - //_mainWin.HumanFrameUpdated -= OnHumanFrameUpdated; - //_mainWin.WebcamClient.StopExtract(); - RouterGoNew(); - } - catch (Exception ex) - { - Debug.WriteLine($"停止抽帧异常: {ex.Message}"); - } - } //测试点击 private void GoNew(object sender, MouseButtonEventArgs e) { @@ -162,9 +148,6 @@ namespace Wpf_AiSportsMicrospace if (coverFlow.SelectedIndex == 0) { var newPage = new GroupJumpRope(); - //mainWin?.SwitchPageWithMaskAnimation(newPage, true); - - //var newPage = new BambooDragonfly(); mainWin?.SwitchPageWithMaskAnimation(newPage, true); } else if (coverFlow.SelectedIndex == 1) @@ -272,7 +255,7 @@ namespace Wpf_AiSportsMicrospace var baseData = await db.BasicConfig.FirstOrDefaultAsync(x => x.Code == AppSettings.Code); if (baseData == null) { - baseData = new BasicConfig { Code = AppSettings.Code, Host = host.Text, UserName = name.Text, Password = password.Text , HomeWidth = int.Parse(homeWidth.Text)}; + baseData = new BasicConfig { Code = AppSettings.Code, Host = host.Text, UserName = name.Text, Password = password.Text, HomeWidth = int.Parse(homeWidth.Text) }; db.BasicConfig.Add(baseData); db.SaveChanges(); } @@ -326,7 +309,7 @@ namespace Wpf_AiSportsMicrospace //var db = scope.ServiceProvider.GetRequiredService(); //var mainWindow = new MainWindow(db); //mainWindow.Show(); - + var mainWin = Application.Current.MainWindow as Main; mainWin.SwitchPageWithMaskAnimation(new MultiJumpRope(), true); } diff --git a/Wpf_AiSportsMicrospace/Views/JumpRope/GroupJumpRope.xaml.cs b/Wpf_AiSportsMicrospace/Views/JumpRope/GroupJumpRope.xaml.cs index cfd104d..5868a77 100644 --- a/Wpf_AiSportsMicrospace/Views/JumpRope/GroupJumpRope.xaml.cs +++ b/Wpf_AiSportsMicrospace/Views/JumpRope/GroupJumpRope.xaml.cs @@ -83,23 +83,25 @@ namespace Wpf_AiSportsMicrospace.Views var app = (App)Application.Current; using var scope = App.AppHost.Services.CreateScope(); var db = scope.ServiceProvider.GetRequiredService(); + + // 获取配置 + var pointCount = await db.BasicConfig + .Where(x => x.Code == AppSettings.Code) + .Select(x => x.HomeWidth) + .FirstOrDefaultAsync(); + var cfg = await db.GroupPoints.FirstOrDefaultAsync(x => x.Code == AppSettings.Code); - List<(double XNorm, double YNorm)> pointlist = new List<(double XNorm, double YNorm)>(); - var points = new[] { cfg.Point1, cfg.Point2, cfg.Point3, - cfg.Point4, cfg.Point5, cfg.Point6, - cfg.Point7, cfg.Point8, cfg.Point9, cfg.Point10 }; - - foreach (var point in points) + if (pointCount == 0 || cfg == null) { - if (point != null) - { - pointlist.Add((point.X, point.Y)); - } + HandyControl.Controls.MessageBox.Error("配置加载失败!", "提示"); + return; } - _groupJumpRopeContext.CirclePositions = pointlist; + + _groupJumpRopeContext.CirclePositions = Utils.GetPointsList(pointCount, cfg); _mainWin.HumanFrameUpdated += OnHumanFrameUpdated; + _mainWin.WebcamClient.StartExtract(); DrawCirclesWithText(); @@ -197,7 +199,7 @@ namespace Wpf_AiSportsMicrospace.Views private void OnHumanFrameUpdated(object sender, List humans) { if (humans == null || humans.Count == 0) return; - //var sportOperate = new SportOperate(); + switch (_currentGameState) { case GameState.NotStarted: // 未开始 @@ -207,9 +209,6 @@ namespace Wpf_AiSportsMicrospace.Views int leftWaving = sportOperate.VerifyLeftWavingAction(humans); if (leftWaving == 5) { - _mainWin.HumanFrameUpdated -= OnHumanFrameUpdated; - _mainWin.WebcamClient.StopExtract(); - // 举左手逻辑,例如结束动画或退出 var newPage = new Home(); _mainWin?.SwitchPageWithMaskAnimation(newPage, true); @@ -223,16 +222,10 @@ namespace Wpf_AiSportsMicrospace.Views switch (rightWaving) { case (int)WavingAction.FirstHand: + _currentGameState = GameState.Running; StartCountdown(3); _isCountdownStarted = true; // 倒计时开始,停止左手检测 break; - //case (int)WavingAction.Raising: - // UpdateCountdown(); - // break; - //case (int)WavingAction.RaiseHand: - // FinishCountdown(); - // _currentGameState = GameState.Running; // 倒计时完成 → 游戏开始 - // break; } } break; @@ -243,74 +236,46 @@ namespace Wpf_AiSportsMicrospace.Views } } - - private double _currentCountdown = 3600; - private DateTime _lastUpdateTime = DateTime.Now; private async void StartCountdown(int start = 3) { _mainWin.ShowCountDownAnimation(); - //_currentCountdown = start; - - //countdownText.Text = _currentCountdown.ToString(); - //countdownGrid.Visibility = Visibility.Visible; - - //_lastUpdateTime = DateTime.Now; Utils.PlayBackgroundMusic("countdown_3.mp3", false); //开始人脸识别============================================= - Action handler = null; + //Action handler = null; - handler = async (frame) => - { - var buffer = frame.GetImageBuffer(ImageFormat.Jpeg).ToArray(); - double[] xValues = [.. _groupJumpRopeContext.CirclePositions.Select(p => p.XNorm)]; + //handler = async (frame) => + //{ + // var buffer = frame.GetImageBuffer(ImageFormat.Jpeg).ToArray(); + // double[] xValues = [.. _groupJumpRopeContext.CirclePositions.Select(p => p.XNorm)]; - var studentList = Utils.FacialRecognition(buffer, xValues); + // var studentList = Utils.FacialRecognition(buffer, xValues); - if (studentList != null && studentList.Count > 0) - { - // 解绑事件 - _mainWin.FacialRecognitionEvent -= handler; + // if (studentList != null && studentList.Count > 0) + // { + // // 解绑事件 + // _mainWin.FacialRecognitionEvent -= handler; - for (int i = 0; i < studentList.Count; i++) - { - var student = studentList[i]; - if (student != null) - { - _groupJumpRopeContext.UserList[i].DisplayText = student.StudentName; - } - } + // for (int i = 0; i < studentList.Count; i++) + // { + // var student = studentList[i]; + // if (student != null) + // { + // _groupJumpRopeContext.UserList[i].DisplayText = student.StudentName; + // } + // } - } + // } - // 这里可以继续处理 studentList - }; + // // 这里可以继续处理 studentList + //}; - // 绑定事件 - _mainWin.FacialRecognitionEvent += handler; + //// 绑定事件 + //_mainWin.FacialRecognitionEvent += handler; await Task.Delay(3000); FinishCountdown(); - _currentGameState = GameState.Running; - } - - private void UpdateCountdown() - { - if ((DateTime.Now - _lastUpdateTime).TotalMilliseconds >= 600) - { - _lastUpdateTime = DateTime.Now; - _currentCountdown -= 600; - - if (_currentCountdown > 0) - { - countdownText.Text = _currentCountdown.ToString(); - } - else - { - FinishCountdown(); - } - } } private async void FinishCountdown() @@ -340,9 +305,6 @@ namespace Wpf_AiSportsMicrospace.Views x.ImageState = "1"; }); - _mainWin.HumanFrameUpdated -= OnHumanFrameUpdated; - _mainWin.WebcamClient.StopExtract(); - RankingItemList = _groupJumpRopeContext.UpdateRankList(); ShowRankingBoard(RankingItemList); Utils.StopBackgroundMusic(); @@ -350,8 +312,6 @@ namespace Wpf_AiSportsMicrospace.Views _currentGameState = GameState.NotStarted; _isCountdownStarted = false; - _mainWin.HumanFrameUpdated += OnHumanFrameUpdated; - _mainWin.WebcamClient.StartExtract(); } private void DrawCirclesWithText() @@ -371,7 +331,10 @@ namespace Wpf_AiSportsMicrospace.Views // 绘制发光圆 var userItem = AddUserItem(x, y, i); // 绑定运动对象 + var sport = SportBase.Create("rope-skipping"); + Optimizes.Optimizeing(sport); + int indexCopy = i; var currentItem = userItem; @@ -391,6 +354,55 @@ namespace Wpf_AiSportsMicrospace.Views _groupJumpRopeContext.Sports.Add(sport); } } + //private void UpdateCircleCounts(List humans) + //{ + // if (humans == null || humans.Count == 0) + // return; + + // double halfW = AppSettings.AreaSize.X; + // double halfH = AppSettings.AreaSize.Y; + + // var circles = _groupJumpRopeContext.CirclePositions; + // var sports = _groupJumpRopeContext.Sports; + + // int circleCount = circles.Count; + + // // 每个圈匹配到的人 + // Human[] circleHumans = new Human[circleCount]; + + // // 1️⃣ 批量匹配圈内的人(保留原验证逻辑) + // foreach (var h in humans) + // { + // for (int i = 0; i < circleCount; i++) + // { + // // 已经有人占用该圈 + // if (circleHumans[i] != null) + // continue; + + // var circle = circles[i]; + // double cx = circle.XNorm; + // double cy = circle.YNorm; + + // // ⭐ 保留你的原验证 + // if (sportOperate.IsAnyFootInCircle(h, cx, cy, halfW, halfH)) + // { + // circleHumans[i] = h; + // break; // 每个人只匹配一个圈 + // } + // } + // } + + // // 2️⃣ 推送跳绳计数 + // for (int i = 0; i < circleCount; i++) + // { + // var human = circleHumans[i]; + // if (human != null) + // { + // sports[i].Pushing(human); + // } + // } + //} + private void UpdateCircleCounts(List humans) { double halfW = AppSettings.AreaSize.X; @@ -418,18 +430,18 @@ namespace Wpf_AiSportsMicrospace.Views bool hasHuman = targetHuman != null; + // 3️⃣ 推送跳绳计数 + if (hasHuman) + { + _groupJumpRopeContext.Sports[i].Pushing(targetHuman); + } + // 2️⃣ 更新视觉状态 lock (_updateLock) { _groupJumpRopeContext.UserList[i].ImageState = GetJumpState(i, hasHuman); } - - // 3️⃣ 推送跳绳计数 - if (hasHuman) - { - _groupJumpRopeContext.Sports[i].Pushing(targetHuman); - } } } diff --git a/Wpf_AiSportsMicrospace/Views/SquatFu/WhackAMole.xaml.cs b/Wpf_AiSportsMicrospace/Views/SquatFu/WhackAMole.xaml.cs index 0d095de..7eb6a9b 100644 --- a/Wpf_AiSportsMicrospace/Views/SquatFu/WhackAMole.xaml.cs +++ b/Wpf_AiSportsMicrospace/Views/SquatFu/WhackAMole.xaml.cs @@ -54,7 +54,7 @@ namespace Views.SquatFu _WhackAMoleContext = new WhackAMoleContext(); } - private async void UserControl_Loaded(object sender, RoutedEventArgs e) + private async void UserControl_Loaded(object sender, RoutedEventArgs e) { // 使用局部变量保存事件处理方法,以便解绑 Action handler = null; @@ -62,27 +62,28 @@ namespace Views.SquatFu var app = (App)Application.Current; using var scope = App.AppHost.Services.CreateScope(); var db = scope.ServiceProvider.GetRequiredService(); + // 获取配置 + var pointCount = await db.BasicConfig + .Where(x => x.Code == AppSettings.Code) + .Select(x => x.HomeWidth) + .FirstOrDefaultAsync(); + var cfg = await db.GroupPoints.FirstOrDefaultAsync(x => x.Code == AppSettings.Code); - List<(double XNorm, double YNorm)> pointlist = new List<(double XNorm, double YNorm)>(); - var points = new[] { cfg.Point1, cfg.Point2, cfg.Point3, - cfg.Point4, cfg.Point5, cfg.Point6, - cfg.Point7, cfg.Point8, cfg.Point9, cfg.Point10 }; - - foreach (var point in points) + if (pointCount == 0 || cfg == null) { - if (point != null) - { - pointlist.Add((point.X, point.Y)); - } + HandyControl.Controls.MessageBox.Error("配置加载失败!", "提示"); + return; } - _WhackAMoleContext.CirclePositions = pointlist; + + + _WhackAMoleContext.CirclePositions = Utils.GetPointsList(pointCount, cfg); DrawCirclesWithText(); // 播放音乐 PlayMusic("raisehand.mp3"); _mainWin.HumanFrameUpdated += OnHumanFrameUpdated; - + _mainWin.WebcamClient.StartExtract(); } private void UserControl_Unloaded(object sender, RoutedEventArgs e) @@ -353,7 +354,7 @@ namespace Views.SquatFu { // 随机决定是地鼠(1)还是兔子(3) int imageState = _random.Next(2) == 0 ? 1 : 3; - if(_WhackAMoleContext.UserList[index].ImageState == "0") + if (_WhackAMoleContext.UserList[index].ImageState == "0") { _WhackAMoleContext.UserList[index].ImageState = imageState.ToString(); } @@ -467,7 +468,7 @@ namespace Views.SquatFu { _WhackAMoleContext.UserList[indexCopy].ImageState = "2"; } - else if(imageState == "3") + else if (imageState == "3") { _WhackAMoleContext.UserList[indexCopy].ImageState = "4"; @@ -515,7 +516,7 @@ namespace Views.SquatFu } } - void AddUserItem(double centerX, double centerY, int index) + void AddUserItem(double centerX, double centerY, int index) { //var userItem = new WhackAMoleUserControl(); //userItem.Width = 400; @@ -547,7 +548,7 @@ namespace Views.SquatFu } - + public void ShowRankingBoard(List list) { var scoreGrid = FindName("ScoreGrid") as Grid;