diff --git a/Wpf_AiSportsMicrospace/App.xaml b/Wpf_AiSportsMicrospace/App.xaml
index 89298c0..a5b322f 100644
--- a/Wpf_AiSportsMicrospace/App.xaml
+++ b/Wpf_AiSportsMicrospace/App.xaml
@@ -29,11 +29,6 @@
-
-
-
diff --git a/Wpf_AiSportsMicrospace/Resources/Img/test_img/jump_long/jumping.gif b/Wpf_AiSportsMicrospace/Resources/Img/test_img/jump_long/jumping.gif
new file mode 100644
index 0000000..4ff993a
Binary files /dev/null and b/Wpf_AiSportsMicrospace/Resources/Img/test_img/jump_long/jumping.gif differ
diff --git a/Wpf_AiSportsMicrospace/Resources/Img/test_img/jump_long/right_hand.gif b/Wpf_AiSportsMicrospace/Resources/Img/test_img/jump_long/right_hand.gif
new file mode 100644
index 0000000..fcd87f3
Binary files /dev/null and b/Wpf_AiSportsMicrospace/Resources/Img/test_img/jump_long/right_hand.gif differ
diff --git a/Wpf_AiSportsMicrospace/Resources/Img/test_img/jump_long/rotate_img.png b/Wpf_AiSportsMicrospace/Resources/Img/test_img/jump_long/rotate_img.png
new file mode 100644
index 0000000..2b1b145
Binary files /dev/null and b/Wpf_AiSportsMicrospace/Resources/Img/test_img/jump_long/rotate_img.png differ
diff --git a/Wpf_AiSportsMicrospace/Views/JumpLong/StandingLeap.xaml b/Wpf_AiSportsMicrospace/Views/JumpLong/StandingLeap.xaml
index 8a098bf..edaf4f7 100644
--- a/Wpf_AiSportsMicrospace/Views/JumpLong/StandingLeap.xaml
+++ b/Wpf_AiSportsMicrospace/Views/JumpLong/StandingLeap.xaml
@@ -12,18 +12,25 @@
-
-
-
-
-
+
+
+
+
+
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
+
+
@@ -67,17 +93,21 @@
-
+
-
-
+
+
+
+
+
+
diff --git a/Wpf_AiSportsMicrospace/Views/JumpLong/StandingLeap.xaml.cs b/Wpf_AiSportsMicrospace/Views/JumpLong/StandingLeap.xaml.cs
index ef5a214..8eeabb1 100644
--- a/Wpf_AiSportsMicrospace/Views/JumpLong/StandingLeap.xaml.cs
+++ b/Wpf_AiSportsMicrospace/Views/JumpLong/StandingLeap.xaml.cs
@@ -46,6 +46,9 @@ namespace Views.JumpLong
private ApparatusLongJumpDelineation _measureApparatus;
private MediaPlayer _mediaPlayer = new MediaPlayer();
private readonly SportDetectionQueue _detectQueue;
+
+ private int _sportType = 0; // 0:未开始 1:运动中 2:运动结束展示排行榜
+
#endregion
public StandingLeap()
@@ -53,7 +56,6 @@ namespace Views.JumpLong
InitializeComponent();
Loaded += UserControl_Loaded;
Unloaded += UserControl_Unloaded;
-
_humanPredictor = HumanPredictorFactory.Create(HumanPredictorType.SingleHigh);
_humanGraphicsRenderer = new HumanGraphicsRenderer();
_humanGraphicsRenderer.DrawLabel = false;
@@ -62,19 +64,13 @@ namespace Views.JumpLong
_sport = SportBase.Create("standing-long-jump");
_sport.OnTicked += this.OnSportTick;
- //if (_sport?.IsCounting == true)
- _sport.Start();
-
+ _mainWin.HumanFrameUpdated += this.OnFrameUpdated;
_measureApparatus = new ApparatusLongJumpDelineation();
_measureApparatus.MeasureLength = 300;
var _isCalibrated = this.ReactangleCalibrating(1920, 1080);
_sport.MeasureApparatus = _measureApparatus;
-
_detectQueue.Sport = _sport;
- _detectQueue.Start();
- // 开始抽帧线程
- _mainWin.FacialRecognitionEvent += HumanPredicting;
}
private async void UserControl_Loaded(object sender, RoutedEventArgs e)
@@ -83,6 +79,47 @@ namespace Views.JumpLong
PlayMusic("raisehand.mp3");
}
+
+ //举手检测
+ private void OnFrameUpdated(object sender, List humans)
+ {
+ try
+ {
+ if (humans == null || humans.Count == 0) return;
+
+ //举手检测 未开始 :退出& 开始 运动中:不检测 2.结束:继续测试 & 退出
+ if (_sportType != 1)
+ {
+ //左手返回
+ int leftWaving = DetectLeftHandRaise(humans);
+ if (leftWaving == 5)
+ {
+ _mainWin.HumanFrameUpdated -= this.OnFrameUpdated;
+ _mainWin.WebcamClient.StopExtract();
+
+ // 举左手逻辑,例如结束动画或退出
+ var newPage = new Home();
+ _mainWin?.SwitchPageWithMaskAnimation(newPage, true);
+ return; // 提前退出
+ }
+
+ //右手判断
+ int rightWaving = DetectRightHandRaise(humans);
+ if (rightWaving >= 3 && _sportType == 0) //举手完成 开始跳远检测
+ {
+ Utils.PlayBackgroundMusic("countdown_3.mp3", false);
+ _sportType = 1;
+ StartSport();
+ }
+ }
+ }
+ catch (Exception ex)
+ {
+ Console.WriteLine("OnFrameExtracted error: " + ex.Message);
+ }
+ }
+
+
private void UserControl_Unloaded(object sender, RoutedEventArgs e)
{
_sport.Stop();
@@ -110,6 +147,7 @@ namespace Views.JumpLong
}
}
+ //播放音乐
private void PlayMusic(string musicFileName)
{
// 获取项目根目录
@@ -158,6 +196,7 @@ namespace Views.JumpLong
});
});
+
}
private void MediaPlayer_MediaEnded(object sender, EventArgs e)
{
@@ -168,24 +207,29 @@ namespace Views.JumpLong
private void OnSportTick(int counts, int times)
{
var ts = TimeSpan.FromSeconds(times);
- Dispatcher.BeginInvoke(() =>
+ _ = Dispatcher.BeginInvoke(() =>
{
//sportCounts.Content = _sport.GetFormatCounts(); //counts.ToString();
//sportTimes.Content = _sport.GetFormatTimes();//ts.ToString(@"mm\'ss\""");
- count.Text = _sport.GetFormatCounts();
- time.Text = _sport.GetFormatTimes();
- ShowRope();
- //触发停止
- if (!_sport.IsCounting)
+ var counts = _sport.GetFormatCounts();
+ if (double.Parse(counts[..^1]) > 0 && _sportType == 1) //正向跳远时候&&处于测试阶段时候触发更新
{
- _sport.Stop();
- _sport.Start();
+ _sport.Stop();//关闭运动
+ _mainWin.FacialRecognitionEvent -= this.HumanPredicting;
+ count.Text = counts;
+ time.Text = _sport.GetFormatTimes();
+ ShowRope();
+ if (!_sport.IsCounting)
+ {
+ _sport.Stop();
+ _sport.Start();
+ }
}
});
}
//展示成绩
- private void ShowRope()
+ private async void ShowRope()
{
// 渐变出现动画
var fadeInAnimation = new DoubleAnimation
@@ -194,18 +238,56 @@ namespace Views.JumpLong
To = 1,
Duration = TimeSpan.FromSeconds(1.5)
};
+ var fadeInAnimation1 = new DoubleAnimation
+ {
+ From = 1,
+ To = 0,
+ Duration = TimeSpan.FromSeconds(1.5)
+ };
+ //videoImage.Opacity = 0;
+ //centerImg1.Opacity = 1;
+ //centerImg2.Opacity = 1;
+ //centerImg3.Opacity = 1;
+ _sportType = 2;
RankingGrid.BeginAnimation(UIElement.OpacityProperty, fadeInAnimation);
-
-
+ //_mainWin.WebcamClient.OnExtractFrame -= this.OnFrameExtracted;
+ for (int i = 12; i >= 0; i--)
+ {
+ countDown.Text = i.ToString() + "s";
+ if (i == 1)
+ {
+ RankingGrid.BeginAnimation(UIElement.OpacityProperty, fadeInAnimation1);
+ _sportType = 0;
+ PlayMusic("raisehand.mp3");
+ }
+ await Task.Delay(1000);
+ }
+ // 关闭跳远抽帧线程
}
- private bool ReactangleCalibrating(int frameWidth, int frameHeight)
+
+ //关闭成绩展示 显示跳远页面
+ private void closRope()
+ {
+ //关闭动画
+ //var fadeInAnimation = new DoubleAnimation
+ //{
+ // From = 1,
+ // To = 0,
+ // Duration = TimeSpan.FromSeconds(1)
+ //};
+ // RankingGrid.BeginAnimation(UIElement.OpacityProperty, fadeInAnimation);
+ RankingGrid.Opacity = 0;
+ _sportType = 0;
+ PlayMusic("raisehand.mp3");
+ }
+ private bool ReactangleCalibrating(int frameWidth, int frameHeight)
{
var mainWin = Application.Current.MainWindow as Main;
var app = (App)Application.Current;
using var scope = App.AppHost.Services.CreateScope();
var db = scope.ServiceProvider.GetRequiredService();
- var points = db.JumpLongPoints.FirstOrDefault();
+ var points = db.JumpLongPoints.FirstOrDefault();
var boxes = new List()
{
new BoundingBox(){
@@ -256,5 +338,245 @@ namespace Views.JumpLong
return _measureApparatus.Calibrating(boxes);
}
+
+
+ public int DetectLeftHandRaise(List humans)
+ {
+ if (humans == null || humans.Count == 0)
+ return (int)WavingAction.None;
+
+
+ foreach (var human in humans)
+ {
+ if (human?.Keypoints == null)
+ continue;
+
+
+ // --- 筛选右脚踝坐标 ---
+ var rightAnkle = human.Keypoints.FirstOrDefault(k => k.Name == "right_ankle");
+ if (rightAnkle == null)
+ continue;
+
+ //double xNorm = rightAnkle.X / 1920;
+ //double yNorm = rightAnkle.Y / 1080;
+
+
+ var app = (App)Application.Current;
+ using var scope = App.AppHost.Services.CreateScope();
+ var db = scope.ServiceProvider.GetRequiredService();
+ var points = db.JumpLongPoints.FirstOrDefault();
+
+ // 检测起跳区域的人
+
+ var isinx = (rightAnkle.X < points.TopPoint3.X && rightAnkle.X > points.TopPoint2.X) || (rightAnkle.X < points.BottomPoint3.X && rightAnkle.X > points.BottomPoint2.X);
+ if (points.TopPoint3.X < points.TopPoint2.X)
+ {
+ isinx = (rightAnkle.X > points.TopPoint3.X && rightAnkle.X < points.TopPoint2.X) || (rightAnkle.X > points.BottomPoint3.X && rightAnkle.X < points.BottomPoint2.X);
+ }
+ var isiny = (rightAnkle.Y > points.TopPoint3.Y && rightAnkle.X < points.BottomPoint3.Y) || (rightAnkle.Y < points.TopPoint2.Y && rightAnkle.X > points.BottomPoint2.Y);
+ if (!isinx || !isiny) continue;
+
+ // 获取左手关键点
+ var leftWrist = human.Keypoints.FirstOrDefault(k => k.Name == "left_wrist");
+ var leftElbow = human.Keypoints.FirstOrDefault(k => k.Name == "left_elbow");
+
+ if (leftWrist == null || leftElbow == null)
+ continue;
+
+ const double raiseThreshold = 60; // 举手阈值
+
+ // 判断左手是否举起
+ double verticalRise = leftElbow.Y - leftWrist.Y;
+ if (verticalRise >= raiseThreshold)
+ {
+ return (int)WavingAction.RaiseHand; // 一旦检测到左手举起,立即返回
+ }
+ }
+
+ return (int)WavingAction.None; // 没有检测到举手
+ }
+
+ private DateTime? _raiseStartTime;
+ private bool _firstHandTriggered;
+ private int _lastCountdownSecond = 3;
+ private DateTime? _countdownStartTime;
+ public int DetectRightHandRaise(List humans)
+ {
+ if (humans == null || humans.Count == 0)
+ return (int)WavingAction.None;
+
+ foreach (var human in humans)
+ {
+ if (human?.Keypoints == null)
+ continue;
+
+ // --- 筛选右脚踝坐标 ---
+ var rightAnkle = human.Keypoints.FirstOrDefault(k => k.Name == "right_ankle");
+ if (rightAnkle == null)
+ continue;
+
+ //double xNorm = rightAnkle.X / 1920;
+ //double yNorm = rightAnkle.Y / 1080;
+
+
+ var app = (App)Application.Current;
+ using var scope = App.AppHost.Services.CreateScope();
+ var db = scope.ServiceProvider.GetRequiredService();
+ var points = db.JumpLongPoints.FirstOrDefault();
+
+ // 检测起跳区域的人
+
+ var isinx = (rightAnkle.X < points.TopPoint3.X && rightAnkle.X > points.TopPoint2.X) || (rightAnkle.X < points.BottomPoint3.X && rightAnkle.X > points.BottomPoint2.X);
+ if (points.TopPoint3.X < points.TopPoint2.X)
+ {
+ isinx = (rightAnkle.X > points.TopPoint3.X && rightAnkle.X < points.TopPoint2.X) || (rightAnkle.X > points.BottomPoint3.X && rightAnkle.X < points.BottomPoint2.X);
+ }
+ var isiny = (rightAnkle.Y > points.TopPoint3.Y && rightAnkle.X < points.BottomPoint3.Y) || (rightAnkle.Y < points.TopPoint2.Y && rightAnkle.X > points.BottomPoint2.Y);
+ if (!isinx || !isiny) continue;
+
+
+ var rightWrist = human.Keypoints.FirstOrDefault(k => k.Name == "right_wrist");
+ var rightElbow = human.Keypoints.FirstOrDefault(k => k.Name == "right_elbow");
+
+ const double raiseThreshold = 60; // 举手阈值
+ const double holdDuration = 1; // 持续时间(秒)
+ const int countdownSeconds = 3; // 倒计时长度
+
+ bool handRaised = false;
+
+ if (rightWrist != null && rightElbow != null)
+ {
+ double verticalRise = rightElbow.Y - rightWrist.Y;
+ handRaised = verticalRise >= raiseThreshold;
+ }
+
+ // ---------- 第一步:持续举手触发倒计时 ----------
+ if (!_firstHandTriggered)
+ {
+
+ if (handRaised)
+ {
+ _raiseStartTime ??= DateTime.Now;
+ var holdElapsed = (DateTime.Now - _raiseStartTime.Value).TotalSeconds;
+ if (holdElapsed >= holdDuration)
+ {
+ // 举手达到指定时间,启动倒计时
+ _firstHandTriggered = true;
+ ResetRaiseState();
+ return (int)WavingAction.FirstHand;
+ }
+ }
+ else
+ {
+ // 手放下,重置举手开始时间
+ _raiseStartTime = DateTime.Now;
+ }
+
+ continue; // 本人未触发,检测下一个人
+ }
+
+ // ---------- 第二步:倒计时逻辑 ----------
+ var countdownElapsed = (DateTime.Now - _countdownStartTime.Value).TotalSeconds;
+ int currentSecond = countdownSeconds - (int)Math.Floor(countdownElapsed);
+
+ if (currentSecond > 0 && currentSecond != _lastCountdownSecond)
+ {
+ _lastCountdownSecond = currentSecond;
+ Console.WriteLine($"倒计时:{currentSecond}");
+ return (int)WavingAction.Raising; // 倒计时中
+ }
+
+ if (countdownElapsed >= countdownSeconds)
+ {
+ ResetRaiseState();
+ return (int)WavingAction.RaiseHand; // 举手完成
+ }
+
+ return (int)WavingAction.Raising;
+ }
+
+ // 没有任何中心区域内的人举手
+ return (int)WavingAction.None;
+ }
+
+ private void ResetRaiseState()
+ {
+ _raiseStartTime = null;
+ _countdownStartTime = null;
+ _firstHandTriggered = false;
+ _lastCountdownSecond = 3;
+ }
+
+ private void StartSport()
+ {
+
+
+ //if (_sport?.IsCounting == true)
+ //_mainWin.WebcamClient.OnExtractFrame += this.OnFrameExtracted;
+ _sport.Start();
+ _detectQueue.Start();
+
+ // 开始跳远抽帧线程
+ _mainWin.FacialRecognitionEvent += this.HumanPredicting;
+ }
+
+ private WriteableBitmap _videoBitmap;
+ private int _lastFrameNumber = -1;
+ private async void OnFrameExtracted(VideoFrame frame)
+ {
+ if (frame == null) return;
+
+ // 跳帧:每秒只渲染 10~15 帧,降低 CPU 占用
+
+ if (frame.Number % 2 != 0) return;
+
+ if (_lastFrameNumber != -1 && frame.Number - _lastFrameNumber < 2) return;
+ _lastFrameNumber = (int)frame.Number;
+
+ //获得帧二进制流,保存图像、人体识别竺
+ var buffer = frame.GetImageBuffer(Yztob.AiSports.Sensors.Things.ImageFormat.Jpeg).ToArray();
+ //await File.WriteAllBytesAsync($"./temps/{frame.Number}.jpg", buffer);
+
+ // === 显示到 WPF ===
+ BitmapImage bitmap = null;
+ await Task.Run(() =>
+ {
+ using var ms = new MemoryStream(buffer);
+ bitmap = new BitmapImage();
+ bitmap.BeginInit();
+ bitmap.CacheOption = BitmapCacheOption.OnLoad;
+ bitmap.StreamSource = ms;
+ bitmap.EndInit();
+ bitmap.Freeze(); // 跨线程安全
+ });
+
+ // UI 线程显示
+ Application.Current.Dispatcher.Invoke(() =>
+ {
+ if (videoImage == null) return;
+
+ if (_videoBitmap == null ||
+ _videoBitmap.PixelWidth != bitmap.PixelWidth ||
+ _videoBitmap.PixelHeight != bitmap.PixelHeight)
+ {
+ _videoBitmap = new WriteableBitmap(bitmap.PixelWidth, bitmap.PixelHeight,
+ 96, 96, PixelFormats.Bgra32, null);
+ videoImage.Source = _videoBitmap;
+ }
+
+ _videoBitmap.Lock();
+ bitmap.CopyPixels(new Int32Rect(0, 0, bitmap.PixelWidth, bitmap.PixelHeight),
+ _videoBitmap.BackBuffer,
+ _videoBitmap.BackBufferStride * bitmap.PixelHeight,
+ _videoBitmap.BackBufferStride);
+ _videoBitmap.AddDirtyRect(new Int32Rect(0, 0, bitmap.PixelWidth, bitmap.PixelHeight));
+ _videoBitmap.Unlock();
+
+ videoImage.Opacity = 1;
+ centerImg1.Opacity = 0;
+ centerImg2.Opacity = 0;
+ centerImg3.Opacity = 0;
+ });
+ }
}
}
diff --git a/Wpf_AiSportsMicrospace/Wpf_AiSportsMicrospace.csproj b/Wpf_AiSportsMicrospace/Wpf_AiSportsMicrospace.csproj
index 99c0124..62dd210 100644
--- a/Wpf_AiSportsMicrospace/Wpf_AiSportsMicrospace.csproj
+++ b/Wpf_AiSportsMicrospace/Wpf_AiSportsMicrospace.csproj
@@ -68,8 +68,11 @@
+
+
+
@@ -119,12 +122,21 @@
Always
+
+ Always
+
Always
Always
+
+ Always
+
+
+ Always
+
Always