第一版
This commit is contained in:
parent
458cbe176c
commit
dcbeed1ea8
@ -2,6 +2,7 @@
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using Wpf_AiSportsMicrospace.MyUserControl;
|
using Wpf_AiSportsMicrospace.MyUserControl;
|
||||||
|
using Wpf_AiSportsMicrospace.Views;
|
||||||
using Yztob.AiSports.Postures.Sports;
|
using Yztob.AiSports.Postures.Sports;
|
||||||
|
|
||||||
namespace Dto
|
namespace Dto
|
||||||
@ -13,9 +14,14 @@ namespace Dto
|
|||||||
public List<(double XNorm, double YNorm)> CirclePositions { get; private set; }
|
public List<(double XNorm, double YNorm)> CirclePositions { get; private set; }
|
||||||
public List<SportUserItem> UserList { get; private set; }
|
public List<SportUserItem> UserList { get; private set; }
|
||||||
public List<string> UserNumberList { get; private set; }
|
public List<string> UserNumberList { get; private set; }
|
||||||
public List<SportBase> Sports { get; private set; } // ✅ 新增运动对象
|
public List<SportBase> Sports { get; private set; }
|
||||||
public Dictionary<int, DateTime> LastJumpUpdateTime { get; private set; }
|
public CircleState[] CircleStates { get; private set; }
|
||||||
public List<string> UseNameList = ["四号位", "一号位", "五号位", "二号位", "六号位", "三号位", "七号位",];
|
|
||||||
|
public List<string> UseNameList { get; private set; } = new()
|
||||||
|
{
|
||||||
|
"四号位", "一号位", "五号位", "二号位", "六号位", "三号位", "七号位"
|
||||||
|
};
|
||||||
|
|
||||||
public GroupJumpRopeState()
|
public GroupJumpRopeState()
|
||||||
{
|
{
|
||||||
CirclePositions = new List<(double XNorm, double YNorm)>
|
CirclePositions = new List<(double XNorm, double YNorm)>
|
||||||
@ -31,8 +37,17 @@ namespace Dto
|
|||||||
|
|
||||||
UserList = new List<SportUserItem>();
|
UserList = new List<SportUserItem>();
|
||||||
UserNumberList = new List<string>();
|
UserNumberList = new List<string>();
|
||||||
Sports = new List<SportBase>(); // ✅ 初始化
|
Sports = new List<SportBase>();
|
||||||
LastJumpUpdateTime = new Dictionary<int, DateTime>();
|
|
||||||
|
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)
|
public void Safe(Action action)
|
||||||
@ -47,4 +62,12 @@ namespace Dto
|
|||||||
return func();
|
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"; // 初始静止
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -342,119 +342,13 @@ namespace Wpf_AiSportsMicrospace.Views
|
|||||||
_firstHandTriggered = false;
|
_firstHandTriggered = false;
|
||||||
_lastCountdownSecond = 3;
|
_lastCountdownSecond = 3;
|
||||||
}
|
}
|
||||||
private Dictionary<int, DateTime> _lastJumpUpdateTime = new();
|
|
||||||
private Dictionary<int, DateTime> _lastUIUpdateTime = new();
|
|
||||||
// 状态缓存类
|
|
||||||
class CircleState
|
|
||||||
{
|
|
||||||
public string LastNumberText = "";
|
|
||||||
public DateTime LastActiveTime = DateTime.MinValue; // 最近跳动时间
|
|
||||||
public DateTime LastUIUpdateTime = DateTime.MinValue; // 最近UI更新时间
|
|
||||||
}
|
|
||||||
|
|
||||||
private CircleState[] _circleStates;
|
|
||||||
|
|
||||||
private void InitializeCircleStates()
|
|
||||||
{
|
|
||||||
int count = _groupJumpRopeState.CirclePositions.Count;
|
|
||||||
_circleStates = new CircleState[count];
|
|
||||||
for (int i = 0; i < count; i++)
|
|
||||||
_circleStates[i] = new CircleState();
|
|
||||||
}
|
|
||||||
|
|
||||||
// 安全访问状态并更新
|
|
||||||
private Human LocateHumanSafe(List<Human> humans, double frameWidth, double frameHeight, int index)
|
|
||||||
{
|
|
||||||
var state = _circleStates[index];
|
|
||||||
var user = _groupJumpRopeState.UserList[index];
|
|
||||||
|
|
||||||
if (humans == null || humans.Count == 0)
|
|
||||||
{
|
|
||||||
// 没人时,判断是否切回静止
|
|
||||||
if ((DateTime.Now - state.LastActiveTime).TotalSeconds >= 2)
|
|
||||||
user.ImageState = "1";
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
var circle = _groupJumpRopeState.CirclePositions[index];
|
|
||||||
double radiusX = 0.073;
|
|
||||||
double radiusY = 0.135;
|
|
||||||
|
|
||||||
Human inCircleHuman = null;
|
|
||||||
bool isOut = false;
|
|
||||||
|
|
||||||
foreach (var hu in humans)
|
|
||||||
{
|
|
||||||
var right = hu.Keypoints.FirstOrDefault(k => k.Name == "right_ankle");
|
|
||||||
var left = hu.Keypoints.FirstOrDefault(k => k.Name == "left_ankle");
|
|
||||||
if (right == null || left == null) continue;
|
|
||||||
|
|
||||||
double xR = right.X / frameWidth;
|
|
||||||
double yR = right.Y / frameHeight;
|
|
||||||
double xL = left.X / frameWidth;
|
|
||||||
double yL = left.Y / frameHeight;
|
|
||||||
|
|
||||||
bool outOfCircle =
|
|
||||||
xR < (circle.XNorm - radiusX) || xR > (circle.XNorm + radiusX) ||
|
|
||||||
xL < (circle.XNorm - radiusX) || xL > (circle.XNorm + radiusX) ||
|
|
||||||
yR < (circle.YNorm - radiusY) || yR > (circle.YNorm + radiusY) ||
|
|
||||||
yL < (circle.YNorm - radiusY) || yL > (circle.YNorm + radiusY);
|
|
||||||
|
|
||||||
if (outOfCircle)
|
|
||||||
isOut = true;
|
|
||||||
else if (inCircleHuman == null)
|
|
||||||
inCircleHuman = hu;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 决定目标状态
|
|
||||||
string targetState;
|
|
||||||
DateTime now = DateTime.Now;
|
|
||||||
|
|
||||||
if (isOut)
|
|
||||||
{
|
|
||||||
targetState = "3"; // 出圈
|
|
||||||
}
|
|
||||||
else if (inCircleHuman != null)
|
|
||||||
{
|
|
||||||
targetState = "2"; // 运动中
|
|
||||||
state.LastActiveTime = now;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// 圈内无人,根据上次活跃时间判断是否切回静止
|
|
||||||
targetState = (now - state.LastActiveTime).TotalSeconds >= 2 ? "1" : "2";
|
|
||||||
}
|
|
||||||
|
|
||||||
// UI 防抖:每 500ms 更新一次
|
|
||||||
if ((now - state.LastUIUpdateTime).TotalMilliseconds > 500 && user.ImageState != targetState)
|
|
||||||
{
|
|
||||||
user.ImageState = targetState;
|
|
||||||
state.LastUIUpdateTime = now;
|
|
||||||
}
|
|
||||||
|
|
||||||
return inCircleHuman;
|
|
||||||
}
|
|
||||||
private void UpdateCircleCounts(List<Human> humans)
|
|
||||||
{
|
|
||||||
double frameWidth = userBox.ActualWidth;
|
|
||||||
double frameHeight = userBox.ActualHeight;
|
|
||||||
|
|
||||||
for (int i = 0; i < _groupJumpRopeState.CirclePositions.Count; i++)
|
|
||||||
{
|
|
||||||
var human = LocateHumanSafe(humans, frameWidth, frameHeight, i);
|
|
||||||
if (human != null)
|
|
||||||
{
|
|
||||||
_groupJumpRopeState.Sports[i].Pushing(human);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void DrawCirclesWithText()
|
private void DrawCirclesWithText()
|
||||||
{
|
{
|
||||||
userBox.Children.Clear();
|
userBox.Children.Clear();
|
||||||
_groupJumpRopeState.Sports.Clear();
|
_groupJumpRopeState.Sports.Clear();
|
||||||
_groupJumpRopeState.UserNumberList.Clear();
|
_groupJumpRopeState.UserList.Clear();
|
||||||
_groupJumpRopeState.UserList.Clear(); // ✅ 别忘了清空
|
|
||||||
|
|
||||||
double imgWidth = userBox.ActualWidth;
|
double imgWidth = userBox.ActualWidth;
|
||||||
double imgHeight = userBox.ActualHeight;
|
double imgHeight = userBox.ActualHeight;
|
||||||
@ -466,8 +360,6 @@ namespace Wpf_AiSportsMicrospace.Views
|
|||||||
double y = pos.YNorm * imgHeight;
|
double y = pos.YNorm * imgHeight;
|
||||||
|
|
||||||
var userItem = AddUserItem(x, y, i);
|
var userItem = AddUserItem(x, y, i);
|
||||||
_groupJumpRopeState.UserList.Add(userItem);
|
|
||||||
_groupJumpRopeState.UserNumberList.Add("0");
|
|
||||||
|
|
||||||
var sport = SportBase.Create("rope-skipping");
|
var sport = SportBase.Create("rope-skipping");
|
||||||
int indexCopy = i;
|
int indexCopy = i;
|
||||||
@ -479,10 +371,101 @@ namespace Wpf_AiSportsMicrospace.Views
|
|||||||
if (currentItem.ImageState != "2")
|
if (currentItem.ImageState != "2")
|
||||||
currentItem.ImageState = "2";
|
currentItem.ImageState = "2";
|
||||||
};
|
};
|
||||||
|
|
||||||
|
sport.PointThreshold = 0.03f;
|
||||||
sport.Start();
|
sport.Start();
|
||||||
_groupJumpRopeState.Sports.Add(sport);
|
_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)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
return _groupJumpRopeState.Safe(() =>
|
||||||
|
{
|
||||||
|
var circle = _groupJumpRopeState.CirclePositions[index];
|
||||||
|
var user = _groupJumpRopeState.UserList[index];
|
||||||
|
var state = _groupJumpRopeState.CircleStates[index];
|
||||||
|
double radiusX = 0.073;
|
||||||
|
double radiusY = 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 xR = rightFoot.X / frameWidth;
|
||||||
|
double xL = leftFoot.X / frameWidth;
|
||||||
|
double yR = rightFoot.Y / frameHeight;
|
||||||
|
double yL = leftFoot.Y / frameHeight;
|
||||||
|
|
||||||
|
bool outOfCircle = xR < circle.XNorm - radiusX || xR > circle.XNorm + radiusX ||
|
||||||
|
xL < circle.XNorm - radiusX || xL > circle.XNorm + radiusX ||
|
||||||
|
yR < circle.YNorm - radiusY || yR > circle.YNorm + radiusY ||
|
||||||
|
yL < circle.YNorm - radiusY || yL > circle.YNorm + radiusY;
|
||||||
|
|
||||||
|
if (outOfCircle) isOut = true;
|
||||||
|
else if (inCircleHuman == null) inCircleHuman = hu;
|
||||||
|
}
|
||||||
|
|
||||||
|
var now = DateTime.Now;
|
||||||
|
|
||||||
|
// 更新状态
|
||||||
|
if (isOut)
|
||||||
|
{
|
||||||
|
if (state.ImageState != "3" && (now - state.LastUIUpdateTime).TotalMilliseconds > 500)
|
||||||
|
{
|
||||||
|
Application.Current.Dispatcher.Invoke(() => user.ImageState = "3");
|
||||||
|
state.ImageState = "3";
|
||||||
|
state.LastUIUpdateTime = now;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (state.LastNumberText == user.NumberText)
|
||||||
|
{
|
||||||
|
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;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
private void UpdateCircleCounts(List<Human> humans)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < _groupJumpRopeState.CirclePositions.Count; i++)
|
||||||
|
{
|
||||||
|
var human = LocateHuman(humans, userBox.ActualWidth, userBox.ActualHeight, i);
|
||||||
|
if (human != null)
|
||||||
|
{
|
||||||
|
// 推动运动对象
|
||||||
|
_groupJumpRopeState.Safe(() => _groupJumpRopeState.Sports[i].Pushing(human));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
InitializeCircleStates();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -502,46 +485,5 @@ namespace Wpf_AiSportsMicrospace.Views
|
|||||||
_groupJumpRopeState.UserList.Add(userItem);
|
_groupJumpRopeState.UserList.Add(userItem);
|
||||||
return userItem; //
|
return userItem; //
|
||||||
}
|
}
|
||||||
|
|
||||||
private readonly ConcurrentDictionary<int, (string lastState, DateTime lastUpdate)> _uiUpdateCache
|
|
||||||
= new ConcurrentDictionary<int, (string lastState, DateTime lastUpdate)>();
|
|
||||||
|
|
||||||
private void SetImageStateSafe(int index, string state)
|
|
||||||
{
|
|
||||||
if (index < 0 || index >= _groupJumpRopeState.UserList.Count) return;
|
|
||||||
|
|
||||||
var now = DateTime.Now;
|
|
||||||
|
|
||||||
if (_uiUpdateCache.TryGetValue(index, out var cache))
|
|
||||||
{
|
|
||||||
// 如果状态没变或更新太频繁(<300ms),跳过
|
|
||||||
if (cache.lastState == state && (now - cache.lastUpdate).TotalMilliseconds < 300)
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
_uiUpdateCache[index] = (state, now);
|
|
||||||
|
|
||||||
if (Application.Current.Dispatcher.CheckAccess())
|
|
||||||
{
|
|
||||||
_groupJumpRopeState.UserList[index].ImageState = state;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Application.Current.Dispatcher.BeginInvoke(new Action(() =>
|
|
||||||
{
|
|
||||||
if (index >= 0 && index < _groupJumpRopeState.UserList.Count)
|
|
||||||
_groupJumpRopeState.UserList[index].ImageState = state;
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class CircleState
|
|
||||||
{
|
|
||||||
public string ImageState = "1"; // 当前显示状态
|
|
||||||
public string LastNumberText = ""; // 上一次的 NumberText
|
|
||||||
public DateTime LastActiveTime = DateTime.MinValue; // 上次活动时间(运动中)
|
|
||||||
public DateTime LastUIUpdateTime = DateTime.MinValue; // UI 防抖时间
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user