diff --git a/Wpf_AiSportsMicrospace/MainWindow.xaml.cs b/Wpf_AiSportsMicrospace/MainWindow.xaml.cs
index a137ef1..aca9758 100644
--- a/Wpf_AiSportsMicrospace/MainWindow.xaml.cs
+++ b/Wpf_AiSportsMicrospace/MainWindow.xaml.cs
@@ -113,8 +113,6 @@ public partial class MainWindow : Window
//var ip = config.Ip;
}
-
-
var host = this.host.Text.Trim();
if (string.IsNullOrWhiteSpace(host))
{
diff --git a/Wpf_AiSportsMicrospace/Views/Home.xaml.cs b/Wpf_AiSportsMicrospace/Views/Home.xaml.cs
index edf08f7..fb39716 100644
--- a/Wpf_AiSportsMicrospace/Views/Home.xaml.cs
+++ b/Wpf_AiSportsMicrospace/Views/Home.xaml.cs
@@ -14,6 +14,7 @@ using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
+using Views.JumpLong;
using Views.JumpRope;
using Wpf_AiSportsMicrospace;
using Wpf_AiSportsMicrospace.Common;
@@ -150,7 +151,7 @@ namespace Wpf_AiSportsMicrospace
}
else
{
- var newPage = new TrainingRecords();
+ var newPage = new StandingLeap();
mainWin?.SwitchPageWithMaskAnimation(newPage, true);
}
}
diff --git a/Wpf_AiSportsMicrospace/Views/JumpLong/StandingLeap.xaml b/Wpf_AiSportsMicrospace/Views/JumpLong/StandingLeap.xaml
new file mode 100644
index 0000000..0dde678
--- /dev/null
+++ b/Wpf_AiSportsMicrospace/Views/JumpLong/StandingLeap.xaml
@@ -0,0 +1,15 @@
+
+
+
+
+
+
+
+
diff --git a/Wpf_AiSportsMicrospace/Views/JumpLong/StandingLeap.xaml.cs b/Wpf_AiSportsMicrospace/Views/JumpLong/StandingLeap.xaml.cs
new file mode 100644
index 0000000..09fb312
--- /dev/null
+++ b/Wpf_AiSportsMicrospace/Views/JumpLong/StandingLeap.xaml.cs
@@ -0,0 +1,257 @@
+using Dto;
+using Enum;
+using System;
+using System.Collections.Concurrent;
+using System.Collections.Generic;
+using System.IO;
+using System.Text;
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Data;
+using System.Windows.Documents;
+using System.Windows.Input;
+using System.Windows.Media;
+using System.Windows.Media.Animation;
+using System.Windows.Media.Imaging;
+using System.Windows.Navigation;
+using System.Windows.Shapes;
+using Wpf_AiSportsMicrospace;
+using Wpf_AiSportsMicrospace.Common;
+using Wpf_AiSportsMicrospace.Enum;
+using Wpf_AiSportsMicrospace.MyUserControl;
+using Wpf_AiSportsMicrospace.Views;
+using Yztob.AiSports.Inferences.Abstractions;
+using Yztob.AiSports.Inferences.Implement;
+using Yztob.AiSports.Inferences.Things;
+using Yztob.AiSports.Postures.Sports;
+using Yztob.AiSports.Postures.Things;
+using Yztob.AiSports.Sensors.Abstractions;
+using Yztob.AiSports.Sensors.Things;
+
+namespace Views.JumpLong
+{
+ ///
+ /// StandingLeap.xaml 的交互逻辑
+ ///
+ public partial class StandingLeap : UserControl
+ {
+ #region 私有成员
+ private Main _mainWin => Application.Current.MainWindow as Main;
+ private IHumanPredictor _humanPredictor;
+ private IObjectDetector _objectDetector;
+ private HumanGraphicsRenderer _humanGraphicsRenderer;
+ private SportBase _sport;
+ private ApparatusLongJumpDelineation _measureApparatus;
+ private MediaPlayer _mediaPlayer = new MediaPlayer();
+ private readonly SportDetectionQueue _detectQueue;
+ #endregion
+
+ public StandingLeap()
+ {
+ InitializeComponent();
+ Loaded += UserControl_Loaded;
+ Unloaded += UserControl_Unloaded;
+
+ _humanPredictor = HumanPredictorFactory.Create(HumanPredictorType.SingleHigh);
+ _humanGraphicsRenderer = new HumanGraphicsRenderer();
+ _humanGraphicsRenderer.DrawLabel = false;
+ _objectDetector = ObjectDetectorFactory.CreateSportGoodsDetector();
+ _detectQueue = new SportDetectionQueue();
+
+ _sport = SportBase.Create("standing-long-jump");
+ _sport.OnTicked += this.OnSportTick;
+ //if (_sport?.IsCounting == true)
+ _sport.Start();
+
+ _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)
+ {
+ // 播放音乐
+ PlayMusic("raisehand.mp3");
+ }
+
+ private void UserControl_Unloaded(object sender, RoutedEventArgs e)
+ {
+ _sport.Stop();
+ _humanPredictor?.Dispose();
+ _objectDetector?.Dispose();
+ }
+
+ private async void HumanPredicting(VideoFrame frame)
+ {
+ try
+ {
+ var buffer = frame.GetImageBuffer(ImageFormat.Jpeg).ToArray();
+ var humanResult = await Task.Run(() => _humanPredictor.Predicting(buffer, frame.Number));
+ var objects = _sport.Equipment ? await Task.Run>(() => _objectDetector.Detecting(buffer)) : new List();
+ if (_sport.MeasureApparatus != null)
+ objects.AddRange(_sport.MeasureApparatus.Apparatuses);
+
+ //运动需要保证时序
+ var human = humanResult?.Humans?.FirstOrDefault();
+ _detectQueue.Enqueue(frame.Number, human, objects);
+ }
+ catch (Exception ex)
+ {
+ Console.WriteLine("ProcessFrame error: " + ex.Message);
+ }
+ }
+
+ private void PlayMusic(string musicFileName)
+ {
+ // 获取项目根目录
+ string projectRoot = System.IO.Path.Combine(AppContext.BaseDirectory, @"..\..\..");
+ string musicPath = System.IO.Path.Combine(projectRoot, "Resources", "Music", musicFileName);
+ string imgPath = System.IO.Path.Combine(projectRoot, "Resources", "Img", "提示图.png");
+
+ if (!File.Exists(musicPath))
+ {
+ Console.WriteLine($"音乐文件不存在: {musicPath}");
+ return;
+ }
+
+ _mediaPlayer.Open(new Uri(musicPath, UriKind.Absolute));
+
+ ShowCenterTip(imgPath, TimeSpan.FromSeconds(3));
+
+ // 监听播放完成事件
+ _mediaPlayer.MediaEnded += MediaPlayer_MediaEnded;
+ //_mainWin.WebcamClient.StartExtract();
+ _mediaPlayer.Play();
+ }
+ private void ShowCenterTip(string imagePath, TimeSpan duration)
+ {
+ var tipImage = new Image
+ {
+ Source = new BitmapImage(new Uri(imagePath, UriKind.Absolute)),
+ HorizontalAlignment = HorizontalAlignment.Center,
+ VerticalAlignment = VerticalAlignment.Center,
+ Opacity = 0,
+ Margin = new Thickness(0, -100, 0, 0),
+ };
+
+ // 增加图片的大小,调整比例
+ tipImage.Width = 1920 * 0.9; // 宽度为 Canvas 宽度的 90%
+ tipImage.Height = 1080 * 0.6; // 高度为 Canvas 高度的 60%
+
+ // 将图片添加到 Overlay Canvas
+ userBox.Children.Add(tipImage);
+
+ // 渐变出现动画
+ var fadeInAnimation = new DoubleAnimation
+ {
+ From = 0,
+ To = 1,
+ Duration = TimeSpan.FromSeconds(1.5)
+ };
+ tipImage.BeginAnimation(UIElement.OpacityProperty, fadeInAnimation);
+
+ // 定时移除,并且渐变消失
+ Task.Delay(duration).ContinueWith(_ =>
+ {
+ Dispatcher.Invoke(() =>
+ {
+ // 渐变消失动画
+ var fadeOutAnimation = new DoubleAnimation
+ {
+ From = 1,
+ To = 0,
+ Duration = TimeSpan.FromSeconds(1.5)
+ };
+ tipImage.BeginAnimation(UIElement.OpacityProperty, fadeOutAnimation);
+
+ // 完成后移除图片
+ fadeOutAnimation.Completed += (s, e) =>
+ {
+ userBox.Children.Remove(tipImage);
+ };
+ });
+ });
+ }
+ private void MediaPlayer_MediaEnded(object sender, EventArgs e)
+ {
+ // 音乐播放完成后的逻辑
+ Console.WriteLine("音乐播放完成!");
+ }
+
+ private void OnSportTick(int counts, int times)
+ {
+ var ts = TimeSpan.FromSeconds(times);
+ Dispatcher.BeginInvoke(() =>
+ {
+ sportCounts.Content = _sport.GetFormatCounts(); //counts.ToString();
+ sportTimes.Content = _sport.GetFormatTimes();//ts.ToString(@"mm\'ss\""");
+
+ //触发停止
+ if (!_sport.IsCounting)
+ {
+ _sport.Stop();
+ _sport.Start();
+ }
+ });
+ }
+ private bool ReactangleCalibrating(int frameWidth, int frameHeight)
+ {
+ var boxes = new List()
+ {
+ new BoundingBox(){
+ ImageWidth = frameWidth,
+ ImageHeight = frameHeight,
+ Label = new PredictionLabel(){
+ Key = "begin"
+ },
+ MaskLayer = new MaskLayer(){
+ Contours = new List(){
+ new SixLabors.ImageSharp.PointF(1722, 909),
+ new SixLabors.ImageSharp.PointF(1689, 724),
+ }
+ }
+ },
+ new BoundingBox(){
+ ImageWidth = frameWidth,
+ ImageHeight = frameHeight,
+ Label = new PredictionLabel{
+ Key = "end"
+ },
+ MaskLayer = new MaskLayer(){
+ Contours = new List(){
+ new SixLabors.ImageSharp.PointF(1516, 918),
+ new SixLabors.ImageSharp.PointF(1498, 730)
+ }
+ }
+ }
+ };
+
+ boxes[1].Label.Key = "take-off";
+ boxes.Add(new BoundingBox()
+ {
+ ImageWidth = frameWidth,
+ ImageHeight = frameHeight,
+ Label = new PredictionLabel
+ {
+ Key = "end"
+ },
+ MaskLayer = new MaskLayer()
+ {
+ Contours = new List(){
+ new SixLabors.ImageSharp.PointF(191, 918),
+ new SixLabors.ImageSharp.PointF(245, 741)
+ }
+ }
+ });
+
+ return _measureApparatus.Calibrating(boxes);
+ }
+ }
+}
diff --git a/Wpf_AiSportsMicrospace/Views/JumpRope/GroupJumpRope.xaml.cs b/Wpf_AiSportsMicrospace/Views/JumpRope/GroupJumpRope.xaml.cs
index 88cc654..6658b7e 100644
--- a/Wpf_AiSportsMicrospace/Views/JumpRope/GroupJumpRope.xaml.cs
+++ b/Wpf_AiSportsMicrospace/Views/JumpRope/GroupJumpRope.xaml.cs
@@ -40,6 +40,7 @@ using Yztob.AiSports.Inferences.Things;
using Yztob.AiSports.Postures.Sports;
using Yztob.AiSports.Sensors.Abstractions;
using Yztob.AiSports.Sensors.Things;
+using static System.Formats.Asn1.AsnWriter;
namespace Wpf_AiSportsMicrospace.Views
{
@@ -52,7 +53,6 @@ namespace Wpf_AiSportsMicrospace.Views
private MediaPlayer _mediaPlayer = new MediaPlayer();
List RankingItemList = new();
- private readonly SportDetectionQueue _detectQueue;
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
@@ -62,18 +62,19 @@ namespace Wpf_AiSportsMicrospace.Views
private readonly object _updateLock = new object();
private readonly Dictionary _jumpStatus = new Dictionary();
private GroupJumpRopeContext _groupJumpRopeContext;
- public GroupJumpRope()
+
+ public GroupJumpRope()
{
InitializeComponent();
- _detectQueue = new SportDetectionQueue();
Loaded += UserControl_Loaded;
Unloaded += UserControl_Unloaded;
_groupJumpRopeContext = new GroupJumpRopeContext();
-
-
}
+
private async void UserControl_Loaded(object sender, RoutedEventArgs e)
{
+ // 使用局部变量保存事件处理方法,以便解绑
+ Action handler = null;
var app = (App)Application.Current;
using var scope = App.AppHost.Services.CreateScope();
@@ -87,7 +88,6 @@ namespace Wpf_AiSportsMicrospace.Views
(cfg.point5.X, cfg.point5.Y),(cfg.point6.X, cfg.point6.Y)];
_groupJumpRopeContext.CirclePositions = pointlist;
-
DrawCirclesWithText();
// 播放音乐
@@ -251,10 +251,11 @@ namespace Wpf_AiSportsMicrospace.Views
Utils.PlayBackgroundMusic("countdown_3.mp3", false);
//开始人脸识别=============================================
- EventHandler handler = null;
+ Action handler = null;
- handler = async (sender, buffer) =>
+ handler = async (frame) =>
{
+ var buffer = frame.GetImageBuffer(ImageFormat.Jpeg).ToArray();
double[] xValues = [.. _groupJumpRopeContext.CirclePositions.Select(p => p.XNorm)];
var studentList = Utils.FacialRecognition(buffer, xValues);
diff --git a/Wpf_AiSportsMicrospace/Views/JumpRope/MusicJumpRope.xaml.cs b/Wpf_AiSportsMicrospace/Views/JumpRope/MusicJumpRope.xaml.cs
index fa6c266..52bcdfc 100644
--- a/Wpf_AiSportsMicrospace/Views/JumpRope/MusicJumpRope.xaml.cs
+++ b/Wpf_AiSportsMicrospace/Views/JumpRope/MusicJumpRope.xaml.cs
@@ -30,6 +30,7 @@ using Wpf_AiSportsMicrospace.Views;
using WpfAnimatedGif;
using Yztob.AiSports.Inferences.Things;
using Yztob.AiSports.Postures.Sports;
+using Yztob.AiSports.Sensors.Things;
namespace Wpf_AiSportsMicrospace.Views.JumpRope
{
@@ -244,12 +245,12 @@ namespace Wpf_AiSportsMicrospace.Views.JumpRope
Utils.PlayBackgroundMusic("countdown_3.mp3", false);
//开始人脸识别=============================================
- EventHandler handler = null;
+ Action handler = null;
- handler = async (sender, buffer) =>
+ handler = async (frame) =>
{
double[] xValues = [.. _musicJumpRopeContext.CirclePositions.Select(p => p.XNorm)];
-
+ var buffer = frame.GetImageBuffer(ImageFormat.Jpeg).ToArray();
var studentList = Utils.FacialRecognition(buffer, xValues);
if (studentList != null && studentList.Count > 0)
diff --git a/Wpf_AiSportsMicrospace/Views/Main.xaml.cs b/Wpf_AiSportsMicrospace/Views/Main.xaml.cs
index ae587da..a65c90c 100644
--- a/Wpf_AiSportsMicrospace/Views/Main.xaml.cs
+++ b/Wpf_AiSportsMicrospace/Views/Main.xaml.cs
@@ -1,4 +1,5 @@
+using SharpDX.Direct3D9;
using System.Collections.Concurrent;
using System.Drawing;
using System.IO;
@@ -38,7 +39,9 @@ namespace Wpf_AiSportsMicrospace.Views
private readonly object _reconnectLock = new();
public event EventHandler> HumanFrameUpdated;
- public event EventHandler FacialRecognitionEvent;
+
+ public event Action FacialRecognitionEvent;
+
public Main()
{
@@ -106,14 +109,14 @@ namespace Wpf_AiSportsMicrospace.Views
//if (frame.Number % 2 != 0)
// return;
- var buffer = frame.GetImageBuffer(ImageFormat.Jpeg).ToArray();
-
// 触发全局事件
Application.Current.Dispatcher.BeginInvoke(() =>
{
- FacialRecognitionEvent?.Invoke(this, buffer);
+ FacialRecognitionEvent?.Invoke(frame);
});
+
+ var buffer = frame.GetImageBuffer(ImageFormat.Jpeg).ToArray();
var humanResult = _humanPredictor.Predicting(buffer, frame.Number);
var humans = humanResult?.Humans?.ToList();
diff --git a/sdks/Yztob.AiSports.Inferences.dll b/sdks/Yztob.AiSports.Inferences.dll
index 7b24909..f02aa46 100644
Binary files a/sdks/Yztob.AiSports.Inferences.dll and b/sdks/Yztob.AiSports.Inferences.dll differ
diff --git a/sdks/Yztob.AiSports.Postures.dll b/sdks/Yztob.AiSports.Postures.dll
index 3690e60..2d51421 100644
Binary files a/sdks/Yztob.AiSports.Postures.dll and b/sdks/Yztob.AiSports.Postures.dll differ
diff --git a/sdks/Yztob.AiSports.Postures.xml b/sdks/Yztob.AiSports.Postures.xml
index bf3a7a0..82150bd 100644
--- a/sdks/Yztob.AiSports.Postures.xml
+++ b/sdks/Yztob.AiSports.Postures.xml
@@ -6,7 +6,7 @@
- 人体姿态计算检测执行器
+ 人体姿态检测规则执行计算器
@@ -727,6 +727,17 @@
一些扩展方法
+
+
+ 获取当前的人体重力线
+
+ 要计算的人体
+
+ 返回重力线的上、下起点
+ [0]-为线的上端点
+ [1]-为线的下端点
+
+
以坐标值转换成
@@ -734,6 +745,13 @@
要转换的关键点
+
+
+ 获取当前关键点集中的中心点位置
+
+ 关键点集
+
+
人体姿态计算检测执行器
@@ -1326,6 +1344,70 @@
+
+
+ 姿态分析相关工具函数集
+
+
+
+
+ 获取身体关键点键-名映射字典
+
+
+
+
+
+ 获取相机视角名称
+
+ 当前视角
+
+
+
+
+ 计算平面三个点的角度
+
+ 角度点
+ 上角点
+ 下角点
+
+ -1至少一个有一个坐标点是空的;否则返回计算角度;
+
+
+
+
+ 计算三个人体关键点角度
+
+ 角度人体关键点
+ 上角人体关键点
+ 下角人体关键点
+
+ 计算后的角度
+
+
+
+
+ 断言是否有匹配
+
+ 实际测量的角度
+ 期望的角度
+ 允许的偏差
+
+
+
+
+ 计算两个边界框的交并比
+
+ 框1
+ 框2
+
+
+
+
+ 计算多个关键点之间的中心点位置
+
+ 关键点集
+
+
运动计数器计数变化委托
@@ -1752,6 +1834,9 @@
获取或设置垫子长度,单位cm
+
+
+
@@ -2335,63 +2420,6 @@
当前势
趋势是否变化
-
-
- 姿态分析相关工具函数集
-
-
-
-
- 获取身体关键点键-名映射字典
-
-
-
-
-
- 获取相机视角名称
-
- 当前视角
-
-
-
-
- 计算平面三个点的角度
-
- 角度点
- 上角点
- 下角点
-
- -1至少一个有一个坐标点是空的;否则返回计算角度;
-
-
-
-
- 计算三个人体关键点角度
-
- 角度人体关键点
- 上角人体关键点
- 下角人体关键点
-
- 计算后的角度
-
-
-
-
- 断言是否有匹配
-
- 实际测量的角度
- 期望的角度
- 允许的偏差
-
-
-
-
- 计算两个边界框的交并比
-
- 框1
- 框2
-
-
表示受权机器CPU部件特性图
diff --git a/sdks/Yztob.AiSports.Sensors.WinForm.dll b/sdks/Yztob.AiSports.Sensors.WinForm.dll
index 5ee5135..0dd949a 100644
Binary files a/sdks/Yztob.AiSports.Sensors.WinForm.dll and b/sdks/Yztob.AiSports.Sensors.WinForm.dll differ
diff --git a/sdks/Yztob.AiSports.Sensors.WinForm.xml b/sdks/Yztob.AiSports.Sensors.WinForm.xml
index 8990204..5cf4c22 100644
--- a/sdks/Yztob.AiSports.Sensors.WinForm.xml
+++ b/sdks/Yztob.AiSports.Sensors.WinForm.xml
@@ -111,6 +111,12 @@
+
+
+ 直接向预览器推送帧,进行预览回放
+
+ 帧图像
+
启动抽帧/预览
diff --git a/sdks/Yztob.AiSports.Sensors.dll b/sdks/Yztob.AiSports.Sensors.dll
index 350b6b0..4d74540 100644
Binary files a/sdks/Yztob.AiSports.Sensors.dll and b/sdks/Yztob.AiSports.Sensors.dll differ
diff --git a/sdks/Yztob.AiSports.Sensors.xml b/sdks/Yztob.AiSports.Sensors.xml
index 0d0d629..801d5d5 100644
--- a/sdks/Yztob.AiSports.Sensors.xml
+++ b/sdks/Yztob.AiSports.Sensors.xml
@@ -218,12 +218,13 @@
视频流解码器
-
+
初始化解码器
设备访问url
硬件设备类型
+ 视频连接超时是间
@@ -307,9 +308,21 @@
获取FFmpeg原始帧
+
+
+ 获取或设置帧相对流开始的展示时间戳,单位:秒
+
+ 通俗的理解便是,此帧在视频的第几秒出现的
+
+
+
+ 获取或设置上一帧距离这帧维持的时长
+
+ 即用当帧的减去上一帧的
+
- 获取或设置时间戳,从UTC:2000-01-01开始的耗秒数
+ 获取或设置帧提取时的系统时间戳,从UTC:2000-01-01开始的耗秒数
@@ -365,6 +378,18 @@
获取FFmpeg原始帧
+
+
+ 获取或设置帧相对流开始的展示时间戳,单位:秒
+
+ 通俗的理解便是,此帧在视频的第几秒出现的
+
+
+
+ 获取或设置上一帧距离这帧维持的时长
+
+ 即用当帧的减去上一帧的
+
获取或设置时间戳,从UTC:2000-01-01开始的耗秒数