Compare commits

..

No commits in common. "69453a1baa9a4163998f04cd2f4fb4e79dd245a9" and "72bf221637477011bc84795c5af8b312733317c6" have entirely different histories.

2 changed files with 139 additions and 126 deletions

View File

@ -2,37 +2,19 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Drawing; using System.Drawing;
using System.Text; using System.Text;
using Wpf_AiSportsMicrospace.MyUserControl;
using Yztob.AiSports.Postures.Sports; using Yztob.AiSports.Postures.Sports;
namespace Dto namespace Dto
{ {
public class GroupJumpRopeContext public class GroupJumpRopeContext
{ {
public List<(double XNorm, double YNorm)> CirclePositions { get; private set; } public int Index { get; set; }
public List<SportUserItem> UserList { get; private set; } public Point NormalizedPosition { get; set; }
public List<string> UserNumberList { get; private set; } public SportBase Sport { get; set; }
public List<SportBase> Sports { get; private set; } public UserItem User { get; set; }
public List<string> UseNameList { get; private set; } = new()
{
"一号位", "四号位", "二号位", "五号位", "三号位", "六号位"
};
public GroupJumpRopeContext()
{
CirclePositions = new List<(double XNorm, double YNorm)>
{
(0.21, 0.88 ),
(0.36, 0.68 ),
(0.50, 0.88),
(0.64, 0.68 ),
(0.78, 0.88),
(0.92, 0.68 )
};
UserList = new List<SportUserItem>(); public string LastNumberText { get; set; } = "";
UserNumberList = new List<string>(); public DateTime LastJumpTime { get; set; } = DateTime.MinValue;
Sports = new List<SportBase>(); public DateTime LastUIUpdateTime { get; set; } = DateTime.MinValue;
}
} }
} }

View File

