挥手 举手
This commit is contained in:
parent
638f039aab
commit
c9393ebd3e
@ -28,11 +28,12 @@ namespace Wpf_AiSportsMicrospace.Common
|
||||
IPointTracker _rightElbow;
|
||||
WebcamClient _webcamClient;
|
||||
|
||||
private DateTime _lastActionTime = DateTime.MinValue;
|
||||
|
||||
private Point? _lastLeftWrist = null;
|
||||
private Point? _lastRightWrist = null;
|
||||
private DateTime _lastActionTime = DateTime.MinValue;
|
||||
// 记录举手开始时间
|
||||
private DateTime? _raiseStartTime = null;
|
||||
private DateTime? _wristStartTime = null;
|
||||
|
||||
|
||||
public SportOperate()
|
||||
@ -42,10 +43,10 @@ namespace Wpf_AiSportsMicrospace.Common
|
||||
_leftElbow = PostureCalculate.CreatePointTracker("left_elbow", 0);
|
||||
_rightElbow = PostureCalculate.CreatePointTracker("right_elbow", 0);
|
||||
|
||||
_leftTracker.Amplitude = 0.05f;
|
||||
_rightTracker.Amplitude = 0.05f;
|
||||
_leftElbow.Amplitude = 0.05f;
|
||||
_rightElbow.Amplitude = 0.05f;
|
||||
//_leftTracker.Amplitude = 0.05f;
|
||||
//_rightTracker.Amplitude = 0.05f;
|
||||
//_leftElbow.Amplitude = 0.05f;
|
||||
//_rightElbow.Amplitude = 0.05f;
|
||||
}
|
||||
|
||||
public WebcamClient CreateRTSP()
|
||||
@ -83,49 +84,82 @@ namespace Wpf_AiSportsMicrospace.Common
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 识别左手动作(左→右为 1)
|
||||
/// 统一的水平挥手检测
|
||||
/// </summary>
|
||||
public int RecognizeLeftHandGesture(Point wrist, Point elbow)
|
||||
private int DetectHorizontalWave(Point wrist, Point elbow, Point? lastWrist, bool isLeft)
|
||||
{
|
||||
if (_lastLeftWrist != null)
|
||||
if (lastWrist != null)
|
||||
{
|
||||
double dx = wrist.X - _lastLeftWrist.Value.X;
|
||||
double dy = Math.Abs(wrist.Y - _lastLeftWrist.Value.Y);
|
||||
double dx = wrist.X - lastWrist.Value.X;
|
||||
double dy = Math.Abs(wrist.Y - lastWrist.Value.Y);
|
||||
|
||||
if (Math.Abs(dx) > 30 && dy < 40)
|
||||
// 挥手:水平位移明显,垂直位移小,且接近肘部水平
|
||||
if (Math.Abs(dx) > 30 && dy < 40 && Math.Abs(wrist.Y - elbow.Y) < 100)
|
||||
{
|
||||
if (CheckCooldown())
|
||||
{
|
||||
if (dx > 0)
|
||||
return 1; // 左手往右挥
|
||||
if (isLeft && dx > 0)
|
||||
return (int)WavingAction.LeftWave; // 左手往右挥
|
||||
if (!isLeft && dx < 0)
|
||||
return (int)WavingAction.RightWave; // 右手往左挥
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_lastLeftWrist = wrist;
|
||||
return 0;
|
||||
return (int)WavingAction.None;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 识别右手动作(右→左为 2,举手为 3)
|
||||
/// 识别左手动作
|
||||
/// </summary>
|
||||
public int RecognizeLeftHandGesture(Point wrist, Point elbow)
|
||||
{
|
||||
int result = DetectHorizontalWave(wrist, elbow, _lastLeftWrist, true);
|
||||
_lastLeftWrist = wrist; // 更新记录
|
||||
return result;
|
||||
}
|
||||
private bool _firstHandTriggered = false;
|
||||
|
||||
/// <summary>
|
||||
/// 识别右手动作
|
||||
/// </summary>
|
||||
/// <param name="wrist"></param>
|
||||
/// <param name="elbow"></param>
|
||||
/// <returns></returns>
|
||||
public int RecognizeRightHandGesture(Point wrist, Point elbow)
|
||||
{
|
||||
// 判断手腕是否在肘部上方,并且达到高度阈值
|
||||
//if (wrist.Y + 40 < elbow.Y && wrist.Y < minRaiseHeight)
|
||||
if (wrist.Y + 40 < elbow.Y)
|
||||
// --- 先判断水平挥手 ---
|
||||
int waveResult = DetectHorizontalWave(wrist, elbow, _lastRightWrist, false);
|
||||
_lastRightWrist = wrist; // 更新记录
|
||||
if (waveResult != (int)WavingAction.None)
|
||||
return waveResult;
|
||||
|
||||
// --- 举手逻辑 ---
|
||||
double verticalRise = elbow.Y - wrist.Y; // 手腕在肘上方 → 正值
|
||||
if (verticalRise > 100) // 举手阈值
|
||||
{
|
||||
// 初始化计时
|
||||
if (_raiseStartTime == null)
|
||||
{
|
||||
_raiseStartTime = DateTime.Now;
|
||||
return (int)WavingAction.FirstHand;
|
||||
|
||||
if (_wristStartTime == null)
|
||||
_wristStartTime = DateTime.Now;
|
||||
|
||||
var wristDuration = DateTime.Now - _wristStartTime.Value;
|
||||
|
||||
// 保持 >1 秒才触发一次 FirstHand
|
||||
if (!_firstHandTriggered && wristDuration.TotalSeconds >= 1)
|
||||
{
|
||||
_firstHandTriggered = true;
|
||||
return (int)WavingAction.FirstHand; // 举手开始,只触发一次
|
||||
}
|
||||
|
||||
// 判断是否完成3秒举手
|
||||
var duration = DateTime.Now - _raiseStartTime.Value;
|
||||
if (duration.TotalSeconds >= 3)
|
||||
if (duration.TotalSeconds >= 4)
|
||||
{
|
||||
_raiseStartTime = null;
|
||||
_wristStartTime = null;
|
||||
_firstHandTriggered = false; // 重置状态
|
||||
return (int)WavingAction.RaiseHand; // 举手完成
|
||||
}
|
||||
else
|
||||
@ -135,33 +169,20 @@ namespace Wpf_AiSportsMicrospace.Common
|
||||
}
|
||||
else
|
||||
{
|
||||
// 手放下,重置计时和状态
|
||||
_raiseStartTime = null;
|
||||
_wristStartTime = null;
|
||||
_firstHandTriggered = false;
|
||||
}
|
||||
|
||||
// 挥手逻辑
|
||||
if (_lastRightWrist != null)
|
||||
{
|
||||
double dx = wrist.X - _lastRightWrist.Value.X;
|
||||
double dy = Math.Abs(wrist.Y - _lastRightWrist.Value.Y);
|
||||
|
||||
if (Math.Abs(dx) > 30 && dy < 40)
|
||||
{
|
||||
if (CheckCooldown())
|
||||
{
|
||||
if (dx < 0)
|
||||
return 2; // 右手往左挥
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_lastRightWrist = wrist;
|
||||
return 0;
|
||||
return (int)WavingAction.None;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 冷却防抖(避免重复触发)
|
||||
/// </summary>
|
||||
private bool CheckCooldown(int cooldownMs = 500)
|
||||
private bool CheckCooldown(int cooldownMs = 1000)
|
||||
{
|
||||
if ((DateTime.Now - _lastActionTime).TotalMilliseconds < cooldownMs)
|
||||
return false;
|
||||
|
||||
@ -8,12 +8,12 @@ namespace Wpf_AiSportsMicrospace.Enum
|
||||
{
|
||||
public enum WavingAction
|
||||
{
|
||||
None = 0, // 没动作
|
||||
LeftWave = 1, // 左挥手
|
||||
RightWave = 2, // 右挥手
|
||||
RaiseHand = 3, // 举手完成
|
||||
Raising = 4, // 举手中,未完成
|
||||
FirstHand = 5,//首次举手
|
||||
None = 0,
|
||||
LeftWave = 1,
|
||||
RightWave = 2,
|
||||
FirstHand = 3, // 举手开始
|
||||
Raising = 4, // 举手中
|
||||
RaiseHand = 5 // 举手完成
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -2,8 +2,8 @@
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:local="clr-namespace:Wpf_AiSportsMicrospace.MyUserControl"
|
||||
Title="Home" Height="600" Width="800" Loaded="Window_Loaded">
|
||||
<Grid>
|
||||
Title="Home" Height="1080" Width="1920" Loaded="Window_Loaded">
|
||||
<Grid Height="1080" Width="1920">
|
||||
<local:CoverFlowControl1 x:Name="coverFlow" HorizontalAlignment="Center" VerticalAlignment="Center"/>
|
||||
</Grid>
|
||||
</Window>
|
||||
|
||||
@ -80,6 +80,7 @@ namespace Wpf_AiSportsMicrospace
|
||||
_webcamClient.StartExtract();
|
||||
|
||||
StartFrameProcessing();
|
||||
coverFlow.ProgressCompleted += CoverFlow_ProgressCompleted;
|
||||
}
|
||||
|
||||
private void CoverFlow_ProgressCompleted(CoverFlowItem item)
|
||||
@ -90,6 +91,7 @@ namespace Wpf_AiSportsMicrospace
|
||||
_cts.Cancel(); // 停止后台处理线程
|
||||
_webcamClient?.StopExtract(); // 停止摄像头抽帧
|
||||
_webcamClient = null;
|
||||
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
@ -97,7 +99,7 @@ namespace Wpf_AiSportsMicrospace
|
||||
}
|
||||
|
||||
// 解绑事件,防止重复触发
|
||||
coverFlow.ProgressCompleted -= CoverFlow_ProgressCompleted;
|
||||
//coverFlow.ProgressCompleted -= CoverFlow_ProgressCompleted;
|
||||
|
||||
// 根据图片跳转新窗口
|
||||
string uri = item.ImageUri.ToString();
|
||||
@ -132,6 +134,15 @@ namespace Wpf_AiSportsMicrospace
|
||||
}
|
||||
else
|
||||
{
|
||||
//_webcamClient.OnExtractFrame += frame =>
|
||||
//{
|
||||
// if (frame != null)
|
||||
// _frameQueue.Enqueue(frame);
|
||||
//};
|
||||
//_webcamClient.StartExtract();
|
||||
|
||||
|
||||
_webcamClient.StartExtract();
|
||||
Thread.Sleep(5);
|
||||
}
|
||||
}
|
||||
@ -163,32 +174,33 @@ namespace Wpf_AiSportsMicrospace
|
||||
//检测挥手动作
|
||||
var wavingaction = _sportOperate.VerifyWavingAction(human);
|
||||
|
||||
if (wavingaction == (int)WavingAction.Raising)
|
||||
// 把低 8 位作为动作类型,高 8 位作为进度
|
||||
int actionType = wavingaction & 0xFF;
|
||||
int progress = (wavingaction >> 8) & 0xFF;
|
||||
|
||||
switch (actionType)
|
||||
{
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
switch (wavingaction)
|
||||
{
|
||||
case (int)WavingAction.LeftWave: // 1
|
||||
Dispatcher.BeginInvoke(() => coverFlow.SlideLeft());
|
||||
break;
|
||||
case (int)WavingAction.RightWave: // 2
|
||||
Dispatcher.BeginInvoke(() => coverFlow.SlideRight());
|
||||
break;
|
||||
case (int)WavingAction.RaiseHand: // 3 举手完成
|
||||
coverFlow.ProgressCompleted -= CoverFlow_ProgressCompleted;
|
||||
coverFlow.ProgressCompleted += CoverFlow_ProgressCompleted;
|
||||
break;
|
||||
case (int)WavingAction.Raising: // 4 举手中
|
||||
break;
|
||||
case 5: // 如果还有其他动作
|
||||
Dispatcher.BeginInvoke(() => coverFlow.StartSelectedProgress());
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
case (int)WavingAction.LeftWave: // 左手挥动
|
||||
Dispatcher.BeginInvoke(() => coverFlow.SlideRight());
|
||||
break;
|
||||
|
||||
case (int)WavingAction.RightWave: // 右手挥动
|
||||
Dispatcher.BeginInvoke(() => coverFlow.SlideLeft());
|
||||
break;
|
||||
|
||||
case (int)WavingAction.FirstHand: // 举手开始
|
||||
Dispatcher.BeginInvoke(() => coverFlow.StartSelectedProgress());
|
||||
break;
|
||||
|
||||
case (int)WavingAction.Raising: // 举手中,实时更新进度
|
||||
break;
|
||||
|
||||
case (int)WavingAction.RaiseHand: // 举手完成
|
||||
_cts.Cancel();
|
||||
break;
|
||||
default: // 没有动作 → 取消进度
|
||||
Dispatcher.BeginInvoke(() => coverFlow.CancelSelectedProgress());
|
||||
break;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
|
||||
@ -10,7 +10,7 @@
|
||||
<local:ProgressToRectangleGeometryConverter x:Key="ProgressToRectangleGeometryConverter"/>
|
||||
</UserControl.Resources>
|
||||
|
||||
<Grid Background="Transparent">
|
||||
<Grid Background="Transparent" Width="300" Height="400">
|
||||
<ItemsControl x:Name="ItemsHost" ItemsSource="{Binding Images, RelativeSource={RelativeSource AncestorType=UserControl}}">
|
||||
<ItemsControl.ItemsPanel>
|
||||
<ItemsPanelTemplate>
|
||||
@ -30,12 +30,12 @@
|
||||
</TransformGroup>
|
||||
</Border.RenderTransform>
|
||||
|
||||
<Canvas Width="150" Height="200">
|
||||
<Image Source="{Binding ImageUri}" Width="150" Height="200" Stretch="UniformToFill"/>
|
||||
<Canvas Width="300" Height="400">
|
||||
<Image Source="{Binding ImageUri}" Width="300" Height="400" Stretch="UniformToFill"/>
|
||||
|
||||
<!-- 矩形进度条 -->
|
||||
<Path Stroke="Yellow" StrokeThickness="4"
|
||||
Data="{Binding Progress, Converter={StaticResource ProgressToRectangleGeometryConverter}, ConverterParameter='150,200'}"/>
|
||||
<Path Stroke="Red" StrokeThickness="20"
|
||||
Data="{Binding Progress, Converter={StaticResource ProgressToRectangleGeometryConverter}, ConverterParameter='300,400'}"/>
|
||||
<!-- 火花 -->
|
||||
<Ellipse x:Name="Spark" Width="8" Height="8" Visibility="Collapsed">
|
||||
<Ellipse.Fill>
|
||||
|
||||
@ -193,9 +193,9 @@ namespace Wpf_AiSportsMicrospace.MyUserControl
|
||||
private void UpdateLayoutWithAnimation(bool instant = false)
|
||||
{
|
||||
double centerX = ActualWidth / 2;
|
||||
double spacing = 180;
|
||||
double sideScale = 0.8;
|
||||
double centerScale = 1.2;
|
||||
double spacing = 550;
|
||||
double sideScale = 0.93;
|
||||
double centerScale = 1.43;
|
||||
|
||||
for (int i = 0; i < ItemsHost.Items.Count; i++)
|
||||
{
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user