This commit is contained in:
ltx 2025-12-30 13:45:14 +08:00
commit f3fd48a651
3 changed files with 80 additions and 658 deletions

View File

@ -1,4 +1,5 @@
using HandyControl.Controls;
using SportExpand;
using System;
using System.Collections.Generic;
using System.Linq;
@ -24,18 +25,13 @@ namespace Wpf_AiSportsMicrospace.Common
{
IPointTracker _leftTracker;
IPointTracker _rightTracker;
IPointTracker _leftElbow;
IPointTracker _rightElbow;
private int _leftWaveCount = 0;
private int _rightWaveCount = 0;
private const int WaveThreshold = 4;
WebcamClient _webcamClient;
private DateTime _lastActionTime = DateTime.MinValue;
private Point? _lastLeftWrist = null;
private Point? _lastRightWrist = null;
private DateTime? _raiseStartTime = null;
private DateTime? _wristStartTime = null;
private double _leftWristDeltaX = 0;
private double _rightWristDeltaX = 0;
private bool _firstHandTriggered = false;
@ -43,13 +39,9 @@ namespace Wpf_AiSportsMicrospace.Common
{
_leftTracker = PostureCalculate.CreatePointTracker("left_wrist", 0);
_rightTracker = PostureCalculate.CreatePointTracker("right_wrist", 0);
_leftElbow = PostureCalculate.CreatePointTracker("left_elbow", 0);
_rightElbow = PostureCalculate.CreatePointTracker("right_elbow", 0);
//_leftTracker.Amplitude = 0.03f;
//_rightTracker.Amplitude = 0.03f;
//_leftElbow.Amplitude = 0.03f;
//_rightElbow.Amplitude = 0.03f;
_leftTracker.Amplitude = 0.3f;
_rightTracker.Amplitude = 0.3f;
}
public WebcamClient CreateRTSP()
@ -65,6 +57,7 @@ namespace Wpf_AiSportsMicrospace.Common
return _webcamClient;
}
public int VerifyWavingAction(List<Human> humans)
{
if (humans == null || humans.Count == 0)
@ -83,47 +76,43 @@ namespace Wpf_AiSportsMicrospace.Common
if (!(xNorm >= 0.44 && xNorm <= 0.57 && yNorm >= 0.81))
continue;
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");
// --- 右手举手优先 ---
int rightHandAction = DetectRightHandRaise(human);
if (rightHandAction != (int)WavingAction.None)
return rightHandAction;
// --- 左手挥手 ---
if (leftWrist != null && leftElbow != null)
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)
{
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;
_leftWaveCount = 0; // 只重置左手
return (int)WavingAction.LeftWave;
}
// --- 右手挥手 ---
//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;
//}
if (_rightWaveCount >= WaveThreshold)
{
_rightWaveCount = 0; // 只重置右手
return (int)WavingAction.RightWave;
}
}
return (int)WavingAction.None;
}
/// <summary>
/// 检测右手举手状态
/// </summary>
@ -131,16 +120,11 @@ namespace Wpf_AiSportsMicrospace.Common
/// <returns>返回 WavingAction:FirstHand / Raising / RaiseHand / None</returns>
public int DetectRightHandRaise(Human human)
{
var rightWrist = human.Keypoints.FirstOrDefault(k => k.Name == "right_wrist");
var rightElbow = human.Keypoints.FirstOrDefault(k => k.Name == "right_elbow");
var calculator = PostureCalculate.CreateCalculator();
var rule = RaiseHand.RaiseRightHand();
var pass = calculator.Calculating(human, rule);
if (rightWrist == null || rightElbow == null)
return (int)WavingAction.None;
double verticalRise = rightElbow.Y - rightWrist.Y; // 手腕在肘上方 → 正值
const double raiseThreshold = 60; // 举手阈值,可根据实际调整
if (verticalRise >= raiseThreshold)
if (pass)
{
// 初始化计时
if (_raiseStartTime == null) _raiseStartTime = DateTime.Now;
@ -177,44 +161,5 @@ namespace Wpf_AiSportsMicrospace.Common
return (int)WavingAction.None;
}
}
/// <summary>
/// 统一的水平挥手检测
/// </summary>
private int DetectHorizontalWave(Point wrist, Point elbow, ref Point? lastWrist, ref double deltaX, bool isLeft)
{
if (lastWrist != null)
{
double dx = wrist.X - lastWrist.Value.X;
double dy = Math.Abs(wrist.Y - lastWrist.Value.Y);
// 累计水平位移
deltaX += dx;
if (Math.Abs(deltaX) > 40 && dy < 60 && Math.Abs(wrist.Y - elbow.Y) < 60)
{
deltaX = 0; // 重置
if (CheckCooldown())
{
return isLeft ? (int)WavingAction.LeftWave : (int)WavingAction.RightWave;
}
}
}
lastWrist = wrist;
return (int)WavingAction.None;
}
/// <summary>
/// 冷却防抖(避免重复触发)
/// </summary>
private bool CheckCooldown(int cooldownMs = 1000)
{
if ((DateTime.Now - _lastActionTime).TotalMilliseconds < cooldownMs)
return false;
_lastActionTime = DateTime.Now;
return true;
}
}
}