@ -1,5 +1,4 @@
using Dto; using Emgu.CV.Flann;
using Emgu.CV.Flann;
using HandyControl.Controls; using HandyControl.Controls;
using SharpDX.Direct3D9; using SharpDX.Direct3D9;
using System; using System;
@ -42,18 +41,22 @@ namespace Wpf_AiSportsMicrospace.Views
/// </summary> /// </summary>
public partial class GroupJumpRope : UserControl public partial class GroupJumpRope : UserControl
{ {
private List<SportBase> sports = new();
private List<TextBlock> circleTexts = new();
private List<SportUserItem> userList = new();
private List<string> userNumberList = ["0", "0", "0", "0", "0", "0"];
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; private Main _mainWin => Application.Current.MainWindow as Main;
private List<(double XNorm, double YNorm)> circlePositions = new();
private MediaPlayer _mediaPlayer = new MediaPlayer(); private MediaPlayer _mediaPlayer = new MediaPlayer();
private bool IsGameStarted = false; private bool IsGameStarted = false;
private readonly object _updateLock = new object(); List<string> _useName = ["一号位", "四号位", "二号位", "五号位", "三号位", "六号位",];
private readonly Dictionary<int, (string lastNumber, DateTime lastChangeTime, string currentState)> _jumpStatus = new Dictionary<int, (string, DateTime, string)>();
private GroupJumpRopeContext _groupJumpRopeContext;
public GroupJumpRope() public GroupJumpRope()
{ {
InitializeComponent(); InitializeComponent();
Loaded += UserControl_Loaded; Loaded += UserControl_Loaded;
Unloaded += UserControl_Unloaded; Unloaded += UserControl_Unloaded;
_groupJumpRopeContext = new GroupJumpRopeContext();
} }
private async void UserControl_Loaded(object sender, RoutedEventArgs e) private async void UserControl_Loaded(object sender, RoutedEventArgs e)
@ -258,7 +261,7 @@ namespace Wpf_AiSportsMicrospace.Views
countdownGrid.Visibility = Visibility.Hidden; countdownGrid.Visibility = Visibility.Hidden;
_groupJumpRopeContext.UserList.ForEach(x => userList.ForEach(x =>
{ {
x.ImageState = "1"; x.ImageState = "1";
}); });
@ -268,6 +271,7 @@ namespace Wpf_AiSportsMicrospace.Views
} }
private DateTime? _raiseStartTime; private DateTime? _raiseStartTime;
private DateTime? _wristStartTime;
private bool _firstHandTriggered; private bool _firstHandTriggered;
private int _lastCountdownSecond = 3; private int _lastCountdownSecond = 3;
private DateTime? _countdownStartTime; private DateTime? _countdownStartTime;
@ -336,24 +340,119 @@ namespace Wpf_AiSportsMicrospace.Views
private void ResetRaiseState() private void ResetRaiseState()
{ {
_raiseStartTime = null; _raiseStartTime = null;
_wristStartTime = null;
_countdownStartTime = null; _countdownStartTime = null;
_firstHandTriggered = false; _firstHandTriggered = false;
_lastCountdownSecond = 3; _lastCountdownSecond = 3;
} }
private Dictionary<int, DateTime> _lastJumpUpdateTime = new(); // 用于存储最后更新时间
private Dictionary<int, DateTime> _lastUIUpdateTime = new(); // 用于防抖处理
public Human LocateHuman(List<Human> humans, double frameWidth, double frameHeight, int circleIndex)
{
if (humans == null || humans.Count == 0)
{
return null;
}
double circleX = circlePositions[circleIndex].XNorm;
double circleY = circlePositions[circleIndex].YNorm;
double radiusNormX = 0.073;
double radiusNormY = 0.135;
Human inCircleHuman = null;
bool isOut = false;
foreach (var hu in humans)
{
var rightFoot = hu.Keypoints.FirstOrDefault(k => k.Name == "right_ankle");
var leftFoot = hu.Keypoints.FirstOrDefault(k => k.Name == "left_ankle");
if (rightFoot == null || leftFoot == null)
continue;
double xRightNorm = rightFoot.X / frameWidth;
double xLeftNorm = leftFoot.X / frameWidth;
double yRightNorm = rightFoot.Y / frameHeight;
double yLeftNorm = leftFoot.Y / frameHeight;
bool outOfCircle =
xRightNorm < (circleX - radiusNormX) || xRightNorm > (circleX + radiusNormX) ||
xLeftNorm < (circleX - radiusNormX) || xLeftNorm > (circleX + radiusNormX) ||
yRightNorm < (circleY - radiusNormY) || yRightNorm > (circleY + radiusNormY) ||
yLeftNorm < (circleY - radiusNormY) || yLeftNorm > (circleY + radiusNormY);
if (outOfCircle)
{
isOut = true;
}
else if (inCircleHuman == null)
{
inCircleHuman = hu;
}
}
if (isOut)
userList[circleIndex].ImageState = "3";
else
{
//if (userList[circleIndex].ImageState == "3")
//{
// userList[circleIndex].ImageState = "1";
//}
// 检查当前编号是否未变化超过 2 秒
if (userNumberList[circleIndex] == userList[circleIndex].NumberText)
{
// 获取上次变化时间
if (!_lastJumpUpdateTime.TryGetValue(circleIndex, out var lastTime))
lastTime = DateTime.Now; // 默认当前时间
var elapsed = (DateTime.Now - lastTime).TotalSeconds;
if (elapsed > 1)
{
userList[circleIndex].ImageState = "1";
_lastJumpUpdateTime[circleIndex] = DateTime.Now;
}
// 否则2 秒内仍在变化,不更新状态
}
else
{
// 编号发生变化,更新记录,并保持状态不变
userNumberList[circleIndex] = userList[circleIndex].NumberText;
_lastJumpUpdateTime[circleIndex] = DateTime.Now;
}
}
return inCircleHuman;
}
private void DrawCirclesWithText() private void DrawCirclesWithText()
{ {
userBox.Children.Clear(); userBox.Children.Clear();
_groupJumpRopeContext.Sports.Clear(); sports.Clear();
_groupJumpRopeContext.UserList.Clear(); circleTexts.Clear();
_groupJumpRopeContext.UserNumberList.Clear(); userList.Clear(); // 清空用户控件列表
double imgWidth = userBox.ActualWidth; double imgWidth = userBox.ActualWidth;
double imgHeight = userBox.ActualHeight; double imgHeight = userBox.ActualHeight;
double radius = 100; double radius = 100;
for (int i = 0; i < _groupJumpRopeContext.CirclePositions.Count; i++) // 每个圆的位置X 和 Y 都归一化 0~1
circlePositions = new List<(double XNorm, double YNorm)>
{
//(0.07, 0.58),
(0.21, 0.88 ),
(0.36, 0.68 ),
(0.50, 0.88),
(0.64, 0.68 ),
(0.78, 0.88),
(0.92, 0.68 )
};
for (int i = 0; i < circlePositions.Count; i++)
{ {
var pos = _groupJumpRopeContext.CirclePositions[i]; var pos = circlePositions[i];
double x = pos.XNorm * imgWidth; double x = pos.XNorm * imgWidth;
double y = pos.YNorm * imgHeight; double y = pos.YNorm * imgHeight;
@ -368,104 +467,37 @@ namespace Wpf_AiSportsMicrospace.Views
// 订阅事件 // 订阅事件
sport.OnTicked += (count, times) => sport.OnTicked += (count, times) =>
{ {
// 更新UI currentItem.NumberText = count.ToString();
userItem.NumberText = count.ToString();
// 更新数字源
_groupJumpRopeContext.UserNumberList[indexCopy] = count.ToString();
// 改变状态为“跳绳中”
if (userItem.ImageState != "2")
userItem.ImageState = "2";
};
if (currentItem.ImageState != "2")
{
currentItem.ImageState = "2";
}
};
sport.PointThreshold = 0.03f;
sport.Start(); sport.Start();
_groupJumpRopeContext.Sports.Add(sport); sports.Add(sport);
} }
} }
private void UpdateCircleCounts(List<Human> humans) private void UpdateCircleCounts(List<Human> humans)
{ {
double radiusNormX = 0.07; // 在后台线程运行检测逻辑
double radiusNormY = 0.14; //Parallel.For(0, circlePositions.Count, i =>
//{
// var human = LocateHuman(humans, userBox.ActualWidth, userBox.ActualHeight, i);
// sports[i].Pushing(human);
//});
for (int i = 0; i < _groupJumpRopeContext.CirclePositions.Count; i++) for (int i = 0; i < circlePositions.Count; i++)
{ {
var circleX = _groupJumpRopeContext.CirclePositions[i].XNorm; var human = LocateHuman(humans, userBox.ActualWidth, userBox.ActualHeight, i);
var circleY = _groupJumpRopeContext.CirclePositions[i].YNorm; if (human != null)
Human humanInCircle = null;
// 找圈内的人
foreach (var hu in humans)
{ {
var rightFoot = hu.Keypoints.FirstOrDefault(k => k.Name == "right_ankle"); sports[i].Pushing(human);
var leftFoot = hu.Keypoints.FirstOrDefault(k => k.Name == "left_ankle");
if (rightFoot == null || leftFoot == null)
continue;
double xRightNorm = rightFoot.X / userBox.ActualWidth;
double xLeftNorm = leftFoot.X / userBox.ActualWidth;
double yRightNorm = rightFoot.Y / userBox.ActualHeight;
double yLeftNorm = leftFoot.Y / userBox.ActualHeight;
bool outOfCircle =
xRightNorm < (circleX - radiusNormX) || xRightNorm > (circleX + radiusNormX) ||
xLeftNorm < (circleX - radiusNormX) || xLeftNorm > (circleX + radiusNormX) ||
yRightNorm < (circleY - radiusNormY) || yRightNorm > (circleY + radiusNormY) ||
yLeftNorm < (circleY - radiusNormY) || yLeftNorm > (circleY + radiusNormY);
if (!outOfCircle)
{
humanInCircle = hu;
break; // 每圈只处理一个人
}
} }
// 根据是否有人和跳绳数字判断状态
bool hasHuman = humanInCircle != null;
lock (_updateLock)
{
_groupJumpRopeContext.UserList[i].ImageState = GetJumpState(i, hasHuman);
}
// 推送计数
if (hasHuman)
_groupJumpRopeContext.Sports[i].Pushing(humanInCircle);
} }
} }
private string GetJumpState(int circleIndex, bool humanInCircle)
{
if (!humanInCircle)
{
// 无人 → 出圈
return "3";
}
string currentNumber = _groupJumpRopeContext.UserNumberList[circleIndex];
if (!_jumpStatus.ContainsKey(circleIndex))
{
_jumpStatus[circleIndex] = (currentNumber, DateTime.Now, "1"); // 初始状态先为停止
return "1";
}
var (lastNumber, lastChangeTime, lastState) = _jumpStatus[circleIndex];
if (currentNumber != lastNumber)
{
// 数字变化 → 跳绳中
_jumpStatus[circleIndex] = (currentNumber, DateTime.Now, "2");
return "2";
}
// 数字未变化,判断是否超过 2 秒
double elapsed = (DateTime.Now - lastChangeTime).TotalSeconds;
if (elapsed >= 0.8)
{
// 超过 2 秒未变化 → 停止
_jumpStatus[circleIndex] = (currentNumber, lastChangeTime, "1");
return "1";
}
// 维持上次状态
return lastState;
}
/// <summary> /// <summary>
/// 添加带渐变光的圆圈(中心红色,边缘蓝色) /// 添加带渐变光的圆圈(中心红色,边缘蓝色)
@ -475,15 +507,14 @@ namespace Wpf_AiSportsMicrospace.Views
var userItem = new SportUserItem(); var userItem = new SportUserItem();
userItem.Width = 270; userItem.Width = 270;
userItem.Height = 560; userItem.Height = 560;
userItem.DisplayText = _groupJumpRopeContext.UseNameList[index]; userItem.DisplayText = _useName[index];
userItem.VerticalAlignment = VerticalAlignment.Top; userItem.VerticalAlignment = VerticalAlignment.Top;
userItem.HorizontalAlignment = HorizontalAlignment.Left; userItem.HorizontalAlignment = HorizontalAlignment.Left;
userItem.ImageState = "1"; userItem.ImageState = "1";
//userItem.Margin = new Thickness(centerX - (index == 0 ? 80 : index == 6 ? 190 : 135), centerY - 540, 0, 0); //userItem.Margin = new Thickness(centerX - (index == 0 ? 80 : index == 6 ? 190 : 135), centerY - 540, 0, 0);
userItem.Margin = new Thickness(centerX - 265, centerY - 500, 0, 0); userItem.Margin = new Thickness(centerX - 265, centerY - 500, 0, 0);
userBox.Children.Add(userItem); userBox.Children.Add(userItem);
_groupJumpRopeContext.UserList.Add(userItem); userList.Add(userItem);
_groupJumpRopeContext.UserNumberList.Add("0");
return userItem; // return userItem; //
} }
} }