探测器设置界面增加图像灰度直方图,用于显示实时采集图像的灰度信息,优化图像灰度直方图的显示方式(无图像提示)和优化资源释放。
This commit is contained in:
@@ -2,12 +2,18 @@
|
|||||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
xmlns:telerik="http://schemas.telerik.com/2008/xaml/presentation"
|
xmlns:telerik="http://schemas.telerik.com/2008/xaml/presentation"
|
||||||
|
xmlns:loc="clr-namespace:XP.Common.Localization.Extensions"
|
||||||
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"
|
||||||
mc:Ignorable="d"
|
mc:Ignorable="d"
|
||||||
d:DesignHeight="200" d:DesignWidth="400">
|
d:DesignHeight="200" d:DesignWidth="400">
|
||||||
<Grid>
|
<Grid>
|
||||||
|
<!-- 图表控件 | Chart control -->
|
||||||
<telerik:RadCartesianChart x:Name="HistogramChart">
|
<telerik:RadCartesianChart x:Name="HistogramChart">
|
||||||
|
<!-- 禁用 Telerik 自带的无数据提示 | Disable Telerik built-in empty content -->
|
||||||
|
<telerik:RadCartesianChart.EmptyContent>
|
||||||
|
<TextBlock/>
|
||||||
|
</telerik:RadCartesianChart.EmptyContent>
|
||||||
<!-- X 轴:灰度级别 | X Axis: Gray Level -->
|
<!-- X 轴:灰度级别 | X Axis: Gray Level -->
|
||||||
<telerik:RadCartesianChart.HorizontalAxis>
|
<telerik:RadCartesianChart.HorizontalAxis>
|
||||||
<telerik:LinearAxis x:Name="XAxis"
|
<telerik:LinearAxis x:Name="XAxis"
|
||||||
@@ -32,5 +38,14 @@
|
|||||||
ShowLabels="False"/>
|
ShowLabels="False"/>
|
||||||
</telerik:RadCartesianChart.Series>
|
</telerik:RadCartesianChart.Series>
|
||||||
</telerik:RadCartesianChart>
|
</telerik:RadCartesianChart>
|
||||||
|
|
||||||
|
<!-- 无数据提示文本(叠加在图表上方)| No data placeholder text (overlaid on chart) -->
|
||||||
|
<TextBlock x:Name="NoDataPlaceholder"
|
||||||
|
Text="{loc:Localization Histogram_NoData}"
|
||||||
|
HorizontalAlignment="Center"
|
||||||
|
VerticalAlignment="Center"
|
||||||
|
FontSize="14"
|
||||||
|
Foreground="#9E9E9E"
|
||||||
|
Visibility="Visible"/>
|
||||||
</Grid>
|
</Grid>
|
||||||
</UserControl>
|
</UserControl>
|
||||||
|
|||||||
@@ -219,10 +219,24 @@ namespace XP.Common.Controls.ImageHistogram
|
|||||||
// 取消帧率限流器中的待处理任务 | Cancel pending tasks in throttler
|
// 取消帧率限流器中的待处理任务 | Cancel pending tasks in throttler
|
||||||
_frameThrottler.Cancel();
|
_frameThrottler.Cancel();
|
||||||
|
|
||||||
// 清空图表 | Clear chart
|
// 清空图表(捕获局部引用避免异步执行时为 null)| Clear chart (capture local ref to avoid null during async)
|
||||||
if (_chartRenderer != null)
|
var renderer = _chartRenderer;
|
||||||
|
if (renderer != null)
|
||||||
{
|
{
|
||||||
Dispatcher.InvokeAsync(() => _chartRenderer.Clear());
|
Dispatcher.InvokeAsync(() =>
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
renderer.Clear();
|
||||||
|
|
||||||
|
// 显示无数据提示 | Show no-data placeholder
|
||||||
|
NoDataPlaceholder.Visibility = Visibility.Visible;
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
// 控件已卸载时忽略 | Ignore if control already unloaded
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
@@ -259,6 +273,9 @@ namespace XP.Common.Controls.ImageHistogram
|
|||||||
{
|
{
|
||||||
isLog = IsLogarithmic;
|
isLog = IsLogarithmic;
|
||||||
_chartRenderer?.UpdateData(histogram, isLog);
|
_chartRenderer?.UpdateData(histogram, isLog);
|
||||||
|
|
||||||
|
// 隐藏无数据提示 | Hide no-data placeholder
|
||||||
|
NoDataPlaceholder.Visibility = Visibility.Collapsed;
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1887,4 +1887,8 @@ Reprojection error: {1:F4} pixels</value>
|
|||||||
<data name="ChessboardImageError" xml:space="preserve">
|
<data name="ChessboardImageError" xml:space="preserve">
|
||||||
<value>Image{0}: {1:F4} pixels</value>
|
<value>Image{0}: {1:F4} pixels</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="Histogram_NoData" xml:space="preserve">
|
||||||
|
<value>Histogram — No data</value>
|
||||||
|
<comment>ImageHistogramControl - Placeholder text when no image data</comment>
|
||||||
|
</data>
|
||||||
</root>
|
</root>
|
||||||
@@ -1920,4 +1920,8 @@
|
|||||||
<data name="ChessboardImageError" xml:space="preserve">
|
<data name="ChessboardImageError" xml:space="preserve">
|
||||||
<value>图像{0}: {1:F4} 像素</value>
|
<value>图像{0}: {1:F4} 像素</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="Histogram_NoData" xml:space="preserve">
|
||||||
|
<value>直方图 — 暂无数据</value>
|
||||||
|
<comment>ImageHistogramControl - 无图像输入时的提示文本 | Placeholder text when no image data</comment>
|
||||||
|
</data>
|
||||||
</root>
|
</root>
|
||||||
@@ -1881,4 +1881,8 @@
|
|||||||
<data name="ChessboardImageError" xml:space="preserve">
|
<data name="ChessboardImageError" xml:space="preserve">
|
||||||
<value>图像{0}: {1:F4} 像素</value>
|
<value>图像{0}: {1:F4} 像素</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="Histogram_NoData" xml:space="preserve">
|
||||||
|
<value>直方图 — 暂无数据</value>
|
||||||
|
<comment>ImageHistogramControl - 无图像输入时的提示文本 | Placeholder text when no image data</comment>
|
||||||
|
</data>
|
||||||
</root>
|
</root>
|
||||||
@@ -1881,4 +1881,8 @@
|
|||||||
<data name="ChessboardImageError" xml:space="preserve">
|
<data name="ChessboardImageError" xml:space="preserve">
|
||||||
<value>图像{0}: {1:F4} 像素</value>
|
<value>图像{0}: {1:F4} 像素</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="Histogram_NoData" xml:space="preserve">
|
||||||
|
<value>直方圖 — 暫無資料</value>
|
||||||
|
<comment>ImageHistogramControl - 無圖像輸入時的提示文字 | Placeholder text when no image data</comment>
|
||||||
|
</data>
|
||||||
</root>
|
</root>
|
||||||
@@ -6,8 +6,9 @@
|
|||||||
xmlns:prism="http://prismlibrary.com/"
|
xmlns:prism="http://prismlibrary.com/"
|
||||||
xmlns:telerik="http://schemas.telerik.com/2008/xaml/presentation"
|
xmlns:telerik="http://schemas.telerik.com/2008/xaml/presentation"
|
||||||
xmlns:loc="clr-namespace:XP.Common.Localization.Extensions;assembly=XP.Common"
|
xmlns:loc="clr-namespace:XP.Common.Localization.Extensions;assembly=XP.Common"
|
||||||
|
xmlns:hist="clr-namespace:XP.Common.Controls.ImageHistogram;assembly=XP.Common"
|
||||||
mc:Ignorable="d"
|
mc:Ignorable="d"
|
||||||
d:DesignWidth="420" d:DesignHeight="210"
|
d:DesignWidth="420" d:DesignHeight="360"
|
||||||
prism:ViewModelLocator.AutoWireViewModel="True"
|
prism:ViewModelLocator.AutoWireViewModel="True"
|
||||||
Background="White">
|
Background="White">
|
||||||
|
|
||||||
@@ -22,6 +23,8 @@
|
|||||||
<RowDefinition Height="Auto"/>
|
<RowDefinition Height="Auto"/>
|
||||||
<RowDefinition Height="10"/>
|
<RowDefinition Height="10"/>
|
||||||
<RowDefinition Height="Auto"/>
|
<RowDefinition Height="Auto"/>
|
||||||
|
<RowDefinition Height="10"/>
|
||||||
|
<RowDefinition Height="150"/>
|
||||||
</Grid.RowDefinitions>
|
</Grid.RowDefinitions>
|
||||||
|
|
||||||
<!-- 第1行:灵敏度 + 帧率 | Row 1: Sensitivity (PGA) + Frame rate -->
|
<!-- 第1行:灵敏度 + 帧率 | Row 1: Sensitivity (PGA) + Frame rate -->
|
||||||
@@ -136,5 +139,12 @@
|
|||||||
</telerik:RadButton.Background>
|
</telerik:RadButton.Background>
|
||||||
</telerik:RadButton>
|
</telerik:RadButton>
|
||||||
</Grid>
|
</Grid>
|
||||||
|
|
||||||
|
<!-- 第6行:图像灰度直方图 | Row 6: Image grayscale histogram -->
|
||||||
|
<hist:ImageHistogramControl x:Name="HistogramControl"
|
||||||
|
Grid.Row="10"
|
||||||
|
MaxFrameRate="10"
|
||||||
|
IsLogarithmic="False"
|
||||||
|
HorizontalAlignment="Stretch"/>
|
||||||
</Grid>
|
</Grid>
|
||||||
</UserControl>
|
</UserControl>
|
||||||
|
|||||||
@@ -1,15 +1,82 @@
|
|||||||
|
using System;
|
||||||
|
using System.Windows;
|
||||||
using System.Windows.Controls;
|
using System.Windows.Controls;
|
||||||
|
using Prism.Events;
|
||||||
|
using Prism.Ioc;
|
||||||
|
using XP.Hardware.Detector.Abstractions;
|
||||||
|
using XP.Hardware.Detector.Abstractions.Events;
|
||||||
|
|
||||||
namespace XP.Hardware.Detector.Views
|
namespace XP.Hardware.Detector.Views
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 面阵探测器配置视图 | Area detector configuration view
|
/// 面阵探测器配置视图 | Area detector configuration view
|
||||||
|
/// 订阅探测器图像采集事件,将图像数据传递给直方图控件
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public partial class DetectorConfigView : UserControl
|
public partial class DetectorConfigView : UserControl
|
||||||
{
|
{
|
||||||
|
private IEventAggregator _eventAggregator;
|
||||||
|
private SubscriptionToken _imageSubscriptionToken;
|
||||||
|
|
||||||
public DetectorConfigView()
|
public DetectorConfigView()
|
||||||
{
|
{
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
|
Loaded += OnLoaded;
|
||||||
|
Unloaded += OnUnloaded;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 加载时订阅图像采集事件 | Subscribe to image captured event on load
|
||||||
|
/// </summary>
|
||||||
|
private void OnLoaded(object sender, RoutedEventArgs e)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
_eventAggregator = ContainerLocator.Current?.Resolve<IEventAggregator>();
|
||||||
|
if (_eventAggregator != null)
|
||||||
|
{
|
||||||
|
_imageSubscriptionToken = _eventAggregator.GetEvent<ImageCapturedEvent>()
|
||||||
|
.Subscribe(OnImageCaptured, ThreadOption.BackgroundThread);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
// 事件聚合器不可用时静默降级 | Silent degradation when event aggregator unavailable
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 卸载时取消订阅 | Unsubscribe on unload
|
||||||
|
/// </summary>
|
||||||
|
private void OnUnloaded(object sender, RoutedEventArgs e)
|
||||||
|
{
|
||||||
|
if (_eventAggregator != null && _imageSubscriptionToken != null)
|
||||||
|
{
|
||||||
|
_eventAggregator.GetEvent<ImageCapturedEvent>().Unsubscribe(_imageSubscriptionToken);
|
||||||
|
_imageSubscriptionToken = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 图像采集回调:将 ushort[] 转为 byte[] 后传给直方图控件 | Image captured callback
|
||||||
|
/// </summary>
|
||||||
|
private void OnImageCaptured(ImageCapturedEventArgs args)
|
||||||
|
{
|
||||||
|
if (args?.ImageData == null || args.Width == 0 || args.Height == 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// 将 ushort[] 转换为 little-endian byte[] | Convert ushort[] to little-endian byte[]
|
||||||
|
var rawBytes = new byte[args.ImageData.Length * 2];
|
||||||
|
Buffer.BlockCopy(args.ImageData, 0, rawBytes, 0, rawBytes.Length);
|
||||||
|
|
||||||
|
// 调用直方图控件更新(控件内部支持从非 UI 线程调用)| Update histogram control (supports non-UI thread calls)
|
||||||
|
HistogramControl?.UpdateImage(rawBytes, (int)args.Width, (int)args.Height, 16);
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
// 异常不影响主流程 | Exception does not affect main flow
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,8 +4,8 @@
|
|||||||
xmlns:loc="clr-namespace:XP.Common.Localization.Extensions;assembly=XP.Common"
|
xmlns:loc="clr-namespace:XP.Common.Localization.Extensions;assembly=XP.Common"
|
||||||
xmlns:detectorViews="clr-namespace:XP.Hardware.Detector.Views"
|
xmlns:detectorViews="clr-namespace:XP.Hardware.Detector.Views"
|
||||||
Title="{loc:Localization Detector_ConfigWindowTitle}"
|
Title="{loc:Localization Detector_ConfigWindowTitle}"
|
||||||
Height="230" Width="400"
|
Height="420" Width="400"
|
||||||
MinHeight="230" MinWidth="360"
|
MinHeight="380" MinWidth="360"
|
||||||
WindowStartupLocation="CenterOwner"
|
WindowStartupLocation="CenterOwner"
|
||||||
ResizeMode="CanResize">
|
ResizeMode="CanResize">
|
||||||
<Grid>
|
<Grid>
|
||||||
|
|||||||
Reference in New Issue
Block a user