View File

@ -0,0 +1,45 @@
using System;
using System.Collections.Generic;
using System.Text;
using Yztob.AiSports.Postures.Things;
namespace SportExpand
{
/// <summary>
/// 举右手
/// </summary>
public class RaiseHand
{
/// <summary>
/// 举右手
/// </summary>
/// <returns></returns>
public static CalcBaseRule RaiseRightHand()
{
return new CalcPositionRule()
{
Name = "右手是否举过头顶",
PositionKey = "right_wrist",
ReferenceKey = "right_eye",
Position = "top",
Relaxed = true
};
}
/// <summary>
/// 举左手
/// </summary>
/// <returns></returns>
public static CalcBaseRule RaiseLeftHand()
{
return new CalcPositionRule
{
Name = "左手是否举过头顶",
PositionKey = "left_wrist",
ReferenceKey = "left_eye",
Position = "top",
Relaxed = true
};
}
}
}

View File

@ -1,568 +0,0 @@
//using AiSportsMicrospaceDB.DBContext;
//using Dto;
//using Enum;
//using Microsoft.Extensions.DependencyInjection;
//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
//{
// /// <summary>
// /// StandingLeap.xaml 的交互逻辑
// /// </summary>
// 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;
// private int _sportType = 0; // 0:未开始 1:运动中 2:运动结束展示排行榜
// #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;
// _mainWin.HumanFrameUpdated += OnHumanFrameUpdated;
// }
// private async void UserControl_Loaded(object sender, RoutedEventArgs e)
// {
// // 播放音乐
// PlayMusic("raisehand.mp3");
// }
// //举手检测
// private void OnHumanFrameUpdated(object sender, List<Human> humans)
// {
// try
// {
// if (humans == null || humans.Count == 0) return;
// //举手检测 未开始 :退出& 开始 运动中:不检测 2.结束:继续测试 & 退出
// if (_sportType != 1)
// {
// //左手返回
// int leftWaving = DetectLeftHandRaise(humans);
// if (leftWaving == 5)
// {
// _mainWin.HumanFrameUpdated -= OnHumanFrameUpdated;
// _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();
// _humanPredictor?.Dispose();
// _objectDetector?.Dispose();
// }
// private async void HumanPredicting(VideoFrame frame)
// {
// try
// {
// var buffer = frame.GetImageBuffer(ImageFormat.Jpeg).ToArray();
// var humanResult = await Task.Run<HumanInferenceResult>(() => _humanPredictor.Predicting(buffer, frame.Number));
// var objects = _sport.Equipment ? await Task.Run<List<BoundingBox>>(() => _objectDetector.Detecting(buffer)) : new List<BoundingBox>();
// 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);
// if (!File.Exists(musicPath))
// {
// Console.WriteLine($"音乐文件不存在: {musicPath}");
// return;
// }
// _mediaPlayer.Open(new Uri(musicPath, UriKind.Absolute));
// ShowCenterTip(TimeSpan.FromSeconds(3));
// // 监听播放完成事件
// _mediaPlayer.MediaEnded += MediaPlayer_MediaEnded;
// //_mainWin.WebcamClient.StartExtract();
// _mediaPlayer.Play();
// }
// private void ShowCenterTip(TimeSpan duration)
// {
// // 渐变出现动画
// var fadeInAnimation = new DoubleAnimation
// {
// From = 0,
// To = 1,
// Duration = TimeSpan.FromSeconds(1.5)
// };
// TipBox.BeginAnimation(UIElement.OpacityProperty, fadeInAnimation);
// // 定时移除,并且渐变消失
// Task.Delay(duration).ContinueWith(_ =>
// {
// Dispatcher.Invoke(() =>
// {
// // 渐变消失动画
// var fadeOutAnimation = new DoubleAnimation
// {
// From = 1,
// To = 0,
// Duration = TimeSpan.FromSeconds(1.5)
// };
// TipBox.BeginAnimation(UIElement.OpacityProperty, fadeOutAnimation);
// });
// });
// }
// 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\""");
// var counts = _sport.GetFormatCounts();
// if (double.Parse(counts[..^1]) > 0 && _sportType == 1) //正向跳远时候&&处于测试阶段时候触发更新
// {
// _sport.Stop();//关闭运动
// count.Text = counts;
// time.Text = _sport.GetFormatTimes();
// ShowRope();
// if (!_sport.IsCounting)
// {
// _sport.Stop();
// _sport.Start();
// }
// }
// });
// }
// //展示成绩
// private async void ShowRope()
// {
// // 渐变出现动画
// var fadeInAnimation = new DoubleAnimation
// {
// From = 0,
// 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 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<AppDbContext>();
// var points = db.JumpLongPoints.FirstOrDefault();
// var boxes = new List<BoundingBox>()
// {
// new BoundingBox(){
// ImageWidth = frameWidth,
// ImageHeight = frameHeight,
// Label = new PredictionLabel(){
// Key = "begin"
// },
// MaskLayer = new MaskLayer(){
// Contours = [
// new ((float)points.BottomPoint3.X, (float)points.BottomPoint3.Y),
// new ((float)points.TopPoint3.X, (float)points.TopPoint3.Y),
// ]
// }
// },
// new BoundingBox(){
// ImageWidth = frameWidth,
// ImageHeight = frameHeight,
// Label = new PredictionLabel{
// Key = "end"
// },
// MaskLayer = new MaskLayer(){
// Contours = [
// new((float)points.BottomPoint2.X, (float)points.BottomPoint2.Y),
// new((float)points.TopPoint2.X, (float)points.TopPoint2.Y),
// ]
// }
// }
// };
// boxes[1].Label.Key = "take-off";
// boxes.Add(new BoundingBox()
// {
// ImageWidth = frameWidth,
// ImageHeight = frameHeight,
// Label = new PredictionLabel
// {
// Key = "end"
// },
// MaskLayer = new MaskLayer()
// {
// Contours = [
// new ((float)points.BottomPoint1.X, (float)points.BottomPoint1.Y),
// new ((float)points.TopPoint1.X, (float)points.TopPoint1.Y),
// ]
// }
// });
// return _measureApparatus.Calibrating(boxes);
// }
// public int DetectLeftHandRaise(List<Human> 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;
// // 仅检测中心区域内的人
// if (!(xNorm >= 0.44 && xNorm <= 0.57 && yNorm >= 0.81))
// 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<Human> 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<AppDbContext>();
// 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;
// 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();
// _measureApparatus = new ApparatusLongJumpDelineation();
// _measureApparatus.MeasureLength = 300;
// var _isCalibrated = this.ReactangleCalibrating(1920, 1080);
// _sport.MeasureApparatus = _measureApparatus;
// _detectQueue.Sport = _sport;
// _detectQueue.Start();
// // 开始跳远抽帧线程
// _mainWin.FacialRecognitionEvent += 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;
// });
// }
// }
//}