行灰度功能添加

This commit is contained in:
李伟
2026-05-14 13:48:56 +08:00
parent aedbef5ecc
commit 7441526ed9
3 changed files with 178 additions and 2 deletions
@@ -1,6 +1,7 @@
using System;
using System.ComponentModel;
using System.IO;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
@@ -128,8 +129,178 @@ namespace XplorePlane.Views
var vm = GetMainVm();
if (vm != null) vm.CursorInfoText = RoiCanvas.CursorInfo;
});
// 行灰度分布
try
{
var ea2 = ContainerLocator.Current?.Resolve<Prism.Events.IEventAggregator>();
ea2?.GetEvent<ToggleLineProfileEvent>().Subscribe(() =>
{
ToggleLineProfile();
}, Prism.Events.ThreadOption.UIThread);
}
catch { }
}
#region
private bool _lineProfileEnabled;
private System.Windows.Shapes.Line _profileRefLine; // 透明命中区域
private System.Windows.Shapes.Line _profileRefLineVisible; // 1px红线显示
private System.Windows.Shapes.Polyline _profileCurve;
private double _profileLineY;
private bool _profileDragging;
private void ToggleLineProfile()
{
_lineProfileEnabled = !_lineProfileEnabled;
var canvas = FindChildByName<System.Windows.Controls.Canvas>(RoiCanvas, "mainCanvas");
if (canvas == null) return;
if (_lineProfileEnabled)
{
// 参考线默认在图像中间
_profileLineY = RoiCanvas.CanvasHeight / 2;
// 创建参考线(红色水平线,可拖动)
// 用透明粗线作为命中区域,叠加1px红线显示
_profileRefLine = new System.Windows.Shapes.Line
{
X1 = 0,
Y1 = _profileLineY,
X2 = RoiCanvas.CanvasWidth,
Y2 = _profileLineY,
Stroke = System.Windows.Media.Brushes.Transparent,
StrokeThickness = 7, // 上下3px命中区域
IsHitTestVisible = true,
Cursor = System.Windows.Input.Cursors.SizeNS
};
_profileRefLineVisible = new System.Windows.Shapes.Line
{
X1 = 0,
Y1 = _profileLineY,
X2 = RoiCanvas.CanvasWidth,
Y2 = _profileLineY,
Stroke = System.Windows.Media.Brushes.Red,
StrokeThickness = 1,
IsHitTestVisible = false
};
_profileRefLine.MouseLeftButtonDown += ProfileLine_MouseDown;
_profileRefLine.MouseMove += ProfileLine_MouseMove;
_profileRefLine.MouseLeftButtonUp += ProfileLine_MouseUp;
canvas.Children.Add(_profileRefLineVisible);
canvas.Children.Add(_profileRefLine);
// 创建灰度折线(固定显示在图像中间位置)
_profileCurve = new System.Windows.Shapes.Polyline
{
Stroke = System.Windows.Media.Brushes.Red,
StrokeThickness = 1,
IsHitTestVisible = false
};
canvas.Children.Add(_profileCurve);
UpdateLineProfile();
SetStatus("行灰度分布:拖动红线改变采样行,再次点击按钮关闭");
}
else
{
if (_profileRefLine != null)
{
canvas.Children.Remove(_profileRefLine);
_profileRefLine = null;
}
if (_profileRefLineVisible != null)
{
canvas.Children.Remove(_profileRefLineVisible);
_profileRefLineVisible = null;
}
if (_profileCurve != null)
{
canvas.Children.Remove(_profileCurve);
_profileCurve = null;
}
SetStatus("行灰度分布已关闭");
}
}
private void ProfileLine_MouseDown(object sender, System.Windows.Input.MouseButtonEventArgs e)
{
_profileDragging = true;
_profileRefLine?.CaptureMouse();
e.Handled = true;
}
private void ProfileLine_MouseMove(object sender, System.Windows.Input.MouseEventArgs e)
{
if (!_profileDragging || _profileRefLine == null) return;
var canvas = FindChildByName<System.Windows.Controls.Canvas>(RoiCanvas, "mainCanvas");
if (canvas == null) return;
var pos = e.GetPosition(canvas);
_profileLineY = Math.Clamp(pos.Y, 0, RoiCanvas.CanvasHeight - 1);
_profileRefLine.Y1 = _profileLineY;
_profileRefLine.Y2 = _profileLineY;
_profileRefLineVisible.Y1 = _profileLineY;
_profileRefLineVisible.Y2 = _profileLineY;
UpdateLineProfile();
}
private void ProfileLine_MouseUp(object sender, System.Windows.Input.MouseButtonEventArgs e)
{
_profileDragging = false;
_profileRefLine?.ReleaseMouseCapture();
e.Handled = true;
}
private void UpdateLineProfile()
{
if (_profileCurve == null) return;
// 从当前显示图像获取像素数据
var viewportVm = DataContext as ViewportPanelViewModel;
var imageSource = viewportVm?.ImageSource as System.Windows.Media.Imaging.BitmapSource;
if (imageSource == null) return;
int imgWidth = imageSource.PixelWidth;
int imgHeight = imageSource.PixelHeight;
int row = (int)Math.Clamp(_profileLineY, 0, imgHeight - 1);
// 转为 Gray8 获取行像素
System.Windows.Media.Imaging.BitmapSource gray8;
if (imageSource.Format != System.Windows.Media.PixelFormats.Gray8)
gray8 = new System.Windows.Media.Imaging.FormatConvertedBitmap(
imageSource, System.Windows.Media.PixelFormats.Gray8, null, 0);
else
gray8 = imageSource;
byte[] rowPixels = new byte[imgWidth];
int stride = imgWidth;
gray8.CopyPixels(new System.Windows.Int32Rect(0, row, imgWidth, 1), rowPixels, stride, 0);
// 构建折线点集:折线固定显示在图像垂直中间位置
// 参考线位置决定采样哪一行,折线位置固定在画布中间
double canvasH = RoiCanvas.CanvasHeight;
double curveCenter = canvasH / 2.0; // 折线基线固定在图像中间
double displayHeight = canvasH * 0.25; // 折线振幅为画布高度的25%
var points = new System.Windows.Media.PointCollection(imgWidth);
for (int x = 0; x < imgWidth; x++)
{
double normalizedGray = rowPixels[x] / 255.0;
double y = curveCenter - normalizedGray * displayHeight;
points.Add(new System.Windows.Point(x, y));
}
_profileCurve.Points = points;
SetStatus($"行灰度分布 | Y={row} | 均值={rowPixels.Select(b => (double)b).Average():F1} | 最大={rowPixels.Max()} | 最小={rowPixels.Min()}");
}
#endregion
private void OnDataContextChanged(object sender, DependencyPropertyChangedEventArgs e)
{
if (e.OldValue is INotifyPropertyChanged oldVm)