TURBO-569:更新工程结构;将导航相机标定和校准功能迁移到XP.Camera类
This commit is contained in:
@@ -0,0 +1,176 @@
|
||||
<UserControl x:Class="XP.Camera.Calibration.Controls.CalibrationControl"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:iconPacks="http://metro.mahapps.com/winfx/xaml/iconpacks"
|
||||
xmlns:cal="clr-namespace:XP.Camera.Calibration"
|
||||
xmlns:controls="clr-namespace:XP.Camera.Calibration.Controls"
|
||||
mc:Ignorable="d"
|
||||
d:DesignHeight="850"
|
||||
d:DesignWidth="1400">
|
||||
<UserControl.Resources>
|
||||
<cal:CalibrationLocalizedStrings x:Key="LocalizedStrings" />
|
||||
<SolidColorBrush x:Key="PrimaryColor" Color="#F5F5F5" />
|
||||
<SolidColorBrush x:Key="AccentColor" Color="#0078D4" />
|
||||
<SolidColorBrush x:Key="BackgroundColor" Color="#FAFAFA" />
|
||||
<SolidColorBrush x:Key="SidebarColor" Color="#FFFFFF" />
|
||||
<SolidColorBrush x:Key="BorderColor" Color="#E1E1E1" />
|
||||
<SolidColorBrush x:Key="TextColor" Color="#333333" />
|
||||
<SolidColorBrush x:Key="TextSecondaryColor" Color="#666666" />
|
||||
|
||||
<Style x:Key="ToolbarButtonStyle" TargetType="Button">
|
||||
<Setter Property="Width" Value="90" />
|
||||
<Setter Property="Height" Value="70" />
|
||||
<Setter Property="Margin" Value="0,0,8,0" />
|
||||
<Setter Property="Background" Value="White" />
|
||||
<Setter Property="Foreground" Value="{StaticResource TextColor}" />
|
||||
<Setter Property="BorderBrush" Value="{StaticResource BorderColor}" />
|
||||
<Setter Property="BorderThickness" Value="1" />
|
||||
<Setter Property="Cursor" Value="Hand" />
|
||||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
<ControlTemplate TargetType="Button">
|
||||
<Border Background="{TemplateBinding Background}"
|
||||
BorderBrush="{TemplateBinding BorderBrush}"
|
||||
BorderThickness="{TemplateBinding BorderThickness}"
|
||||
CornerRadius="3">
|
||||
<StackPanel HorizontalAlignment="Center" VerticalAlignment="Center">
|
||||
<ContentPresenter Content="{TemplateBinding Tag}"
|
||||
HorizontalAlignment="Center"
|
||||
Margin="0,4,0,4" />
|
||||
<TextBlock Text="{TemplateBinding Content}"
|
||||
FontSize="12"
|
||||
HorizontalAlignment="Center"
|
||||
Foreground="{TemplateBinding Foreground}"
|
||||
TextWrapping="Wrap"
|
||||
TextAlignment="Center" />
|
||||
</StackPanel>
|
||||
</Border>
|
||||
<ControlTemplate.Triggers>
|
||||
<Trigger Property="IsMouseOver" Value="True">
|
||||
<Setter Property="Background" Value="#E5F3FF" />
|
||||
<Setter Property="BorderBrush" Value="{StaticResource AccentColor}" />
|
||||
</Trigger>
|
||||
<Trigger Property="IsPressed" Value="True">
|
||||
<Setter Property="Background" Value="#CCE8FF" />
|
||||
</Trigger>
|
||||
<Trigger Property="IsEnabled" Value="False">
|
||||
<Setter Property="Opacity" Value="0.5" />
|
||||
</Trigger>
|
||||
</ControlTemplate.Triggers>
|
||||
</ControlTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Style>
|
||||
</UserControl.Resources>
|
||||
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="*" />
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<Border Grid.Row="0" Background="{StaticResource PrimaryColor}" BorderBrush="{StaticResource BorderColor}"
|
||||
BorderThickness="0,0,0,1" Padding="15,10">
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<Button Content="{Binding Source={StaticResource LocalizedStrings}, Path=Resources.CalibrationLoadImage}"
|
||||
Command="{Binding LoadImageCommand}" FontFamily="Segoe UI"
|
||||
Style="{StaticResource ToolbarButtonStyle}">
|
||||
<Button.Tag>
|
||||
<iconPacks:PackIconMaterial Kind="ImageOutline" Width="24" Height="24" />
|
||||
</Button.Tag>
|
||||
</Button>
|
||||
<Button Content="{Binding Source={StaticResource LocalizedStrings}, Path=Resources.CalibrationLoadCsv}"
|
||||
Command="{Binding LoadCsvCommand}" FontFamily="Segoe UI"
|
||||
Style="{StaticResource ToolbarButtonStyle}">
|
||||
<Button.Tag>
|
||||
<iconPacks:PackIconMaterial Kind="FileDelimited" Width="24" Height="24" />
|
||||
</Button.Tag>
|
||||
</Button>
|
||||
<Button Content="{Binding Source={StaticResource LocalizedStrings}, Path=Resources.CalibrationExecute}"
|
||||
Command="{Binding CalibrateCommand}" FontFamily="Segoe UI"
|
||||
Style="{StaticResource ToolbarButtonStyle}">
|
||||
<Button.Tag>
|
||||
<iconPacks:PackIconMaterial Kind="Crosshairs" Width="24" Height="24" />
|
||||
</Button.Tag>
|
||||
</Button>
|
||||
<Button Content="{Binding Source={StaticResource LocalizedStrings}, Path=Resources.CalibrationSave}"
|
||||
Command="{Binding SaveCalibrationCommand}" FontFamily="Segoe UI"
|
||||
Style="{StaticResource ToolbarButtonStyle}">
|
||||
<Button.Tag>
|
||||
<iconPacks:PackIconMaterial Kind="ContentSave" Width="24" Height="24" />
|
||||
</Button.Tag>
|
||||
</Button>
|
||||
<Button Content="{Binding Source={StaticResource LocalizedStrings}, Path=Resources.CalibrationLoad}"
|
||||
Command="{Binding LoadCalibrationCommand}" FontFamily="Segoe UI"
|
||||
Style="{StaticResource ToolbarButtonStyle}">
|
||||
<Button.Tag>
|
||||
<iconPacks:PackIconMaterial Kind="FolderOpen" Width="24" Height="24" />
|
||||
</Button.Tag>
|
||||
</Button>
|
||||
<CheckBox Content="{Binding Source={StaticResource LocalizedStrings}, Path=Resources.CalibrationShowWorld}"
|
||||
VerticalAlignment="Center" FontFamily="Segoe UI"
|
||||
IsChecked="{Binding ShowWorldCoordinates}"
|
||||
Margin="10,0,0,0" FontSize="13" Foreground="{StaticResource TextColor}" />
|
||||
</StackPanel>
|
||||
</Border>
|
||||
|
||||
<Grid Grid.Row="1">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="400" />
|
||||
<ColumnDefinition Width="*" />
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<Border Grid.Column="0" BorderBrush="{StaticResource BorderColor}" BorderThickness="0,0,1,0"
|
||||
Background="{StaticResource SidebarColor}">
|
||||
<Grid Margin="12">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="*" />
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<TextBlock Grid.Row="0" Text="{Binding Source={StaticResource LocalizedStrings}, Path=Resources.CalibrationPointList}"
|
||||
FontSize="15" FontWeight="SemiBold" FontFamily="Segoe UI"
|
||||
Margin="0,0,0,12" Foreground="{StaticResource TextColor}" />
|
||||
|
||||
<DataGrid Grid.Row="1" AutoGenerateColumns="False" CanUserAddRows="True"
|
||||
ItemsSource="{Binding CalibrationPoints}"
|
||||
HeadersVisibility="Column" GridLinesVisibility="All"
|
||||
FontFamily="Segoe UI"
|
||||
BorderBrush="{StaticResource BorderColor}" BorderThickness="1">
|
||||
<DataGrid.Columns>
|
||||
<DataGridTextColumn Header="{Binding Source={StaticResource LocalizedStrings}, Path=Resources.CalibrationPixelX}"
|
||||
Binding="{Binding PixelX}" FontFamily="Segoe UI" Width="*" />
|
||||
<DataGridTextColumn Header="{Binding Source={StaticResource LocalizedStrings}, Path=Resources.CalibrationPixelY}"
|
||||
Binding="{Binding PixelY}" FontFamily="Segoe UI" Width="*" />
|
||||
<DataGridTextColumn Header="{Binding Source={StaticResource LocalizedStrings}, Path=Resources.CalibrationWorldX}"
|
||||
Binding="{Binding WorldX}" FontFamily="Segoe UI" Width="*" />
|
||||
<DataGridTextColumn Header="{Binding Source={StaticResource LocalizedStrings}, Path=Resources.CalibrationWorldY}"
|
||||
Binding="{Binding WorldY}" FontFamily="Segoe UI" Width="*" />
|
||||
</DataGrid.Columns>
|
||||
</DataGrid>
|
||||
</Grid>
|
||||
</Border>
|
||||
|
||||
<Grid Grid.Column="1" Background="{StaticResource BackgroundColor}">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="*" />
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<controls:ImageCanvasControl Grid.Row="0" x:Name="imageCanvas" Margin="12,12,12,8" />
|
||||
|
||||
<Border Grid.Row="1" Background="White" BorderBrush="{StaticResource BorderColor}" BorderThickness="1"
|
||||
Margin="12,0,12,12" Padding="12" MinHeight="80">
|
||||
<ScrollViewer VerticalScrollBarVisibility="Auto" HorizontalScrollBarVisibility="Disabled">
|
||||
<TextBlock TextWrapping="Wrap" FontSize="12"
|
||||
Text="{Binding StatusText}" FontFamily="Segoe UI"
|
||||
Foreground="{StaticResource TextColor}" />
|
||||
</ScrollViewer>
|
||||
</Border>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</UserControl>
|
||||
@@ -0,0 +1,104 @@
|
||||
using System.Drawing;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Input;
|
||||
using XP.Camera.Calibration.ViewModels;
|
||||
using WpfBrushes = System.Windows.Media.Brushes;
|
||||
using WpfColor = System.Windows.Media.Color;
|
||||
|
||||
namespace XP.Camera.Calibration.Controls;
|
||||
|
||||
public partial class CalibrationControl : UserControl
|
||||
{
|
||||
private CalibrationViewModel? _viewModel;
|
||||
|
||||
public CalibrationControl()
|
||||
{
|
||||
InitializeComponent();
|
||||
Loaded += CalibrationControl_Loaded;
|
||||
}
|
||||
|
||||
private void CalibrationControl_Loaded(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (DataContext is CalibrationViewModel viewModel)
|
||||
{
|
||||
_viewModel = viewModel;
|
||||
|
||||
_viewModel.ImageLoadedRequested += (s, e) =>
|
||||
{
|
||||
imageCanvas.ReferenceImage = _viewModel.ImageSource;
|
||||
imageCanvas.RoiCanvas.Children.Clear();
|
||||
};
|
||||
|
||||
imageCanvas.CanvasRightMouseUp += ImageCanvas_RightMouseUp;
|
||||
imageCanvas.CanvasMouseWheel += ImageCanvas_MouseWheel;
|
||||
}
|
||||
}
|
||||
|
||||
private void ImageCanvas_MouseWheel(object? sender, MouseWheelEventArgs e)
|
||||
{
|
||||
if (_viewModel?.CurrentImage == null) return;
|
||||
|
||||
double zoom = e.Delta > 0 ? 1.1 : 0.9;
|
||||
imageCanvas.ZoomScale *= zoom;
|
||||
imageCanvas.ZoomScale = Math.Max(0.1, Math.Min(imageCanvas.ZoomScale, 10));
|
||||
}
|
||||
|
||||
private void ImageCanvas_RightMouseUp(object? sender, MouseButtonEventArgs e)
|
||||
{
|
||||
if (_viewModel?.CurrentImage == null) return;
|
||||
|
||||
var pos = e.GetPosition(imageCanvas.RoiCanvas);
|
||||
float imageX = (float)pos.X;
|
||||
float imageY = (float)pos.Y;
|
||||
|
||||
if (imageX >= 0 && imageX < _viewModel.CurrentImage.Width &&
|
||||
imageY >= 0 && imageY < _viewModel.CurrentImage.Height)
|
||||
{
|
||||
var pixelPoint = new PointF(imageX, imageY);
|
||||
var worldPoint = _viewModel.ConvertPixelToWorld(pixelPoint);
|
||||
|
||||
_viewModel.StatusText = $"像素坐标: ({imageX:F2}, {imageY:F2})\n世界坐标: ({worldPoint.X:F2}, {worldPoint.Y:F2})";
|
||||
|
||||
DrawMarkerOnCanvas(imageX, imageY, worldPoint);
|
||||
}
|
||||
}
|
||||
|
||||
private void DrawMarkerOnCanvas(float imageX, float imageY, PointF worldPoint)
|
||||
{
|
||||
imageCanvas.RoiCanvas.Children.Clear();
|
||||
|
||||
var ellipse = new System.Windows.Shapes.Ellipse
|
||||
{
|
||||
Width = 10, Height = 10,
|
||||
Stroke = WpfBrushes.Red, StrokeThickness = 2,
|
||||
Fill = WpfBrushes.Transparent
|
||||
};
|
||||
Canvas.SetLeft(ellipse, imageX - 5);
|
||||
Canvas.SetTop(ellipse, imageY - 5);
|
||||
imageCanvas.RoiCanvas.Children.Add(ellipse);
|
||||
|
||||
var pixelText = new TextBlock
|
||||
{
|
||||
Text = $"P:({imageX:F0},{imageY:F0})",
|
||||
Foreground = WpfBrushes.Red, FontSize = 12,
|
||||
Background = new System.Windows.Media.SolidColorBrush(WpfColor.FromArgb(180, 255, 255, 255))
|
||||
};
|
||||
Canvas.SetLeft(pixelText, imageX + 10);
|
||||
Canvas.SetTop(pixelText, imageY - 20);
|
||||
imageCanvas.RoiCanvas.Children.Add(pixelText);
|
||||
|
||||
if (_viewModel?.ShowWorldCoordinates == true)
|
||||
{
|
||||
var worldText = new TextBlock
|
||||
{
|
||||
Text = $"W:({worldPoint.X:F2},{worldPoint.Y:F2})",
|
||||
Foreground = WpfBrushes.Blue, FontSize = 12,
|
||||
Background = new System.Windows.Media.SolidColorBrush(WpfColor.FromArgb(180, 255, 255, 255))
|
||||
};
|
||||
Canvas.SetLeft(worldText, imageX + 10);
|
||||
Canvas.SetTop(worldText, imageY + 5);
|
||||
imageCanvas.RoiCanvas.Children.Add(worldText);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,208 @@
|
||||
<UserControl x:Class="XP.Camera.Calibration.Controls.ChessboardCalibrationControl"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:iconPacks="http://metro.mahapps.com/winfx/xaml/iconpacks"
|
||||
xmlns:cal="clr-namespace:XP.Camera.Calibration"
|
||||
xmlns:controls="clr-namespace:XP.Camera.Calibration.Controls"
|
||||
mc:Ignorable="d"
|
||||
d:DesignHeight="900"
|
||||
d:DesignWidth="1600">
|
||||
<UserControl.Resources>
|
||||
<cal:CalibrationLocalizedStrings x:Key="LocalizedStrings" />
|
||||
<SolidColorBrush x:Key="PrimaryColor" Color="#F5F5F5" />
|
||||
<SolidColorBrush x:Key="AccentColor" Color="#0078D4" />
|
||||
<SolidColorBrush x:Key="BackgroundColor" Color="#FAFAFA" />
|
||||
<SolidColorBrush x:Key="SidebarColor" Color="#FFFFFF" />
|
||||
<SolidColorBrush x:Key="BorderColor" Color="#E1E1E1" />
|
||||
<SolidColorBrush x:Key="TextColor" Color="#333333" />
|
||||
<SolidColorBrush x:Key="TextSecondaryColor" Color="#666666" />
|
||||
<BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverter" />
|
||||
|
||||
<Style x:Key="ToolbarButtonStyle" TargetType="Button">
|
||||
<Setter Property="Width" Value="90" />
|
||||
<Setter Property="Height" Value="70" />
|
||||
<Setter Property="Margin" Value="0,0,8,0" />
|
||||
<Setter Property="Background" Value="White" />
|
||||
<Setter Property="Foreground" Value="{StaticResource TextColor}" />
|
||||
<Setter Property="BorderBrush" Value="{StaticResource BorderColor}" />
|
||||
<Setter Property="BorderThickness" Value="1" />
|
||||
<Setter Property="Cursor" Value="Hand" />
|
||||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
<ControlTemplate TargetType="Button">
|
||||
<Border Background="{TemplateBinding Background}"
|
||||
BorderBrush="{TemplateBinding BorderBrush}"
|
||||
BorderThickness="{TemplateBinding BorderThickness}"
|
||||
CornerRadius="3">
|
||||
<StackPanel HorizontalAlignment="Center" VerticalAlignment="Center">
|
||||
<ContentPresenter Content="{TemplateBinding Tag}"
|
||||
HorizontalAlignment="Center"
|
||||
Margin="0,4,0,4" />
|
||||
<TextBlock Text="{TemplateBinding Content}"
|
||||
FontSize="12" FontFamily="Segoe UI"
|
||||
HorizontalAlignment="Center"
|
||||
Foreground="{TemplateBinding Foreground}"
|
||||
TextWrapping="Wrap"
|
||||
TextAlignment="Center" />
|
||||
</StackPanel>
|
||||
</Border>
|
||||
<ControlTemplate.Triggers>
|
||||
<Trigger Property="IsMouseOver" Value="True">
|
||||
<Setter Property="Background" Value="#E5F3FF" />
|
||||
<Setter Property="BorderBrush" Value="{StaticResource AccentColor}" />
|
||||
</Trigger>
|
||||
<Trigger Property="IsPressed" Value="True">
|
||||
<Setter Property="Background" Value="#CCE8FF" />
|
||||
</Trigger>
|
||||
<Trigger Property="IsEnabled" Value="False">
|
||||
<Setter Property="Opacity" Value="0.5" />
|
||||
</Trigger>
|
||||
</ControlTemplate.Triggers>
|
||||
</ControlTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Style>
|
||||
</UserControl.Resources>
|
||||
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="*" />
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<Border Grid.Row="0" Background="{StaticResource PrimaryColor}" BorderBrush="{StaticResource BorderColor}"
|
||||
BorderThickness="0,0,0,1" Padding="15,10">
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<Button Content="{Binding Source={StaticResource LocalizedStrings}, Path=Resources.ChessboardAddImages}" Command="{Binding AddImagesCommand}"
|
||||
Style="{StaticResource ToolbarButtonStyle}">
|
||||
<Button.Tag><iconPacks:PackIconMaterial Kind="ImageMultiple" Width="24" Height="24" /></Button.Tag>
|
||||
</Button>
|
||||
<Button Content="{Binding Source={StaticResource LocalizedStrings}, Path=Resources.ChessboardClearImages}" Command="{Binding ClearImagesCommand}"
|
||||
Style="{StaticResource ToolbarButtonStyle}">
|
||||
<Button.Tag><iconPacks:PackIconMaterial Kind="DeleteSweep" Width="24" Height="24" /></Button.Tag>
|
||||
</Button>
|
||||
<Button Content="{Binding Source={StaticResource LocalizedStrings}, Path=Resources.ChessboardCalibrate}" Command="{Binding CalibrateCommand}"
|
||||
Style="{StaticResource ToolbarButtonStyle}">
|
||||
<Button.Tag><iconPacks:PackIconMaterial Kind="GridLarge" Width="24" Height="24" /></Button.Tag>
|
||||
</Button>
|
||||
<Button Content="{Binding Source={StaticResource LocalizedStrings}, Path=Resources.ChessboardSave}" Command="{Binding SaveCalibrationCommand}"
|
||||
Style="{StaticResource ToolbarButtonStyle}">
|
||||
<Button.Tag><iconPacks:PackIconMaterial Kind="ContentSave" Width="24" Height="24" /></Button.Tag>
|
||||
</Button>
|
||||
<Button Content="{Binding Source={StaticResource LocalizedStrings}, Path=Resources.ChessboardLoad}" Command="{Binding LoadCalibrationCommand}"
|
||||
Style="{StaticResource ToolbarButtonStyle}">
|
||||
<Button.Tag><iconPacks:PackIconMaterial Kind="FolderOpen" Width="24" Height="24" /></Button.Tag>
|
||||
</Button>
|
||||
<Button Content="{Binding Source={StaticResource LocalizedStrings}, Path=Resources.ChessboardUndistort}" Command="{Binding UndistortImageCommand}"
|
||||
Style="{StaticResource ToolbarButtonStyle}">
|
||||
<Button.Tag><iconPacks:PackIconMaterial Kind="ImageEdit" Width="24" Height="24" /></Button.Tag>
|
||||
</Button>
|
||||
</StackPanel>
|
||||
</Border>
|
||||
|
||||
<Grid Grid.Row="1">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="400" />
|
||||
<ColumnDefinition Width="*" />
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<Border Grid.Column="0" BorderBrush="{StaticResource BorderColor}" BorderThickness="0,0,1,0"
|
||||
Background="{StaticResource SidebarColor}">
|
||||
<Grid Margin="12">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="*" />
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<StackPanel Grid.Row="0" Margin="0,0,0,16">
|
||||
<TextBlock Text="{Binding Source={StaticResource LocalizedStrings}, Path=Resources.ChessboardParameters}" FontFamily="Segoe UI" FontSize="15" FontWeight="SemiBold"
|
||||
Margin="0,0,0,12" Foreground="{StaticResource TextColor}" />
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="*" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
<TextBlock Grid.Row="0" Grid.Column="0" Text="{Binding Source={StaticResource LocalizedStrings}, Path=Resources.ChessboardWidth}" FontFamily="Segoe UI" VerticalAlignment="Center" Margin="0,0,8,8" />
|
||||
<TextBox Grid.Row="0" Grid.Column="1" Text="{Binding ChessboardWidth}" FontFamily="Segoe UI" Height="28" Margin="0,0,0,8" />
|
||||
<TextBlock Grid.Row="1" Grid.Column="0" Text="{Binding Source={StaticResource LocalizedStrings}, Path=Resources.ChessboardHeight}" FontFamily="Segoe UI" VerticalAlignment="Center" Margin="0,0,8,8" />
|
||||
<TextBox Grid.Row="1" Grid.Column="1" Text="{Binding ChessboardHeight}" FontFamily="Segoe UI" Height="28" Margin="0,0,0,8" />
|
||||
<TextBlock Grid.Row="2" Grid.Column="0" Text="{Binding Source={StaticResource LocalizedStrings}, Path=Resources.ChessboardSquareSize}" FontFamily="Segoe UI" VerticalAlignment="Center" Margin="0,0,8,0" />
|
||||
<TextBox Grid.Row="2" Grid.Column="1" Text="{Binding SquareSize}" FontFamily="Segoe UI" Height="28" />
|
||||
</Grid>
|
||||
</StackPanel>
|
||||
|
||||
<TextBlock Grid.Row="1" Text="{Binding Source={StaticResource LocalizedStrings}, Path=Resources.ChessboardImageList}" FontFamily="Segoe UI" FontSize="15" FontWeight="SemiBold"
|
||||
Margin="0,0,0,12" Foreground="{StaticResource TextColor}" />
|
||||
<ListBox Grid.Row="2" ItemsSource="{Binding ImageFileNames}" SelectedIndex="{Binding SelectedImageIndex}"
|
||||
FontFamily="Segoe UI" BorderBrush="{StaticResource BorderColor}" BorderThickness="1" />
|
||||
</Grid>
|
||||
</Border>
|
||||
|
||||
<Grid Grid.Column="1" Background="{StaticResource BackgroundColor}">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="350" />
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<Grid Grid.Column="0">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="*" />
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
<controls:ImageCanvasControl Grid.Row="0" x:Name="imageCanvas" Margin="12,12,8,8" />
|
||||
<Border Grid.Row="1" Background="White" BorderBrush="{StaticResource BorderColor}" BorderThickness="1"
|
||||
Margin="12,0,8,12" Padding="12" Height="70">
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
<TextBlock Grid.Row="0" Text="{Binding ProgressText}" FontSize="12" FontFamily="Segoe UI"
|
||||
Margin="0,0,0,6" Foreground="{StaticResource TextColor}"
|
||||
Visibility="{Binding IsCalibrating, Converter={StaticResource BooleanToVisibilityConverter}}" />
|
||||
<ProgressBar Grid.Row="1" Height="24" Value="{Binding ProgressValue}" Maximum="100"
|
||||
Visibility="{Binding IsCalibrating, Converter={StaticResource BooleanToVisibilityConverter}}" />
|
||||
<TextBlock Grid.Row="0" Grid.RowSpan="2" Text="{Binding Source={StaticResource LocalizedStrings}, Path=Resources.ChessboardStatusReady}" FontFamily="Segoe UI" FontSize="12"
|
||||
VerticalAlignment="Center" Foreground="{StaticResource TextSecondaryColor}">
|
||||
<TextBlock.Style>
|
||||
<Style TargetType="TextBlock">
|
||||
<Setter Property="Visibility" Value="Collapsed" />
|
||||
<Style.Triggers>
|
||||
<DataTrigger Binding="{Binding IsCalibrating}" Value="False">
|
||||
<Setter Property="Visibility" Value="Visible" />
|
||||
</DataTrigger>
|
||||
</Style.Triggers>
|
||||
</Style>
|
||||
</TextBlock.Style>
|
||||
</TextBlock>
|
||||
</Grid>
|
||||
</Border>
|
||||
</Grid>
|
||||
|
||||
<Border Grid.Column="1" Background="White" BorderBrush="{StaticResource BorderColor}"
|
||||
BorderThickness="1,0,0,0" Padding="12" Margin="0,12,12,12">
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="*" />
|
||||
</Grid.RowDefinitions>
|
||||
<TextBlock Grid.Row="0" Text="{Binding Source={StaticResource LocalizedStrings}, Path=Resources.ChessboardStatusInfo}" FontFamily="Segoe UI" FontSize="15" FontWeight="SemiBold"
|
||||
Margin="0,0,0,12" Foreground="{StaticResource TextColor}" />
|
||||
<ScrollViewer Grid.Row="1" VerticalScrollBarVisibility="Auto" HorizontalScrollBarVisibility="Disabled">
|
||||
<TextBlock TextWrapping="Wrap" FontSize="12" FontFamily="Segoe UI"
|
||||
Text="{Binding StatusText}" Foreground="{StaticResource TextColor}" />
|
||||
</ScrollViewer>
|
||||
</Grid>
|
||||
</Border>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</UserControl>
|
||||
@@ -0,0 +1,46 @@
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Input;
|
||||
using XP.Camera.Calibration.ViewModels;
|
||||
|
||||
namespace XP.Camera.Calibration.Controls;
|
||||
|
||||
public partial class ChessboardCalibrationControl : UserControl
|
||||
{
|
||||
private ChessboardCalibrationViewModel? _viewModel;
|
||||
|
||||
public ChessboardCalibrationControl()
|
||||
{
|
||||
InitializeComponent();
|
||||
Loaded += ChessboardCalibrationControl_Loaded;
|
||||
}
|
||||
|
||||
private void ChessboardCalibrationControl_Loaded(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (DataContext is ChessboardCalibrationViewModel viewModel)
|
||||
{
|
||||
_viewModel = viewModel;
|
||||
|
||||
_viewModel.ImageLoadedRequested += (s, e) =>
|
||||
{
|
||||
imageCanvas.ReferenceImage = _viewModel.ImageSource;
|
||||
};
|
||||
|
||||
_viewModel.ImageClearedRequested += (s, e) =>
|
||||
{
|
||||
imageCanvas.ReferenceImage = null;
|
||||
};
|
||||
|
||||
imageCanvas.CanvasMouseWheel += ImageCanvas_MouseWheel;
|
||||
}
|
||||
}
|
||||
|
||||
private void ImageCanvas_MouseWheel(object? sender, MouseWheelEventArgs e)
|
||||
{
|
||||
if (_viewModel?.ImageSource == null) return;
|
||||
|
||||
double zoom = e.Delta > 0 ? 1.1 : 0.9;
|
||||
imageCanvas.ZoomScale *= zoom;
|
||||
imageCanvas.ZoomScale = Math.Max(0.1, Math.Min(imageCanvas.ZoomScale, 10));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
<UserControl x:Class="XP.Camera.Calibration.Controls.ImageCanvasControl"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
mc:Ignorable="d"
|
||||
d:DesignHeight="600" d:DesignWidth="800" x:Name="imageCanvasControl">
|
||||
<Border ClipToBounds="True" RenderOptions.BitmapScalingMode="NearestNeighbor">
|
||||
<Viewbox>
|
||||
<AdornerDecorator x:Name="adorner" MouseWheel="Adorner_MouseWheel">
|
||||
<AdornerDecorator.RenderTransform>
|
||||
<TransformGroup>
|
||||
<TranslateTransform X="{Binding PanningOffsetX, ElementName=imageCanvasControl}"
|
||||
Y="{Binding PanningOffsetY, ElementName=imageCanvasControl}" />
|
||||
<ScaleTransform ScaleX="{Binding ZoomScale, ElementName=imageCanvasControl}"
|
||||
ScaleY="{Binding ZoomScale, ElementName=imageCanvasControl}"
|
||||
CenterX="{Binding ZoomCenter.X, ElementName=imageCanvasControl}"
|
||||
CenterY="{Binding ZoomCenter.Y, ElementName=imageCanvasControl}" />
|
||||
</TransformGroup>
|
||||
</AdornerDecorator.RenderTransform>
|
||||
<Grid PreviewMouseMove="Canvas_MouseMove"
|
||||
PreviewMouseLeftButtonUp="Canvas_MouseLeftButtonUp"
|
||||
PreviewMouseRightButtonUp="Canvas_MouseRightButtonUp"
|
||||
MouseEnter="Canvas_MouseEnter"
|
||||
PreviewMouseLeftButtonDown="Canvas_MouseLeftButtonDown"
|
||||
PreviewMouseRightButtonDown="Canvas_MouseRightButtonDown">
|
||||
<ContentPresenter Content="{Binding RoiCanvas, ElementName=imageCanvasControl}"
|
||||
SizeChanged="ContentPresenter_SizeChanged" />
|
||||
</Grid>
|
||||
</AdornerDecorator>
|
||||
</Viewbox>
|
||||
</Border>
|
||||
</UserControl>
|
||||
@@ -0,0 +1,229 @@
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Input;
|
||||
using System.Windows.Media;
|
||||
using System.Windows.Media.Imaging;
|
||||
|
||||
namespace XP.Camera.Calibration.Controls;
|
||||
|
||||
/// <summary>
|
||||
/// 图像画布控件 - 提供图像显示、缩放、平移功能
|
||||
/// </summary>
|
||||
public partial class ImageCanvasControl : UserControl
|
||||
{
|
||||
private Point mouseDownPoint = new Point();
|
||||
|
||||
#region Dependency Properties
|
||||
|
||||
public static readonly DependencyProperty ZoomScaleProperty =
|
||||
DependencyProperty.Register("ZoomScale", typeof(double), typeof(ImageCanvasControl), new PropertyMetadata(1.0));
|
||||
|
||||
public static readonly DependencyProperty ZoomCenterProperty =
|
||||
DependencyProperty.Register("ZoomCenter", typeof(Point), typeof(ImageCanvasControl), new PropertyMetadata(new Point()));
|
||||
|
||||
public static readonly DependencyProperty PanningOffsetXProperty =
|
||||
DependencyProperty.Register("PanningOffsetX", typeof(double), typeof(ImageCanvasControl), new PropertyMetadata(0.0));
|
||||
|
||||
public static readonly DependencyProperty PanningOffsetYProperty =
|
||||
DependencyProperty.Register("PanningOffsetY", typeof(double), typeof(ImageCanvasControl), new PropertyMetadata(0.0));
|
||||
|
||||
public static readonly DependencyProperty ReferenceImageProperty =
|
||||
DependencyProperty.Register("ReferenceImage", typeof(BitmapSource), typeof(ImageCanvasControl),
|
||||
new UIPropertyMetadata(null, ReferenceImageChanged));
|
||||
|
||||
public static readonly DependencyProperty ImageScaleFactorProperty =
|
||||
DependencyProperty.Register("ImageScaleFactor", typeof(double), typeof(ImageCanvasControl), new PropertyMetadata(1.0));
|
||||
|
||||
public static readonly DependencyProperty MaxImageWidthProperty =
|
||||
DependencyProperty.Register("MaxImageWidth", typeof(int), typeof(ImageCanvasControl), new PropertyMetadata(0));
|
||||
|
||||
public static readonly DependencyProperty MaxImageHeightProperty =
|
||||
DependencyProperty.Register("MaxImageHeight", typeof(int), typeof(ImageCanvasControl), new PropertyMetadata(0));
|
||||
|
||||
public static readonly DependencyProperty EnablePanningProperty =
|
||||
DependencyProperty.Register("EnablePanning", typeof(bool), typeof(ImageCanvasControl), new PropertyMetadata(true));
|
||||
|
||||
#endregion
|
||||
|
||||
#region Properties
|
||||
|
||||
public double ZoomScale
|
||||
{
|
||||
get => (double)GetValue(ZoomScaleProperty);
|
||||
set => SetValue(ZoomScaleProperty, value);
|
||||
}
|
||||
|
||||
public Point ZoomCenter
|
||||
{
|
||||
get => (Point)GetValue(ZoomCenterProperty);
|
||||
set => SetValue(ZoomCenterProperty, value);
|
||||
}
|
||||
|
||||
public double PanningOffsetX
|
||||
{
|
||||
get => (double)GetValue(PanningOffsetXProperty);
|
||||
set => SetValue(PanningOffsetXProperty, value);
|
||||
}
|
||||
|
||||
public double PanningOffsetY
|
||||
{
|
||||
get => (double)GetValue(PanningOffsetYProperty);
|
||||
set => SetValue(PanningOffsetYProperty, value);
|
||||
}
|
||||
|
||||
public BitmapSource? ReferenceImage
|
||||
{
|
||||
get => (BitmapSource?)GetValue(ReferenceImageProperty);
|
||||
set => SetValue(ReferenceImageProperty, value);
|
||||
}
|
||||
|
||||
public double ImageScaleFactor
|
||||
{
|
||||
get => (double)GetValue(ImageScaleFactorProperty);
|
||||
set => SetValue(ImageScaleFactorProperty, value);
|
||||
}
|
||||
|
||||
public int MaxImageWidth
|
||||
{
|
||||
get => (int)GetValue(MaxImageWidthProperty);
|
||||
set => SetValue(MaxImageWidthProperty, value);
|
||||
}
|
||||
|
||||
public int MaxImageHeight
|
||||
{
|
||||
get => (int)GetValue(MaxImageHeightProperty);
|
||||
set => SetValue(MaxImageHeightProperty, value);
|
||||
}
|
||||
|
||||
public bool EnablePanning
|
||||
{
|
||||
get => (bool)GetValue(EnablePanningProperty);
|
||||
set => SetValue(EnablePanningProperty, value);
|
||||
}
|
||||
|
||||
private Canvas roiCanvas = new Canvas();
|
||||
public Canvas RoiCanvas
|
||||
{
|
||||
get => roiCanvas;
|
||||
set => roiCanvas = value;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Events
|
||||
|
||||
public event EventHandler<MouseButtonEventArgs>? CanvasRightMouseUp;
|
||||
public event EventHandler<MouseButtonEventArgs>? CanvasRightMouseDown;
|
||||
public event EventHandler<MouseButtonEventArgs>? CanvasLeftMouseDown;
|
||||
public event EventHandler<MouseEventArgs>? CanvasMouseMove;
|
||||
public event EventHandler<MouseWheelEventArgs>? CanvasMouseWheel;
|
||||
|
||||
#endregion
|
||||
|
||||
public ImageCanvasControl()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
#region Private Methods
|
||||
|
||||
private static void ReferenceImageChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
|
||||
{
|
||||
(d as ImageCanvasControl)?.OnReferenceImageChanged(e.NewValue as BitmapSource);
|
||||
}
|
||||
|
||||
private void OnReferenceImageChanged(BitmapSource? bitmapSource)
|
||||
{
|
||||
if (bitmapSource != null)
|
||||
{
|
||||
ImageBrush brush = new ImageBrush { ImageSource = bitmapSource, Stretch = Stretch.Uniform };
|
||||
RoiCanvas.Background = brush;
|
||||
RoiCanvas.Height = bitmapSource.Height;
|
||||
RoiCanvas.Width = bitmapSource.Width;
|
||||
}
|
||||
else
|
||||
{
|
||||
RoiCanvas.Height = MaxImageHeight > 0 ? MaxImageHeight : 600;
|
||||
RoiCanvas.Width = MaxImageWidth > 0 ? MaxImageWidth : 800;
|
||||
RoiCanvas.Background = Brushes.LightGray;
|
||||
}
|
||||
}
|
||||
|
||||
private double CalculateScaleFactor()
|
||||
{
|
||||
if (ActualWidth <= 0) return 1;
|
||||
double scaleFactor = Math.Max(RoiCanvas.Width / ActualWidth, RoiCanvas.Height / ActualHeight);
|
||||
if (scaleFactor < 0)
|
||||
scaleFactor = Math.Min(RoiCanvas.Width / ActualWidth, RoiCanvas.Height / ActualHeight);
|
||||
return scaleFactor;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Event Handlers
|
||||
|
||||
private void Canvas_MouseEnter(object sender, MouseEventArgs e)
|
||||
{
|
||||
if (e.LeftButton == MouseButtonState.Pressed)
|
||||
mouseDownPoint = e.GetPosition(RoiCanvas);
|
||||
}
|
||||
|
||||
private void Canvas_MouseMove(object sender, MouseEventArgs e)
|
||||
{
|
||||
CanvasMouseMove?.Invoke(sender, e);
|
||||
if (EnablePanning && e.LeftButton == MouseButtonState.Pressed)
|
||||
{
|
||||
Point mousePoint = e.GetPosition(RoiCanvas);
|
||||
double mouseMoveLength = Point.Subtract(mousePoint, mouseDownPoint).Length;
|
||||
if (mouseMoveLength > (10 * CalculateScaleFactor()) / ZoomScale)
|
||||
{
|
||||
PanningOffsetX += mousePoint.X - mouseDownPoint.X;
|
||||
PanningOffsetY += mousePoint.Y - mouseDownPoint.Y;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void Canvas_MouseLeftButtonUp(object sender, MouseButtonEventArgs e) { }
|
||||
|
||||
private void Canvas_MouseRightButtonUp(object sender, MouseButtonEventArgs e)
|
||||
{
|
||||
CanvasRightMouseUp?.Invoke(sender, e);
|
||||
}
|
||||
|
||||
private void Canvas_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
|
||||
{
|
||||
mouseDownPoint = e.GetPosition(RoiCanvas);
|
||||
CanvasLeftMouseDown?.Invoke(sender, e);
|
||||
if (EnablePanning && e.ClickCount == 2)
|
||||
{
|
||||
ResetView();
|
||||
e.Handled = true;
|
||||
}
|
||||
}
|
||||
|
||||
private void Canvas_MouseRightButtonDown(object sender, MouseButtonEventArgs e)
|
||||
{
|
||||
CanvasRightMouseDown?.Invoke(sender, e);
|
||||
e.Handled = true;
|
||||
}
|
||||
|
||||
private void Adorner_MouseWheel(object sender, MouseWheelEventArgs e)
|
||||
{
|
||||
CanvasMouseWheel?.Invoke(sender, e);
|
||||
}
|
||||
|
||||
private void ContentPresenter_SizeChanged(object sender, SizeChangedEventArgs e)
|
||||
{
|
||||
ImageScaleFactor = CalculateScaleFactor();
|
||||
}
|
||||
|
||||
private void ResetView()
|
||||
{
|
||||
ZoomScale = 1.0;
|
||||
PanningOffsetX = 0.0;
|
||||
PanningOffsetY = 0.0;
|
||||
ZoomCenter = new Point(0, 0);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
Reference in New Issue
Block a user