using System.Windows; using System.Windows.Controls; using System.Windows.Media; using System.Windows.Media.Imaging; using System.Windows.Shapes; using System.Windows.Media.Animation; namespace OrpaonVision.SiteApp.Controls; /// /// 实时图像显示控件。 /// public partial class RealTimeImageDisplay : UserControl { /// /// 图像源依赖属性。 /// public static readonly DependencyProperty ImageSourceProperty = DependencyProperty.Register(nameof(ImageSource), typeof(BitmapSource), typeof(RealTimeImageDisplay), new PropertyMetadata(null, OnImageSourceChanged)); /// /// 显示模式依赖属性。 /// public static readonly DependencyProperty DisplayModeProperty = DependencyProperty.Register(nameof(DisplayMode), typeof(ImageDisplayMode), typeof(RealTimeImageDisplay), new PropertyMetadata(ImageDisplayMode.Fit, OnDisplayModeChanged)); /// /// 显示检测框依赖属性。 /// public static readonly DependencyProperty ShowDetectionBoxesProperty = DependencyProperty.Register(nameof(ShowDetectionBoxes), typeof(bool), typeof(RealTimeImageDisplay), new PropertyMetadata(true, OnShowDetectionBoxesChanged)); /// /// 检测结果依赖属性。 /// public static readonly DependencyProperty DetectionResultsProperty = DependencyProperty.Register(nameof(DetectionResults), typeof(List), typeof(RealTimeImageDisplay), new PropertyMetadata(null, OnDetectionResultsChanged)); /// /// 构造函数。 /// public RealTimeImageDisplay() { InitializeComponent(); InitializeCanvas(); } /// /// 图像源。 /// public BitmapSource? ImageSource { get => (BitmapSource?)GetValue(ImageSourceProperty); set => SetValue(ImageSourceProperty, value); } /// /// 显示模式。 /// public ImageDisplayMode DisplayMode { get => (ImageDisplayMode)GetValue(DisplayModeProperty); set => SetValue(DisplayModeProperty, value); } /// /// 是否显示检测框。 /// public bool ShowDetectionBoxes { get => (bool)GetValue(ShowDetectionBoxesProperty); set => SetValue(ShowDetectionBoxesProperty, value); } /// /// 检测结果。 /// public List? DetectionResults { get => (List?)GetValue(DetectionResultsProperty); set => SetValue(DetectionResultsProperty, value); } /// /// 图像控件。 /// private Image _imageControl = null!; /// /// 检测框画布。 /// private Canvas _detectionCanvas = null!; /// /// 当前图像尺寸。 /// private Size _currentImageSize = Size.Empty; /// /// 显示区域尺寸。 /// private Size _displaySize = Size.Empty; /// /// 缩放比例。 /// private double _scaleX = 1.0; private double _scaleY = 1.0; private static void OnImageSourceChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { if (d is RealTimeImageDisplay control) { control.OnImageSourceChanged((BitmapSource?)e.NewValue); } } private static void OnDisplayModeChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { if (d is RealTimeImageDisplay control) { control.OnDisplayModeChanged((ImageDisplayMode)e.NewValue); } } private static void OnShowDetectionBoxesChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { if (d is RealTimeImageDisplay control) { control.OnShowDetectionBoxesChanged((bool)e.NewValue); } } private static void OnDetectionResultsChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { if (d is RealTimeImageDisplay control) { control.OnDetectionResultsChanged((List?)e.NewValue); } } private void OnImageSourceChanged(BitmapSource? newImageSource) { if (newImageSource != null) { _currentImageSize = new Size(newImageSource.PixelWidth, newImageSource.PixelHeight); _imageControl.Source = newImageSource; UpdateImageLayout(); UpdateDetectionBoxes(); } else { _imageControl.Source = null; _currentImageSize = Size.Empty; ClearDetectionBoxes(); } } private void OnDisplayModeChanged(ImageDisplayMode newMode) { UpdateImageLayout(); } private void OnShowDetectionBoxesChanged(bool showBoxes) { _detectionCanvas.Visibility = showBoxes ? Visibility.Visible : Visibility.Collapsed; } private void OnDetectionResultsChanged(List? newResults) { UpdateDetectionBoxes(); } private void InitializeCanvas() { _imageControl = new Image { Stretch = Stretch.Uniform, HorizontalAlignment = HorizontalAlignment.Center, VerticalAlignment = VerticalAlignment.Center }; _detectionCanvas = new Canvas { IsHitTestVisible = false, Background = Brushes.Transparent }; var grid = new Grid(); grid.Children.Add(_imageControl); grid.Children.Add(_detectionCanvas); Content = grid; } private void UpdateImageLayout() { if (_currentImageSize.IsEmpty) return; _displaySize = new Size(ActualWidth, ActualHeight); if (_displaySize.Width <= 0 || _displaySize.Height <= 0) return; switch (DisplayMode) { case ImageDisplayMode.Fit: UpdateFitLayout(); break; case ImageDisplayMode.Fill: UpdateFillLayout(); break; case ImageDisplayMode.Original: UpdateOriginalLayout(); break; } } private void UpdateFitLayout() { var imageRatio = _currentImageSize.Width / _currentImageSize.Height; var displayRatio = _displaySize.Width / _displaySize.Height; if (imageRatio > displayRatio) { // 以宽度为准 _scaleX = _displaySize.Width / _currentImageSize.Width; _scaleY = _scaleX; } else { // 以高度为准 _scaleY = _displaySize.Height / _currentImageSize.Height; _scaleX = _scaleY; } _imageControl.Stretch = Stretch.Uniform; } private void UpdateFillLayout() { _scaleX = _displaySize.Width / _currentImageSize.Width; _scaleY = _displaySize.Height / _currentImageSize.Height; _imageControl.Stretch = Stretch.Fill; } private void UpdateOriginalLayout() { _scaleX = 1.0; _scaleY = 1.0; _imageControl.Stretch = Stretch.None; } private void UpdateDetectionBoxes() { if (!ShowDetectionBoxes || DetectionResults == null || DetectionResults.Count == 0) { ClearDetectionBoxes(); return; } _detectionCanvas.Children.Clear(); foreach (var detection in DetectionResults) { var rect = CreateDetectionRectangle(detection); _detectionCanvas.Children.Add(rect); } } private Rectangle CreateDetectionRectangle(DetectionBox detection) { var color = GetDetectionColor(detection.ClassName); var brush = new SolidColorBrush(color); var rect = new Rectangle { Stroke = brush, StrokeThickness = 2, Fill = new SolidColorBrush(color) { Opacity = 0.1 } }; // 计算缩放后的位置和尺寸 var x = detection.X * _scaleX; var y = detection.Y * _scaleY; var width = detection.Width * _scaleX; var height = detection.Height * _scaleY; // 居中显示 var offsetX = (_displaySize.Width - _currentImageSize.Width * _scaleX) / 2; var offsetY = (_displaySize.Height - _currentImageSize.Height * _scaleY) / 2; Canvas.SetLeft(rect, x + offsetX); Canvas.SetTop(rect, y + offsetY); Canvas.SetZIndex(rect, 1); // 添加标签 var labelColor = new SolidColorBrush(color); var label = new TextBlock { Text = $"{detection.ClassName} ({detection.Confidence:P0})", Foreground = labelColor, FontSize = 12, FontWeight = FontWeights.Bold, Background = Brushes.White, Padding = new Thickness(4, 2, 4, 2) }; Canvas.SetLeft(label, x + offsetX); Canvas.SetTop(label, y + offsetY - 20); Canvas.SetZIndex(label, 2); _detectionCanvas.Children.Add(label); return rect; } private void ClearDetectionBoxes() { _detectionCanvas.Children.Clear(); } private static Color GetDetectionColor(string className) { return className.ToLowerInvariant() switch { "ng" or "defect" => Color.FromRgb(255, 0, 0), // 红色 "ok" or "good" => Color.FromRgb(0, 255, 0), // 绿色 "warning" => Color.FromRgb(255, 165, 0), // 橙色 _ => Color.FromRgb(0, 0, 255) // 蓝色 }; } protected override void OnRenderSizeChanged(SizeChangedInfo sizeInfo) { base.OnRenderSizeChanged(sizeInfo); _displaySize = new Size(sizeInfo.NewSize.Width, sizeInfo.NewSize.Height); UpdateImageLayout(); UpdateDetectionBoxes(); } } /// /// 图像显示模式。 /// public enum ImageDisplayMode { /// /// 适应显示(保持比例)。 /// Fit, /// /// 填充显示(可能变形)。 /// Fill, /// /// 原始尺寸显示。 /// Original } /// /// 检测框信息。 /// public sealed class DetectionBox { /// /// 类别名称。 /// public string ClassName { get; set; } = string.Empty; /// /// 置信度。 /// public double Confidence { get; set; } /// /// X坐标。 /// public double X { get; set; } /// /// Y坐标。 /// public double Y { get; set; } /// /// 宽度。 /// public double Width { get; set; } /// /// 高度。 /// public double Height { get; set; } /// /// 标签信息。 /// public string? Label { get; set; } }