Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| dcbeed1ea8 | |||
| 458cbe176c |
73
Wpf_AiSportsMicrospace/Dto/GroupJumpRopeState.cs
Normal file
73
Wpf_AiSportsMicrospace/Dto/GroupJumpRopeState.cs
Normal file
@ -0,0 +1,73 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Text;
|
||||||
|
using Wpf_AiSportsMicrospace.MyUserControl;
|
||||||
|
using Wpf_AiSportsMicrospace.Views;
|
||||||
|
using Yztob.AiSports.Postures.Sports;
|
||||||
|
|
||||||
|
namespace Dto
|
||||||
|
{
|
||||||
|
public class GroupJumpRopeState
|
||||||
|
{
|
||||||
|
private readonly object _lock = new();
|
||||||
|
|
||||||
|
public List<(double XNorm, double YNorm)> CirclePositions { get; private set; }
|
||||||
|
public List<SportUserItem> UserList { get; private set; }
|
||||||
|
public List<string> UserNumberList { get; private set; }
|
||||||
|
public List<SportBase> Sports { get; private set; }
|
||||||
|
public CircleState[] CircleStates { get; private set; }
|
||||||
|
|
||||||
|
public List<string> UseNameList { get; private set; } = new()
|
||||||
|
{
|
||||||
|
"四号位", "一号位", "五号位", "二号位", "六号位", "三号位", "七号位"
|
||||||
|
};
|
||||||
|
|
||||||
|
public GroupJumpRopeState()
|
||||||
|
{
|
||||||
|
CirclePositions = new List<(double XNorm, double YNorm)>
|
||||||
|
{
|
||||||
|
(0.07, 0.58),
|
||||||
|
(0.21, 0.88),
|
||||||
|
(0.36, 0.58),
|
||||||
|
(0.50, 0.88),
|
||||||
|
(0.64, 0.58),
|
||||||
|
(0.78, 0.88),
|
||||||
|
(0.92, 0.58)
|
||||||
|
};
|
||||||
|
|
||||||
|
UserList = new List<SportUserItem>();
|
||||||
|
UserNumberList = new List<string>();
|
||||||
|
Sports = new List<SportBase>();
|
||||||
|
|
||||||
|
InitializeCircleStates();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void InitializeCircleStates()
|
||||||
|
{
|
||||||
|
int count = CirclePositions.Count;
|
||||||
|
CircleStates = new CircleState[count];
|
||||||
|
for (int i = 0; i < count; i++)
|
||||||
|
CircleStates[i] = new CircleState();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Safe(Action action)
|
||||||
|
{
|
||||||
|
lock (_lock)
|
||||||
|
action();
|
||||||
|
}
|
||||||
|
|
||||||
|
public T Safe<T>(Func<T> func)
|
||||||
|
{
|
||||||
|
lock (_lock)
|
||||||
|
return func();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public class CircleState
|
||||||
|
{
|
||||||
|
public string LastNumberText { get; set; } = "";
|
||||||
|
public DateTime LastJumpTime { get; set; } = DateTime.MinValue;
|
||||||
|
public DateTime LastUIUpdateTime { get; set; } = DateTime.MinValue;
|
||||||
|
public string ImageState { get; set; } = "1"; // 初始静止
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@ -1,4 +1,6 @@
|
|||||||
using Emgu.CV.Flann;
|
using Dto;
|
||||||
|
using Emgu.CV.Features2D;
|
||||||
|
using Emgu.CV.Flann;
|
||||||
using HandyControl.Controls;
|
using HandyControl.Controls;
|
||||||
using SharpDX.Direct3D9;
|
using SharpDX.Direct3D9;
|
||||||
using System;
|
using System;
|
||||||
@ -41,28 +43,23 @@ namespace Wpf_AiSportsMicrospace.Views
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public partial class GroupJumpRope : UserControl
|
public partial class GroupJumpRope : UserControl
|
||||||
{
|
{
|
||||||
private List<SportBase> sports = new();
|
private GroupJumpRopeState _groupJumpRopeState = new();
|
||||||
private List<TextBlock> circleTexts = new();
|
|
||||||
private List<SportUserItem> userList = new();
|
|
||||||
private List<string> userNumberList = ["0", "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;
|
||||||
List<string> _useName = ["四号位", "一号位", "五号位", "二号位", "六号位", "三号位", "七号位",];
|
|
||||||
public GroupJumpRope()
|
public GroupJumpRope()
|
||||||
{
|
{
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
Loaded += UserControl_Loaded;
|
Loaded += UserControl_Loaded;
|
||||||
Unloaded += UserControl_Unloaded;
|
Unloaded += UserControl_Unloaded;
|
||||||
|
|
||||||
|
_groupJumpRopeState = new GroupJumpRopeState();
|
||||||
}
|
}
|
||||||
private async void UserControl_Loaded(object sender, RoutedEventArgs e)
|
private async void UserControl_Loaded(object sender, RoutedEventArgs e)
|
||||||
{
|
{
|
||||||
DrawCirclesWithText();
|
DrawCirclesWithText();
|
||||||
|
|
||||||
// 播放音乐
|
// 播放音乐
|
||||||
PlayMusic("raisehand.mp3");
|
PlayMusic("raisehand.mp3");
|
||||||
}
|
}
|
||||||
@ -261,7 +258,7 @@ namespace Wpf_AiSportsMicrospace.Views
|
|||||||
|
|
||||||
countdownGrid.Visibility = Visibility.Hidden;
|
countdownGrid.Visibility = Visibility.Hidden;
|
||||||
|
|
||||||
userList.ForEach(x =>
|
_groupJumpRopeState.UserList.ForEach(x =>
|
||||||
{
|
{
|
||||||
x.ImageState = "1";
|
x.ImageState = "1";
|
||||||
});
|
});
|
||||||
@ -345,19 +342,54 @@ namespace Wpf_AiSportsMicrospace.Views
|
|||||||
_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)
|
private void DrawCirclesWithText()
|
||||||
|
{
|
||||||
|
userBox.Children.Clear();
|
||||||
|
_groupJumpRopeState.Sports.Clear();
|
||||||
|
_groupJumpRopeState.UserList.Clear();
|
||||||
|
|
||||||
|
double imgWidth = userBox.ActualWidth;
|
||||||
|
double imgHeight = userBox.ActualHeight;
|
||||||
|
|
||||||
|
for (int i = 0; i < _groupJumpRopeState.CirclePositions.Count; i++)
|
||||||
|
{
|
||||||
|
var pos = _groupJumpRopeState.CirclePositions[i];
|
||||||
|
double x = pos.XNorm * imgWidth;
|
||||||
|
double y = pos.YNorm * imgHeight;
|
||||||
|
|
||||||
|
var userItem = AddUserItem(x, y, i);
|
||||||
|
|
||||||
|
var sport = SportBase.Create("rope-skipping");
|
||||||
|
int indexCopy = i;
|
||||||
|
var currentItem = userItem;
|
||||||
|
|
||||||
|
sport.OnTicked += (count, times) =>
|
||||||
|
{
|
||||||
|
currentItem.NumberText = count.ToString();
|
||||||
|
if (currentItem.ImageState != "2")
|
||||||
|
currentItem.ImageState = "2";
|
||||||
|
};
|
||||||
|
|
||||||
|
sport.PointThreshold = 0.03f;
|
||||||
|
sport.Start();
|
||||||
|
_groupJumpRopeState.Sports.Add(sport);
|
||||||
|
_groupJumpRopeState.UserList.Add(userItem);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public Human LocateHuman(List<Human> humans, double frameWidth, double frameHeight, int index)
|
||||||
{
|
{
|
||||||
if (humans == null || humans.Count == 0)
|
if (humans == null || humans.Count == 0)
|
||||||
{
|
|
||||||
return null;
|
return null;
|
||||||
}
|
|
||||||
|
|
||||||
double circleX = circlePositions[circleIndex].XNorm;
|
return _groupJumpRopeState.Safe(() =>
|
||||||
double circleY = circlePositions[circleIndex].YNorm;
|
{
|
||||||
double radiusNormX = 0.073;
|
var circle = _groupJumpRopeState.CirclePositions[index];
|
||||||
double radiusNormY = 0.135;
|
var user = _groupJumpRopeState.UserList[index];
|
||||||
|
var state = _groupJumpRopeState.CircleStates[index];
|
||||||
|
double radiusX = 0.073;
|
||||||
|
double radiusY = 0.135;
|
||||||
|
|
||||||
Human inCircleHuman = null;
|
Human inCircleHuman = null;
|
||||||
bool isOut = false;
|
bool isOut = false;
|
||||||
@ -366,139 +398,76 @@ namespace Wpf_AiSportsMicrospace.Views
|
|||||||
{
|
{
|
||||||
var rightFoot = hu.Keypoints.FirstOrDefault(k => k.Name == "right_ankle");
|
var rightFoot = hu.Keypoints.FirstOrDefault(k => k.Name == "right_ankle");
|
||||||
var leftFoot = hu.Keypoints.FirstOrDefault(k => k.Name == "left_ankle");
|
var leftFoot = hu.Keypoints.FirstOrDefault(k => k.Name == "left_ankle");
|
||||||
if (rightFoot == null || leftFoot == null)
|
if (rightFoot == null || leftFoot == null) continue;
|
||||||
continue;
|
|
||||||
|
|
||||||
double xRightNorm = rightFoot.X / frameWidth;
|
double xR = rightFoot.X / frameWidth;
|
||||||
double xLeftNorm = leftFoot.X / frameWidth;
|
double xL = leftFoot.X / frameWidth;
|
||||||
double yRightNorm = rightFoot.Y / frameHeight;
|
double yR = rightFoot.Y / frameHeight;
|
||||||
double yLeftNorm = leftFoot.Y / frameHeight;
|
double yL = leftFoot.Y / frameHeight;
|
||||||
|
|
||||||
bool outOfCircle =
|
bool outOfCircle = xR < circle.XNorm - radiusX || xR > circle.XNorm + radiusX ||
|
||||||
xRightNorm < (circleX - radiusNormX) || xRightNorm > (circleX + radiusNormX) ||
|
xL < circle.XNorm - radiusX || xL > circle.XNorm + radiusX ||
|
||||||
xLeftNorm < (circleX - radiusNormX) || xLeftNorm > (circleX + radiusNormX) ||
|
yR < circle.YNorm - radiusY || yR > circle.YNorm + radiusY ||
|
||||||
yRightNorm < (circleY - radiusNormY) || yRightNorm > (circleY + radiusNormY) ||
|
yL < circle.YNorm - radiusY || yL > circle.YNorm + radiusY;
|
||||||
yLeftNorm < (circleY - radiusNormY) || yLeftNorm > (circleY + radiusNormY);
|
|
||||||
|
|
||||||
if (outOfCircle)
|
if (outOfCircle) isOut = true;
|
||||||
{
|
else if (inCircleHuman == null) inCircleHuman = hu;
|
||||||
isOut = true;
|
|
||||||
}
|
|
||||||
else if (inCircleHuman == null)
|
|
||||||
{
|
|
||||||
inCircleHuman = hu;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
var now = DateTime.Now;
|
||||||
|
|
||||||
|
// 更新状态
|
||||||
if (isOut)
|
if (isOut)
|
||||||
userList[circleIndex].ImageState = "3";
|
|
||||||
else
|
|
||||||
{
|
{
|
||||||
//if (userList[circleIndex].ImageState == "3")
|
if (state.ImageState != "3" && (now - state.LastUIUpdateTime).TotalMilliseconds > 500)
|
||||||
//{
|
|
||||||
// userList[circleIndex].ImageState = "1";
|
|
||||||
//}
|
|
||||||
|
|
||||||
// 检查当前编号是否未变化超过 2 秒
|
|
||||||
if (userNumberList[circleIndex] == userList[circleIndex].NumberText)
|
|
||||||
{
|
{
|
||||||
// 获取上次变化时间
|
Application.Current.Dispatcher.Invoke(() => user.ImageState = "3");
|
||||||
if (!_lastJumpUpdateTime.TryGetValue(circleIndex, out var lastTime))
|
state.ImageState = "3";
|
||||||
lastTime = DateTime.Now; // 默认当前时间
|
state.LastUIUpdateTime = now;
|
||||||
|
|
||||||
var elapsed = (DateTime.Now - lastTime).TotalSeconds;
|
|
||||||
|
|
||||||
if (elapsed > 1)
|
|
||||||
{
|
|
||||||
userList[circleIndex].ImageState = "1";
|
|
||||||
_lastJumpUpdateTime[circleIndex] = DateTime.Now;
|
|
||||||
}
|
}
|
||||||
// 否则:2 秒内仍在变化,不更新状态
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// 编号发生变化,更新记录,并保持状态不变
|
if (state.LastNumberText == user.NumberText)
|
||||||
userNumberList[circleIndex] = userList[circleIndex].NumberText;
|
{
|
||||||
_lastJumpUpdateTime[circleIndex] = DateTime.Now;
|
var elapsed = (now - state.LastJumpTime).TotalSeconds;
|
||||||
|
if (elapsed > 2 && (now - state.LastUIUpdateTime).TotalMilliseconds > 500)
|
||||||
|
{
|
||||||
|
Application.Current.Dispatcher.Invoke(() => user.ImageState = "1");
|
||||||
|
state.ImageState = "1";
|
||||||
|
state.LastUIUpdateTime = now;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
state.LastNumberText = user.NumberText;
|
||||||
|
state.LastJumpTime = now;
|
||||||
|
|
||||||
|
if (state.ImageState != "2" && (now - state.LastUIUpdateTime).TotalMilliseconds > 500)
|
||||||
|
{
|
||||||
|
Application.Current.Dispatcher.Invoke(() => user.ImageState = "2");
|
||||||
|
state.ImageState = "2";
|
||||||
|
state.LastUIUpdateTime = now;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return inCircleHuman;
|
return inCircleHuman;
|
||||||
}
|
});
|
||||||
|
|
||||||
|
|
||||||
private void DrawCirclesWithText()
|
|
||||||
{
|
|
||||||
userBox.Children.Clear();
|
|
||||||
sports.Clear();
|
|
||||||
circleTexts.Clear();
|
|
||||||
userList.Clear(); // 清空用户控件列表
|
|
||||||
|
|
||||||
double imgWidth = userBox.ActualWidth;
|
|
||||||
double imgHeight = userBox.ActualHeight;
|
|
||||||
double radius = 100;
|
|
||||||
|
|
||||||
// 每个圆的位置:X 和 Y 都归一化 0~1
|
|
||||||
circlePositions = new List<(double XNorm, double YNorm)>
|
|
||||||
{
|
|
||||||
(0.07, 0.58),
|
|
||||||
(0.21, 0.88 ),
|
|
||||||
(0.36, 0.58 ),
|
|
||||||
(0.50, 0.88),
|
|
||||||
(0.64, 0.58 ),
|
|
||||||
(0.78, 0.88),
|
|
||||||
(0.92, 0.58 )
|
|
||||||
};
|
|
||||||
|
|
||||||
for (int i = 0; i < circlePositions.Count; i++)
|
|
||||||
{
|
|
||||||
var pos = circlePositions[i];
|
|
||||||
double x = pos.XNorm * imgWidth;
|
|
||||||
double y = pos.YNorm * imgHeight;
|
|
||||||
|
|
||||||
// 绘制发光圆
|
|
||||||
//AddGlowEllipse(x, y, overlayCanvas);
|
|
||||||
var userItem = AddUserItem(x, y, i);
|
|
||||||
// 绑定运动对象
|
|
||||||
var sport = SportBase.Create("rope-skipping");
|
|
||||||
int indexCopy = i;
|
|
||||||
var currentItem = userItem;
|
|
||||||
|
|
||||||
// 订阅事件
|
|
||||||
sport.OnTicked += (count, times) =>
|
|
||||||
{
|
|
||||||
currentItem.NumberText = count.ToString();
|
|
||||||
|
|
||||||
if (currentItem.ImageState != "2")
|
|
||||||
{
|
|
||||||
currentItem.ImageState = "2";
|
|
||||||
}
|
|
||||||
};
|
|
||||||
sport.PointThreshold = 0.03f;
|
|
||||||
sport.Start();
|
|
||||||
sports.Add(sport);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
private void UpdateCircleCounts(List<Human> humans)
|
private void UpdateCircleCounts(List<Human> humans)
|
||||||
{
|
{
|
||||||
// 在后台线程运行检测逻辑
|
for (int i = 0; i < _groupJumpRopeState.CirclePositions.Count; i++)
|
||||||
//Parallel.For(0, circlePositions.Count, i =>
|
|
||||||
//{
|
|
||||||
// var human = LocateHuman(humans, userBox.ActualWidth, userBox.ActualHeight, i);
|
|
||||||
// sports[i].Pushing(human);
|
|
||||||
//});
|
|
||||||
|
|
||||||
for (int i = 0; i < circlePositions.Count; i++)
|
|
||||||
{
|
{
|
||||||
var human = LocateHuman(humans, userBox.ActualWidth, userBox.ActualHeight, i);
|
var human = LocateHuman(humans, userBox.ActualWidth, userBox.ActualHeight, i);
|
||||||
if (human != null)
|
if (human != null)
|
||||||
{
|
{
|
||||||
sports[i].Pushing(human);
|
// 推动运动对象
|
||||||
|
_groupJumpRopeState.Safe(() => _groupJumpRopeState.Sports[i].Pushing(human));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 添加带渐变光的圆圈(中心红色,边缘蓝色)
|
/// 添加带渐变光的圆圈(中心红色,边缘蓝色)
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -507,13 +476,13 @@ 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 = _useName[index];
|
userItem.DisplayText = _groupJumpRopeState.UseNameList[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);
|
||||||
userBox.Children.Add(userItem);
|
userBox.Children.Add(userItem);
|
||||||
userList.Add(userItem);
|
_groupJumpRopeState.UserList.Add(userItem);
|
||||||
return userItem; //
|
return userItem; //
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user