第一版

This commit is contained in:
tanglong 2025-10-14 09:46:14 +08:00
parent 458cbe176c
commit dcbeed1ea8
2 changed files with 121 additions and 156 deletions

View File

@ -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"; // 初始静止
}
} }

View File

@ -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 防抖时间
}
} }