2025-09-26 16:46:22 +08:00
|
|
|
|
using HandyControl.Controls;
|
2025-12-30 11:32:35 +08:00
|
|
|
|
using SportExpand;
|
2025-09-26 16:46:22 +08:00
|
|
|
|
using System;
|
|
|
|
|
|
using System.Collections.Generic;
|
|
|
|
|
|
using System.Linq;
|
|
|
|
|
|
using System.Text;
|
|
|
|
|
|
using System.Threading.Tasks;
|
|
|
|
|
|
using System.Windows;
|
|
|
|
|
|
using Wpf_AiSportsMicrospace.Enum;
|
|
|
|
|
|
using Yztob.AiSports.Inferences.Abstractions;
|
|
|
|
|
|
using Yztob.AiSports.Inferences.Things;
|
|
|
|
|
|
using Yztob.AiSports.Postures;
|
|
|
|
|
|
using Yztob.AiSports.Postures.Abstractions;
|
|
|
|
|
|
using Yztob.AiSports.Postures.Sports;
|
|
|
|
|
|
using Yztob.AiSports.Postures.Things;
|
|
|
|
|
|
using Yztob.AiSports.Sensors.Abstractions;
|
|
|
|
|
|
using Yztob.AiSports.Sensors.Things;
|
|
|
|
|
|
|
|
|
|
|
|
namespace Wpf_AiSportsMicrospace.Common
|
|
|
|
|
|
{
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 运动检测操作类
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
public class SportOperate
|
|
|
|
|
|
{
|
|
|
|
|
|
IPointTracker _leftTracker;
|
|
|
|
|
|
IPointTracker _rightTracker;
|
2025-12-30 11:32:35 +08:00
|
|
|
|
private int _leftWaveCount = 0;
|
|
|
|
|
|
private int _rightWaveCount = 0;
|
|
|
|
|
|
private const int WaveThreshold = 4;
|
|
|
|
|
|
//IPointTracker _leftElbow;
|
|
|
|
|
|
//IPointTracker _rightElbow;
|
2025-09-26 16:46:22 +08:00
|
|
|
|
WebcamClient _webcamClient;
|
|
|
|
|
|
|
|
|
|
|
|
private DateTime _lastActionTime = DateTime.MinValue;
|
|
|
|
|
|
|
|
|
|
|
|
private Point? _lastLeftWrist = null;
|
2025-12-30 11:32:35 +08:00
|
|
|
|
//private Point? _lastRightWrist = null;
|
2025-09-26 16:46:22 +08:00
|
|
|
|
private DateTime? _raiseStartTime = null;
|
|
|
|
|
|
private DateTime? _wristStartTime = null;
|
2025-09-28 14:08:36 +08:00
|
|
|
|
private double _leftWristDeltaX = 0;
|
2025-12-30 11:32:35 +08:00
|
|
|
|
//private double _rightWristDeltaX = 0;
|
2025-09-28 14:08:36 +08:00
|
|
|
|
private bool _firstHandTriggered = false;
|
2025-09-26 16:46:22 +08:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public SportOperate()
|
|
|
|
|
|
{
|
|
|
|
|
|
_leftTracker = PostureCalculate.CreatePointTracker("left_wrist", 0);
|
|
|
|
|
|
_rightTracker = PostureCalculate.CreatePointTracker("right_wrist", 0);
|
2025-12-30 11:32:35 +08:00
|
|
|
|
//_leftElbow = PostureCalculate.CreatePointTracker("left_elbow", 0);
|
|
|
|
|
|
//_rightElbow = PostureCalculate.CreatePointTracker("right_elbow", 0);
|
2025-09-26 16:46:22 +08:00
|
|
|
|
|
2025-12-30 11:32:35 +08:00
|
|
|
|
_leftTracker.Amplitude = 0.3f;
|
|
|
|
|
|
_rightTracker.Amplitude = 0.3f;
|
2025-09-28 09:17:44 +08:00
|
|
|
|
//_leftElbow.Amplitude = 0.03f;
|
|
|
|
|
|
//_rightElbow.Amplitude = 0.03f;
|
2025-09-26 16:46:22 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public WebcamClient CreateRTSP()
|
|
|
|
|
|
{
|
2025-10-12 10:20:08 +08:00
|
|
|
|
try
|
|
|
|
|
|
{
|
2025-12-23 17:20:59 +08:00
|
|
|
|
_webcamClient = WebcamClient.CreateRTSP("172.17.30.64", "admin", "yd708090", 554u);
|
2025-10-12 10:20:08 +08:00
|
|
|
|
}
|
|
|
|
|
|
catch (Exception)
|
|
|
|
|
|
{
|
|
|
|
|
|
HandyControl.Controls.MessageBox.Show("摄像头网络异常,请重新连接!");
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-09-26 16:46:22 +08:00
|
|
|
|
return _webcamClient;
|
|
|
|
|
|
}
|
2025-10-15 10:01:58 +08:00
|
|
|
|
public int VerifyWavingAction(List<Human> humans)
|
2025-09-26 16:46:22 +08:00
|
|
|
|
{
|
2025-10-15 10:01:58 +08:00
|
|
|
|
if (humans == null || humans.Count == 0)
|
2025-10-12 10:20:08 +08:00
|
|
|
|
return (int)WavingAction.None;
|
2025-10-11 14:54:57 +08:00
|
|
|
|
|
2025-10-15 10:01:58 +08:00
|
|
|
|
foreach (var human in humans)
|
2025-09-26 16:46:22 +08:00
|
|
|
|
{
|
2025-10-15 10:01:58 +08:00
|
|
|
|
var rightAnkle = human.Keypoints.FirstOrDefault(k => k.Name == "right_ankle");
|
|
|
|
|
|
if (rightAnkle == null)
|
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
|
|
double xNorm = rightAnkle.X / 1920;
|
|
|
|
|
|
double yNorm = rightAnkle.Y / 1080;
|
|
|
|
|
|
|
|
|
|
|
|
// 仅检测中心区域内的人
|
|
|
|
|
|
if (!(xNorm >= 0.44 && xNorm <= 0.57 && yNorm >= 0.81))
|
|
|
|
|
|
continue;
|
|
|
|
|
|
|
2025-12-30 11:32:35 +08:00
|
|
|
|
//var leftWrist = human.Keypoints.FirstOrDefault(x => x.Name == "left_wrist");
|
|
|
|
|
|
//var leftElbow = human.Keypoints.FirstOrDefault(x => x.Name == "left_elbow");
|
|
|
|
|
|
//var rightWrist = human.Keypoints.FirstOrDefault(x => x.Name == "right_wrist");
|
|
|
|
|
|
//var rightElbow = human.Keypoints.FirstOrDefault(x => x.Name == "right_elbow");
|
2025-10-15 10:01:58 +08:00
|
|
|
|
|
|
|
|
|
|
// --- 右手举手优先 ---
|
|
|
|
|
|
int rightHandAction = DetectRightHandRaise(human);
|
|
|
|
|
|
if (rightHandAction != (int)WavingAction.None)
|
|
|
|
|
|
return rightHandAction;
|
|
|
|
|
|
|
2025-12-30 11:32:35 +08:00
|
|
|
|
var leftValue = _leftTracker.Tracking(human);
|
|
|
|
|
|
var rightValue = _rightTracker.Tracking(human);
|
|
|
|
|
|
|
|
|
|
|
|
// ===== 左挥手判定 =====
|
|
|
|
|
|
if (leftValue == 1 || leftValue == 2)
|
|
|
|
|
|
_leftWaveCount++;
|
|
|
|
|
|
//else
|
|
|
|
|
|
// _leftWaveCount = 0;
|
|
|
|
|
|
|
|
|
|
|
|
// ===== 右挥手判定 =====
|
|
|
|
|
|
if (rightValue == 1 || rightValue == 2)
|
|
|
|
|
|
_rightWaveCount++;
|
|
|
|
|
|
//else
|
|
|
|
|
|
// _rightWaveCount = 0;
|
|
|
|
|
|
|
|
|
|
|
|
// ===== 触发判断 =====
|
|
|
|
|
|
if (_leftWaveCount >= WaveThreshold)
|
|
|
|
|
|
{
|
|
|
|
|
|
_leftWaveCount = 0; // 只重置左手
|
|
|
|
|
|
return (int)WavingAction.LeftWave;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (_rightWaveCount >= WaveThreshold)
|
2025-10-15 10:01:58 +08:00
|
|
|
|
{
|
2025-12-30 11:32:35 +08:00
|
|
|
|
_rightWaveCount = 0; // 只重置右手
|
|
|
|
|
|
return (int)WavingAction.RightWave;
|
2025-10-15 10:01:58 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-12-30 11:32:35 +08:00
|
|
|
|
// --- 左手挥手 ---
|
|
|
|
|
|
//if (leftWrist != null && leftElbow != null)
|
|
|
|
|
|
//{
|
|
|
|
|
|
// int leftResult = DetectHorizontalWave(
|
|
|
|
|
|
// new Point(leftWrist.X, leftWrist.Y),
|
|
|
|
|
|
// new Point(leftElbow.X, leftElbow.Y),
|
|
|
|
|
|
// ref _lastLeftWrist,
|
|
|
|
|
|
// ref _leftWristDeltaX,
|
|
|
|
|
|
// true);
|
|
|
|
|
|
// if (leftResult != (int)WavingAction.None)
|
|
|
|
|
|
// return leftResult;
|
|
|
|
|
|
|
|
|
|
|
|
|
2025-10-15 10:01:58 +08:00
|
|
|
|
// --- 右手挥手 ---
|
2025-12-23 17:20:59 +08:00
|
|
|
|
//if (rightWrist != null && rightElbow != null)
|
|
|
|
|
|
//{
|
|
|
|
|
|
// int rightWaveResult = DetectHorizontalWave(
|
|
|
|
|
|
// new Point(rightWrist.X, rightWrist.Y),
|
|
|
|
|
|
// new Point(rightElbow.X, rightElbow.Y),
|
|
|
|
|
|
// ref _lastRightWrist,
|
|
|
|
|
|
// ref _rightWristDeltaX,
|
|
|
|
|
|
// false);
|
|
|
|
|
|
// if (rightWaveResult != (int)WavingAction.None)
|
|
|
|
|
|
// return rightWaveResult;
|
|
|
|
|
|
//}
|
2025-10-12 16:13:44 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return (int)WavingAction.None;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 检测右手举手状态
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <param name="human">人体关键点信息</param>
|
|
|
|
|
|
/// <returns>返回 WavingAction:FirstHand / Raising / RaiseHand / None</returns>
|
|
|
|
|
|
public int DetectRightHandRaise(Human human)
|
|
|
|
|
|
{
|
2025-12-30 11:32:35 +08:00
|
|
|
|
//var rightWrist = human.Keypoints.FirstOrDefault(k => k.Name == "right_wrist");
|
|
|
|
|
|
//var rightElbow = human.Keypoints.FirstOrDefault(k => k.Name == "right_elbow");
|
2025-10-12 16:13:44 +08:00
|
|
|
|
|
2025-12-30 11:32:35 +08:00
|
|
|
|
//if (rightWrist == null || rightElbow == null)
|
|
|
|
|
|
// return (int)WavingAction.None;
|
|
|
|
|
|
|
|
|
|
|
|
//double verticalRise = rightElbow.Y - rightWrist.Y; // 手腕在肘上方 → 正值
|
|
|
|
|
|
//const double raiseThreshold = 60; // 举手阈值,可根据实际调整
|
2025-10-12 16:13:44 +08:00
|
|
|
|
|
2025-12-30 11:32:35 +08:00
|
|
|
|
var calculator = PostureCalculate.CreateCalculator();
|
|
|
|
|
|
var rule = RaiseHand.RaiseRightHand();
|
|
|
|
|
|
var pass = calculator.Calculating(human, rule);
|
2025-09-28 14:08:36 +08:00
|
|
|
|
|
2025-12-30 11:32:35 +08:00
|
|
|
|
if (pass)
|
2025-10-12 16:13:44 +08:00
|
|
|
|
{
|
|
|
|
|
|
// 初始化计时
|
|
|
|
|
|
if (_raiseStartTime == null) _raiseStartTime = DateTime.Now;
|
|
|
|
|
|
if (_wristStartTime == null) _wristStartTime = DateTime.Now;
|
|
|
|
|
|
|
2025-12-24 12:37:08 +08:00
|
|
|
|
|
2025-10-12 16:13:44 +08:00
|
|
|
|
var wristDuration = DateTime.Now - _wristStartTime.Value;
|
|
|
|
|
|
|
|
|
|
|
|
if (!_firstHandTriggered && wristDuration.TotalSeconds >= 1)
|
2025-09-28 14:08:36 +08:00
|
|
|
|
{
|
2025-10-12 16:13:44 +08:00
|
|
|
|
_firstHandTriggered = true;
|
|
|
|
|
|
return (int)WavingAction.FirstHand; // 举手开始
|
2025-09-26 16:46:22 +08:00
|
|
|
|
}
|
2025-10-12 16:13:44 +08:00
|
|
|
|
|
|
|
|
|
|
var duration = DateTime.Now - _raiseStartTime.Value;
|
2025-10-18 10:44:56 +08:00
|
|
|
|
if (duration.TotalSeconds >= 1)
|
2025-09-28 14:08:36 +08:00
|
|
|
|
{
|
|
|
|
|
|
_raiseStartTime = null;
|
|
|
|
|
|
_wristStartTime = null;
|
2025-10-12 16:13:44 +08:00
|
|
|
|
_firstHandTriggered = false; // 重置状态
|
|
|
|
|
|
return (int)WavingAction.RaiseHand; // 举手完成
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
return (int)WavingAction.Raising; // 举手中
|
2025-09-28 14:08:36 +08:00
|
|
|
|
}
|
2025-09-26 16:46:22 +08:00
|
|
|
|
}
|
2025-10-12 16:13:44 +08:00
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
// 手放下,重置状态
|
|
|
|
|
|
_raiseStartTime = null;
|
|
|
|
|
|
_wristStartTime = null;
|
|
|
|
|
|
_firstHandTriggered = false;
|
|
|
|
|
|
return (int)WavingAction.None;
|
|
|
|
|
|
}
|
2025-09-26 16:46:22 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
2025-09-28 14:08:36 +08:00
|
|
|
|
/// 统一的水平挥手检测
|
2025-09-26 16:46:22 +08:00
|
|
|
|
/// </summary>
|
2025-09-28 14:08:36 +08:00
|
|
|
|
private int DetectHorizontalWave(Point wrist, Point elbow, ref Point? lastWrist, ref double deltaX, bool isLeft)
|
2025-09-26 16:46:22 +08:00
|
|
|
|
{
|
2025-09-28 14:08:36 +08:00
|
|
|
|
if (lastWrist != null)
|
2025-09-26 16:46:22 +08:00
|
|
|
|
{
|
2025-09-28 14:08:36 +08:00
|
|
|
|
double dx = wrist.X - lastWrist.Value.X;
|
|
|
|
|
|
double dy = Math.Abs(wrist.Y - lastWrist.Value.Y);
|
2025-09-26 16:46:22 +08:00
|
|
|
|
|
2025-09-28 14:08:36 +08:00
|
|
|
|
// 累计水平位移
|
|
|
|
|
|
deltaX += dx;
|
2025-09-26 16:46:22 +08:00
|
|
|
|
|
2025-09-28 14:08:36 +08:00
|
|
|
|
if (Math.Abs(deltaX) > 40 && dy < 60 && Math.Abs(wrist.Y - elbow.Y) < 60)
|
2025-09-26 16:46:22 +08:00
|
|
|
|
{
|
2025-09-28 14:08:36 +08:00
|
|
|
|
deltaX = 0; // 重置
|
|
|
|
|
|
if (CheckCooldown())
|
|
|
|
|
|
{
|
|
|
|
|
|
return isLeft ? (int)WavingAction.LeftWave : (int)WavingAction.RightWave;
|
|
|
|
|
|
}
|
2025-09-26 16:46:22 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-09-28 14:08:36 +08:00
|
|
|
|
lastWrist = wrist;
|
2025-09-26 16:46:22 +08:00
|
|
|
|
return (int)WavingAction.None;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 冷却防抖(避免重复触发)
|
|
|
|
|
|
/// </summary>
|
2025-09-28 14:08:36 +08:00
|
|
|
|
private bool CheckCooldown(int cooldownMs = 1000)
|
2025-09-26 16:46:22 +08:00
|
|
|
|
{
|
|
|
|
|
|
if ((DateTime.Now - _lastActionTime).TotalMilliseconds < cooldownMs)
|
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
|
|
_lastActionTime = DateTime.Now;
|
|
|
|
|
|
return true;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|