点位
This commit is contained in:
parent
b3feab76d6
commit
0cdfe7c23d
@ -4,6 +4,7 @@
|
|||||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||||
xmlns:local="clr-namespace:Wpf_AiSportsMicrospace"
|
xmlns:local="clr-namespace:Wpf_AiSportsMicrospace"
|
||||||
|
Loaded="Window_Loaded" Closing="Window_Closing"
|
||||||
mc:Ignorable="d" Title="AI运动微空间" Height="700" Width="1200">
|
mc:Ignorable="d" Title="AI运动微空间" Height="700" Width="1200">
|
||||||
<Grid>
|
<Grid>
|
||||||
<Grid.ColumnDefinitions>
|
<Grid.ColumnDefinitions>
|
||||||
@ -31,9 +32,9 @@
|
|||||||
</StackPanel>
|
</StackPanel>
|
||||||
|
|
||||||
<!-- 右侧视频预览 -->
|
<!-- 右侧视频预览 -->
|
||||||
<Canvas x:Name="rtspPreviewCanvas" Grid.Column="1" Background="Black">
|
<Grid Grid.Column="1" Background="Black">
|
||||||
<Image x:Name="rtspPreviewImage" Stretch="Uniform" />
|
<Image x:Name="videoImage" Stretch="Uniform" />
|
||||||
</Canvas>
|
<Canvas x:Name="overlayCanvas" IsHitTestVisible="False" />
|
||||||
|
</Grid>
|
||||||
</Grid>
|
</Grid>
|
||||||
</Window>
|
</Window>
|
||||||
|
@ -1,15 +1,19 @@
|
|||||||
using Emgu.CV.Reg;
|
using Emgu.CV.Reg;
|
||||||
|
using SharpDX.Direct3D9;
|
||||||
using SkiaSharp;
|
using SkiaSharp;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.Drawing;
|
using System.Drawing.Imaging;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Net.NetworkInformation;
|
using System.Net.NetworkInformation;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
using System.Windows;
|
using System.Windows;
|
||||||
using System.Windows.Controls;
|
using System.Windows.Controls;
|
||||||
|
using System.Windows.Interop;
|
||||||
using System.Windows.Media;
|
using System.Windows.Media;
|
||||||
using System.Windows.Media.Imaging;
|
using System.Windows.Media.Imaging;
|
||||||
|
using System.Windows.Shapes;
|
||||||
|
using System.Windows.Threading;
|
||||||
using Wpf_AiSportsMicrospace.Common;
|
using Wpf_AiSportsMicrospace.Common;
|
||||||
using Yztob.AiSports.Common;
|
|
||||||
using Yztob.AiSports.Inferences.Abstractions;
|
using Yztob.AiSports.Inferences.Abstractions;
|
||||||
using Yztob.AiSports.Inferences.Things;
|
using Yztob.AiSports.Inferences.Things;
|
||||||
using Yztob.AiSports.Postures.Sports;
|
using Yztob.AiSports.Postures.Sports;
|
||||||
@ -31,7 +35,9 @@ public partial class MainWindow : Window
|
|||||||
private readonly List<SportDescriptor> _sports;
|
private readonly List<SportDescriptor> _sports;
|
||||||
private SportBase _sport;
|
private SportBase _sport;
|
||||||
private readonly SportDetectionQueue _detectQueue;
|
private readonly SportDetectionQueue _detectQueue;
|
||||||
private RTSPPreviewRenderer _rtspRenderer;
|
|
||||||
|
private WriteableBitmap _videoBitmap;
|
||||||
|
private int _lastFrameNumber = -1;
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
|
|
||||||
@ -51,13 +57,28 @@ public partial class MainWindow : Window
|
|||||||
this.sportList.ItemsSource = _sports;
|
this.sportList.ItemsSource = _sports;
|
||||||
this.sportList.SelectedIndex = 0;
|
this.sportList.SelectedIndex = 0;
|
||||||
this.OnSelectedSport();
|
this.OnSelectedSport();
|
||||||
|
}
|
||||||
// 确保 Canvas 已经加载完成
|
private void Window_Loaded(object sender, RoutedEventArgs e)
|
||||||
_rtspRenderer = new RTSPPreviewRenderer(rtspPreviewCanvas)
|
|
||||||
{
|
{
|
||||||
Adaptive = true
|
Application.Current.Dispatcher.InvokeAsync(() =>
|
||||||
};
|
{
|
||||||
|
DrawJumpRope3DPointsWithGlow();
|
||||||
|
}, System.Windows.Threading.DispatcherPriority.Loaded);
|
||||||
|
|
||||||
|
videoImage.SizeChanged += (s, ev) =>
|
||||||
|
{
|
||||||
|
DrawJumpRope3DPointsWithGlow();
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Window_Closing(object sender, System.ComponentModel.CancelEventArgs e)
|
||||||
|
{
|
||||||
|
base.OnClosed(e);
|
||||||
|
|
||||||
|
// 释放 WriteableBitmap
|
||||||
|
_videoBitmap = null;
|
||||||
|
videoImage.Source = null;
|
||||||
|
overlayCanvas = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void sportList_SelectionChanged(object sender, SelectionChangedEventArgs e)
|
private void sportList_SelectionChanged(object sender, SelectionChangedEventArgs e)
|
||||||
@ -138,28 +159,62 @@ public partial class MainWindow : Window
|
|||||||
}
|
}
|
||||||
private async void OnFrameExtracted(VideoFrame frame)
|
private async void OnFrameExtracted(VideoFrame frame)
|
||||||
{
|
{
|
||||||
|
if (frame == null) return;
|
||||||
|
|
||||||
|
// 跳帧:每秒只渲染 10~15 帧,降低 CPU 占用
|
||||||
|
if (_lastFrameNumber != -1 && frame.Number - _lastFrameNumber < 2) return;
|
||||||
|
_lastFrameNumber = (int)frame.Number;
|
||||||
|
|
||||||
//获得帧二进制流,保存图像、人体识别竺
|
//获得帧二进制流,保存图像、人体识别竺
|
||||||
var buffer = frame.GetImageBuffer(Yztob.AiSports.Sensors.Things.ImageFormat.Jpeg).ToArray();
|
var buffer = frame.GetImageBuffer(Yztob.AiSports.Sensors.Things.ImageFormat.Jpeg).ToArray();
|
||||||
await File.WriteAllBytesAsync($"./temps/{frame.Number}.jpg", buffer);
|
//await File.WriteAllBytesAsync($"./temps/{frame.Number}.jpg", buffer);
|
||||||
|
|
||||||
//// 2️⃣ 转换成 BGRA32
|
// === 显示到 WPF ===
|
||||||
//int width, height;
|
BitmapImage bitmap = null;
|
||||||
//var bgraBuffer = ConvertJpegToBGRA32(buffer, out width, out height);
|
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(); // 跨线程安全
|
||||||
|
});
|
||||||
|
|
||||||
//// 3️⃣ 更新 WPF Canvas
|
// UI 线程显示
|
||||||
//Application.Current.Dispatcher.Invoke(() =>
|
Application.Current.Dispatcher.Invoke(() =>
|
||||||
|
{
|
||||||
|
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();
|
||||||
|
|
||||||
|
//// 延迟一帧等待控件布局完成
|
||||||
|
//Application.Current.Dispatcher.InvokeAsync(() =>
|
||||||
//{
|
//{
|
||||||
// _rtspRenderer.UpdateFrame(bgraBuffer, width, height);
|
// DrawJumpRopePointsOnVideoImage();
|
||||||
//});
|
//}, System.Windows.Threading.DispatcherPriority.Loaded);
|
||||||
//_ = Task.Run(async () =>
|
});
|
||||||
//{
|
|
||||||
// await HumanPredictingAsync(frame.Number, buffer);
|
|
||||||
//});
|
|
||||||
//可以进一步进行人体识别等
|
//可以进一步进行人体识别等
|
||||||
var humanResult = await Task.Run<HumanInferenceResult>(() => _humanPredictor.Predicting(buffer, frame.Number));
|
//var humanResult = await Task.Run(() => _humanPredictor.Predicting(buffer, frame.Number));
|
||||||
|
|
||||||
var human = humanResult?.Humans?.FirstOrDefault();
|
//var human = humanResult?.Humans?.FirstOrDefault();
|
||||||
_detectQueue.Enqueue(frame.Number, human, null);
|
//_detectQueue.Enqueue(frame.Number, human, null);
|
||||||
}
|
}
|
||||||
private void OnSportTick(int counts, int times)
|
private void OnSportTick(int counts, int times)
|
||||||
{
|
{
|
||||||
@ -178,51 +233,80 @@ public partial class MainWindow : Window
|
|||||||
|
|
||||||
//VoiceBroadcast.PlayTick();
|
//VoiceBroadcast.PlayTick();
|
||||||
}
|
}
|
||||||
private byte[] ConvertJpegToBGRA32(byte[] jpegBuffer, out int width, out int height)
|
|
||||||
|
private void DrawJumpRope3DPointsWithGlow()
|
||||||
{
|
{
|
||||||
using var ms = new MemoryStream(jpegBuffer);
|
if (videoImage == null || overlayCanvas == null) return;
|
||||||
using var bmp = new Bitmap(ms);
|
|
||||||
|
|
||||||
width = bmp.Width;
|
overlayCanvas.Children.Clear();
|
||||||
height = bmp.Height;
|
|
||||||
|
|
||||||
var rect = new Rectangle(0, 0, width, height);
|
double imgWidth = videoImage.ActualWidth;
|
||||||
var bmpData = bmp.LockBits(rect, System.Drawing.Imaging.ImageLockMode.ReadOnly, System.Drawing.Imaging.PixelFormat.Format32bppArgb);
|
double imgHeight = videoImage.ActualHeight;
|
||||||
|
|
||||||
int bytes = Math.Abs(bmpData.Stride) * height;
|
if (imgWidth <= 0 || imgHeight <= 0) return;
|
||||||
byte[] bgra = new byte[bytes];
|
|
||||||
System.Runtime.InteropServices.Marshal.Copy(bmpData.Scan0, bgra, 0, bytes);
|
|
||||||
|
|
||||||
bmp.UnlockBits(bmpData);
|
overlayCanvas.Width = imgWidth;
|
||||||
return bgra;
|
overlayCanvas.Height = imgHeight;
|
||||||
|
|
||||||
|
// 前排 3 人(近景)
|
||||||
|
double frontRadius = 40;
|
||||||
|
var frontPositions = new List<(double XNorm, double YNorm)>
|
||||||
|
{
|
||||||
|
(0.2, 0.70), (0.5, 0.70), (0.8, 0.70) // 前排略高
|
||||||
|
};
|
||||||
|
|
||||||
|
foreach (var pos in frontPositions)
|
||||||
|
{
|
||||||
|
double x = pos.XNorm * imgWidth;
|
||||||
|
double y = pos.YNorm * imgHeight;
|
||||||
|
AddGlowEllipse(x, y, frontRadius, 0.8, overlayCanvas);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task HumanPredictingAsync(long frameNumber, byte[] buffer)
|
// 后排 4 人(远景,更靠下面,大一点)
|
||||||
|
double backRadius = 50;
|
||||||
|
var backPositions = new List<(double XNorm, double YNorm)>
|
||||||
{
|
{
|
||||||
var humanResultTask = Task.Run(() => _humanPredictor.Predicting(buffer, frameNumber));
|
(0.1, 0.88), (0.35, 0.88), (0.65, 0.88), (0.9, 0.88) // 后排靠底
|
||||||
var objectsTask = _sport.Equipment
|
};
|
||||||
? Task.Run(() => _objectDetector.Detecting(buffer))
|
|
||||||
: Task.FromResult(new List<BoundingBox>());
|
|
||||||
|
|
||||||
await Task.WhenAll(humanResultTask, objectsTask);
|
foreach (var pos in backPositions)
|
||||||
|
|
||||||
var humanResult = humanResultTask.Result;
|
|
||||||
var objects = objectsTask.Result;
|
|
||||||
|
|
||||||
if (_sport.MeasureApparatus != null)
|
|
||||||
objects.AddRange(_sport.MeasureApparatus.Apparatuses);
|
|
||||||
|
|
||||||
var human = humanResult?.Humans?.FirstOrDefault();
|
|
||||||
_detectQueue.Enqueue(frameNumber, human, objects);
|
|
||||||
|
|
||||||
// 更新渲染器属性(UI线程)
|
|
||||||
Application.Current.Dispatcher.Invoke(() =>
|
|
||||||
{
|
{
|
||||||
//_humanGraphicsRenderer.ScaleRatio = _rtspRenderer.ScaleRatio;
|
double x = pos.XNorm * imgWidth;
|
||||||
//_humanGraphicsRenderer.OffsetX = _rtspRenderer.OffsetX;
|
double y = pos.YNorm * imgHeight;
|
||||||
//_humanGraphicsRenderer.OffsetY = _rtspRenderer.OffsetY;
|
AddGlowEllipse(x, y, backRadius, 0.6, overlayCanvas);
|
||||||
_humanGraphicsRenderer.Humans = humanResult?.Humans;
|
|
||||||
_humanGraphicsRenderer.Objects = objects;
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 添加带渐变光的圆圈
|
||||||
|
/// </summary>
|
||||||
|
private void AddGlowEllipse(double centerX, double centerY, double radius, double opacity, Canvas canvas)
|
||||||
|
{
|
||||||
|
var ellipse = new Ellipse
|
||||||
|
{
|
||||||
|
Width = radius * 2,
|
||||||
|
Height = radius, // 压扁成椭圆模拟地面
|
||||||
|
Stroke = Brushes.White,
|
||||||
|
StrokeThickness = 2,
|
||||||
|
Opacity = opacity,
|
||||||
|
Fill = new RadialGradientBrush
|
||||||
|
{
|
||||||
|
GradientOrigin = new Point(0.5, 0.5),
|
||||||
|
Center = new Point(0.5, 0.5),
|
||||||
|
RadiusX = 0.5,
|
||||||
|
RadiusY = 0.5,
|
||||||
|
GradientStops = new GradientStopCollection
|
||||||
|
{
|
||||||
|
new GradientStop(Color.FromArgb(180, 255, 0, 0), 0), // 中心红色
|
||||||
|
new GradientStop(Color.FromArgb(0, 255, 0, 0), 1) // 外部透明
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
Canvas.SetLeft(ellipse, centerX - radius);
|
||||||
|
Canvas.SetTop(ellipse, centerY - radius / 2);
|
||||||
|
canvas.Children.Add(ellipse);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
@ -1,143 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.ComponentModel;
|
|
||||||
using System.IO;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Text;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
using System.Windows.Controls;
|
|
||||||
using System.Windows.Media.Imaging;
|
|
||||||
using System.Windows.Media;
|
|
||||||
using System.Windows;
|
|
||||||
using Yztob.AiSports.Sensors.Things;
|
|
||||||
|
|
||||||
namespace Wpf_AiSportsMicrospace
|
|
||||||
{
|
|
||||||
public class RTSPPreview : UserControl, IDisposable
|
|
||||||
{
|
|
||||||
private Image _imageControl;
|
|
||||||
private WriteableBitmap _bitmap;
|
|
||||||
private bool _isPlaying = false;
|
|
||||||
|
|
||||||
public RTSPPreview()
|
|
||||||
{
|
|
||||||
_imageControl = new Image();
|
|
||||||
this.Content = _imageControl;
|
|
||||||
this.Loaded += RTSPPreview_Loaded;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void RTSPPreview_Loaded(object sender, RoutedEventArgs e)
|
|
||||||
{
|
|
||||||
if (Adaptive)
|
|
||||||
{
|
|
||||||
_imageControl.Stretch = Stretch.Uniform;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
_imageControl.Stretch = Stretch.Fill;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#region 属性
|
|
||||||
|
|
||||||
[Browsable(true)]
|
|
||||||
[DefaultValue(true)]
|
|
||||||
[Description("是否自应用大小,保持同比缩放。")]
|
|
||||||
public bool Adaptive { get; set; } = true;
|
|
||||||
|
|
||||||
public string Host { get; set; }
|
|
||||||
public uint Port { get; set; }
|
|
||||||
public string UserName { get; set; }
|
|
||||||
public string Password { get; set; }
|
|
||||||
|
|
||||||
public Action<VideoFrame> OnExtracted { get; set; }
|
|
||||||
public Action<VideoFrameWithBuffer> OnFrameProcessing { get; set; }
|
|
||||||
|
|
||||||
public bool IsPlaying => _isPlaying;
|
|
||||||
|
|
||||||
public float OffsetX { get; private set; }
|
|
||||||
public float OffsetY { get; private set; }
|
|
||||||
public float ScaleRatio { get; private set; }
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region 播放/停止/保存
|
|
||||||
|
|
||||||
public void Play()
|
|
||||||
{
|
|
||||||
_isPlaying = true;
|
|
||||||
// TODO: RTSP 播放初始化
|
|
||||||
// 可使用 FFmpeg.AutoGen 或 LibVLCSharp
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Stop()
|
|
||||||
{
|
|
||||||
_isPlaying = false;
|
|
||||||
// TODO: 停止播放
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool SaveFrameToJpeg(string path)
|
|
||||||
{
|
|
||||||
if (_bitmap == null) return false;
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
BitmapEncoder encoder = new JpegBitmapEncoder();
|
|
||||||
encoder.Frames.Add(BitmapFrame.Create(_bitmap));
|
|
||||||
|
|
||||||
using (var fs = new FileStream(path, FileMode.Create, FileAccess.Write))
|
|
||||||
{
|
|
||||||
encoder.Save(fs);
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
catch
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region 更新帧
|
|
||||||
|
|
||||||
public void UpdateFrame(byte[] pixelData, int width, int height)
|
|
||||||
{
|
|
||||||
// 假设 pixelData 为 BGRA32
|
|
||||||
if (_bitmap == null || _bitmap.PixelWidth != width || _bitmap.PixelHeight != height)
|
|
||||||
{
|
|
||||||
_bitmap = new WriteableBitmap(width, height, 96, 96, PixelFormats.Bgra32, null);
|
|
||||||
_imageControl.Source = _bitmap;
|
|
||||||
}
|
|
||||||
|
|
||||||
_bitmap.Lock();
|
|
||||||
_bitmap.WritePixels(new Int32Rect(0, 0, width, height), pixelData, width * 4, 0);
|
|
||||||
_bitmap.AddDirtyRect(new Int32Rect(0, 0, width, height));
|
|
||||||
_bitmap.Unlock();
|
|
||||||
|
|
||||||
OnFrameProcessing?.Invoke(new VideoFrameWithBuffer
|
|
||||||
{
|
|
||||||
Width = width,
|
|
||||||
Height = height,
|
|
||||||
Buffer = pixelData
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
public void Dispose()
|
|
||||||
{
|
|
||||||
Stop();
|
|
||||||
_bitmap = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 数据类示例
|
|
||||||
|
|
||||||
public class VideoFrameWithBuffer
|
|
||||||
{
|
|
||||||
public int Width;
|
|
||||||
public int Height;
|
|
||||||
public byte[] Buffer;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,66 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.IO;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Windows.Controls;
|
|
||||||
using System.Windows.Media;
|
|
||||||
using System.Windows.Media.Imaging;
|
|
||||||
|
|
||||||
public class RTSPPreviewRenderer : IDisposable
|
|
||||||
{
|
|
||||||
private Canvas _canvas;
|
|
||||||
private Image _imageControl;
|
|
||||||
private WriteableBitmap _bitmap;
|
|
||||||
public bool Adaptive { get; set; } = true;
|
|
||||||
|
|
||||||
public RTSPPreviewRenderer(Canvas canvas)
|
|
||||||
{
|
|
||||||
_canvas = canvas ?? throw new ArgumentNullException(nameof(canvas));
|
|
||||||
|
|
||||||
_imageControl = new Image();
|
|
||||||
_canvas.Children.Add(_imageControl);
|
|
||||||
|
|
||||||
Canvas.SetLeft(_imageControl, 0);
|
|
||||||
Canvas.SetTop(_imageControl, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>更新帧到 Canvas</summary>
|
|
||||||
public void UpdateFrame(byte[] pixelData, int width, int height)
|
|
||||||
{
|
|
||||||
if (_bitmap == null || _bitmap.PixelWidth != width || _bitmap.PixelHeight != height)
|
|
||||||
{
|
|
||||||
_bitmap = new WriteableBitmap(width, height, 96, 96, PixelFormats.Bgra32, null);
|
|
||||||
_imageControl.Source = _bitmap;
|
|
||||||
}
|
|
||||||
|
|
||||||
_bitmap.Lock();
|
|
||||||
_bitmap.WritePixels(new System.Windows.Int32Rect(0, 0, width, height), pixelData, width * 4, 0);
|
|
||||||
_bitmap.AddDirtyRect(new System.Windows.Int32Rect(0, 0, width, height));
|
|
||||||
_bitmap.Unlock();
|
|
||||||
|
|
||||||
// 自动适应 Canvas
|
|
||||||
if (Adaptive)
|
|
||||||
{
|
|
||||||
double scaleX = _canvas.ActualWidth / width;
|
|
||||||
double scaleY = _canvas.ActualHeight / height;
|
|
||||||
double scale = Math.Min(scaleX, scaleY);
|
|
||||||
|
|
||||||
_imageControl.Width = width * scale;
|
|
||||||
_imageControl.Height = height * scale;
|
|
||||||
|
|
||||||
Canvas.SetLeft(_imageControl, (_canvas.ActualWidth - _imageControl.Width) / 2);
|
|
||||||
Canvas.SetTop(_imageControl, (_canvas.ActualHeight - _imageControl.Height) / 2);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
_imageControl.Width = _canvas.ActualWidth;
|
|
||||||
_imageControl.Height = _canvas.ActualHeight;
|
|
||||||
Canvas.SetLeft(_imageControl, 0);
|
|
||||||
Canvas.SetTop(_imageControl, 0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Dispose()
|
|
||||||
{
|
|
||||||
_bitmap = null;
|
|
||||||
}
|
|
||||||
}
|
|
@ -9,7 +9,7 @@
|
|||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="AutoMapper" Version="15.0.1" />
|
<PackageReference Include="AutoMapper" Version="14.0.0" />
|
||||||
<PackageReference Include="Emgu.CV" Version="4.12.0.5764" />
|
<PackageReference Include="Emgu.CV" Version="4.12.0.5764" />
|
||||||
<PackageReference Include="Emgu.CV.runtime.windows" Version="4.12.0.5764" />
|
<PackageReference Include="Emgu.CV.runtime.windows" Version="4.12.0.5764" />
|
||||||
<PackageReference Include="FFmpeg.AutoGen" Version="7.1.1" />
|
<PackageReference Include="FFmpeg.AutoGen" Version="7.1.1" />
|
||||||
@ -18,6 +18,7 @@
|
|||||||
<PackageReference Include="Microsoft.Management.Infrastructure" Version="3.0.0" />
|
<PackageReference Include="Microsoft.Management.Infrastructure" Version="3.0.0" />
|
||||||
<PackageReference Include="Microsoft.ML" Version="4.0.2" />
|
<PackageReference Include="Microsoft.ML" Version="4.0.2" />
|
||||||
<PackageReference Include="Microsoft.ML.OnnxRuntime" Version="1.22.1" />
|
<PackageReference Include="Microsoft.ML.OnnxRuntime" Version="1.22.1" />
|
||||||
|
<PackageReference Include="SharpDX.Direct3D9" Version="4.2.0" />
|
||||||
<PackageReference Include="SixLabors.ImageSharp" Version="3.1.11" />
|
<PackageReference Include="SixLabors.ImageSharp" Version="3.1.11" />
|
||||||
<PackageReference Include="SixLabors.ImageSharp.Drawing" Version="2.1.7" />
|
<PackageReference Include="SixLabors.ImageSharp.Drawing" Version="2.1.7" />
|
||||||
<PackageReference Include="SkiaSharp" Version="3.119.0" />
|
<PackageReference Include="SkiaSharp" Version="3.119.0" />
|
||||||
|
Loading…
x
Reference in New Issue
Block a user