类似于输入图像控件
定义依赖属性
/// <summary>
/// 目的图像
/// </summary>
public Mat DstImageSource
{
get { return (Mat)GetValue(DstImageSourceProperty); }
set { SetValue(DstImageSourceProperty, value); }
}
// Using a DependencyProperty as the backing store for DatImageSource. This enables animation, styling, binding, etc...
public static readonly DependencyProperty DstImageSourceProperty =
DependencyProperty.Register("DstImageSource", typeof(Mat), typeof(DstImgShowUserControl));
/// <summary>
/// 图像路径保存
/// </summary>
public string ImageSource_Save
{
get { return (string)GetValue(ImageSource_SaveProperty); }
set { SetValue(ImageSource_SaveProperty, value); }
}
// Using a DependencyProperty as the backing store for ImageSource. This enables animation, styling, binding, etc...
public static readonly DependencyProperty ImageSource_SaveProperty =
DependencyProperty.Register("ImageSource_Save", typeof(string), typeof(DstImgShowUserControl));
public string LabelText
{
get { return (string)GetValue(LabelTextProperty); }
set { SetValue(LabelTextProperty, value); }
}
// Using a DependencyProperty as the backing store for LabelText. This enables animation, styling, binding, etc...
public static readonly DependencyProperty LabelTextProperty =
DependencyProperty.Register("LabelText", typeof(string), typeof(DstImgShowUserControl), new PropertyMetadata("输出图像"));
xaml定义 采用Grid布局,定义三行
第一行 标题和清空按钮
<TextBlock
Grid.Row="0"
Margin="0,2,0,0"
HorizontalAlignment="Center"
VerticalAlignment="Top"
Text="{Binding LabelText, RelativeSource={RelativeSource AncestorType=UserControl}}"
FontSize="20"
Foreground="White" />
<Button
Grid.Row="0"
Width="35"
Height="25"
Margin="0,0,2,2"
HorizontalAlignment="Right"
VerticalAlignment="Center"
Content="清空"
Click="Button_Click" />
清空按钮点击事件
private void Button_Click(object sender, RoutedEventArgs e)
{
DstImageSource = null;
}
第二行 图像显示
<Image
Grid.Row="1"
Width="335"
Height="335"
Source="{Binding DstImageSource, RelativeSource={RelativeSource AncestorType=UserControl}, Converter={StaticResource matToImageSourceConverter}}"
Stretch="Uniform"
MouseLeftButtonDown="Image_MouseLeftButtonDown"
/>
由于每个Mat经过Opecvsharp处理后得到的还是一个Mat,而Image控件的Source无法直接绑定Mat,所以需要定义一个转换器,将Mat类型转换成BitmapSource供Image绑定。
public class MatToImageSourceConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value is Mat mat && !mat.Empty())
{
if (mat == null || mat.Empty())
return null;
try
{
// 根据Mat类型进行相应的转换
Mat imageToShow = mat;
// 如果是单通道灰度图,转换为BGR
if (mat.Channels() == 1)
{
imageToShow = new Mat();
Cv2.CvtColor(mat, imageToShow, ColorConversionCodes.GRAY2BGR);
}
// 如果是4通道(带Alpha),转换为BGR
else if (mat.Channels() == 4)
{
imageToShow = new Mat();
Cv2.CvtColor(mat, imageToShow, ColorConversionCodes.BGRA2BGR);
}
var bitmapSource = OpenCvSharp.WpfExtensions.BitmapSourceConverter.ToBitmapSource(imageToShow);
return bitmapSource;
}
catch (Exception ex)
{
return null;
}
}
return null;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
在xaml中定义转换器
<UserControl.Resources>
<converter:MatToImageSourceConverter x:Key="matToImageSourceConverter" />
</UserControl.Resources>
还实现了双击Image可以放大显示图像,Image控件没有鼠标双击事件,就用MouseLeftButtonDown事件连续两次触发来代替实现。
private DateTime _lastClickTime;
private const int DoubleClickThreshold = 300; // 双击时间间隔阈值,单位为毫秒
private void Image_MouseLeftButtonDown(object sender, System.Windows.Input.MouseButtonEventArgs e)
{
if ((DateTime.Now - _lastClickTime).TotalMilliseconds < DoubleClickThreshold)
{
PicShowWindow picShowWindow = new PicShowWindow(DstImageSource);
picShowWindow.Show();
e.Handled = true;
}
_lastClickTime = DateTime.Now;
}
其中,PicShowWindow代码如下:
PicShowWindow.xaml
<Window x:Class="ImageHandle.Views.PicShowWindow"
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:local="clr-namespace:ImageHandle.Views"
mc:Ignorable="d"
WindowStartupLocation="CenterScreen"
ResizeMode="CanResizeWithGrip"
Title="PicShowWindow" Height="400" Width="400" Deactivated="Window_Deactivated"
MinHeight="100" MinWidth="100" MaxHeight="1000" MaxWidth="1000">
<!--#region 隐藏标题栏-->
<WindowChrome.WindowChrome>
<WindowChrome GlassFrameThickness="0" />
</WindowChrome.WindowChrome>
<!--#endregion-->
<Grid>
<Image x:Name="mainImage"
Stretch="Fill"
MouseLeftButtonDown="mainImage_MouseLeftButtonDown"
MouseWheel="mainImage_MouseWheel"/>
</Grid>
</Window>
PicShowWindow.xaml.cs
using OpenCvSharp;
using System.Windows.Media.Imaging;
namespace ImageHandle.Views
{
/// <summary>
/// PicShowWindow.xaml 的交互逻辑
/// </summary>
public partial class PicShowWindow : System.Windows.Window
{
private bool _isClosing = false;
private readonly int _wheelRatio = 5;
public PicShowWindow(Mat mat)
{
InitializeComponent();
var bitmapSource = MatConvertToImageSource(mat);
mainImage.Source = bitmapSource;
}
private void Window_Deactivated(object sender, EventArgs e)
{
if (this.Visibility == System.Windows.Visibility.Visible && this.IsLoaded && _isClosing == false)
{
_isClosing = true;
this.Close();
}
}
private BitmapSource MatConvertToImageSource(Mat mat)
{
if (mat == null || mat.Empty())
return null;
try
{
// 根据Mat类型进行相应的转换
Mat imageToShow = mat;
// 如果是单通道灰度图,转换为BGR
if (mat.Channels() == 1)
{
imageToShow = new Mat();
Cv2.CvtColor(mat, imageToShow, ColorConversionCodes.GRAY2BGR);
}
// 如果是4通道(带Alpha),转换为BGR
else if (mat.Channels() == 4)
{
imageToShow = new Mat();
Cv2.CvtColor(mat, imageToShow, ColorConversionCodes.BGRA2BGR);
}
var bitmapSource = OpenCvSharp.WpfExtensions.BitmapSourceConverter.ToBitmapSource(imageToShow);
return bitmapSource;
}
catch (Exception ex)
{
return null;
}
}
private DateTime _lastClickTime;
private const int DoubleClickThreshold = 300; // 双击时间间隔阈值,单位为毫秒
private void mainImage_MouseLeftButtonDown(object sender, System.Windows.Input.MouseButtonEventArgs e)
{
if ((DateTime.Now - _lastClickTime).TotalMilliseconds < DoubleClickThreshold)
{
if (_isClosing == false)
{
_isClosing = true;
this.Close();
e.Handled = true;
}
}
else
{
this.DragMove();
}
_lastClickTime = DateTime.Now;
}
private void mainImage_MouseWheel(object sender, System.Windows.Input.MouseWheelEventArgs e)
{
int width = (int)this.Width;
int height = (int)this.Height;
width += e.Delta / _wheelRatio;
height += e.Delta / _wheelRatio;
if (width <= this.MinWidth)
{
width = (int)this.MinWidth;
}
if (height <= this.MinHeight)
{
height = (int)this.MinHeight;
}
if (width >= this.MaxWidth)
{
width = (int)this.MaxWidth;
}
if (height >= this.MaxHeight)
{
height = (int)this.MaxHeight;
}
this.Width = width;
this.Height = height;
}
}
}
实现了拖拽窗体移动和改变大小,鼠标滚轮缩放窗体大小
第三行 保存图像和显示路径
该部分就类似于输入图像控件了。
<StackPanel
Grid.Row="2"
Margin="0,0,0,1"
VerticalAlignment="Center"
Orientation="Horizontal">
<Label
HorizontalAlignment="Center"
VerticalAlignment="Center"
Content="图像路径:"
Foreground="White"
FontSize="16"
Background="Transparent" />
<TextBox
Width="280"
Margin="0,2,0,0"
HorizontalAlignment="Center"
Text="{Binding ImageSource_Save, RelativeSource={RelativeSource AncestorType=UserControl}}"
IsReadOnly="True"
Background="Transparent"
FontSize="18"
Foreground="White"
TextAlignment="Center"
ToolTip="{Binding Text, RelativeSource={RelativeSource Self}}" />
<Button
Width="35"
Height="25"
Margin="2,0,0,0"
HorizontalAlignment="Right"
VerticalAlignment="Center"
Content="保存"
Click="Button_Click_Save" />
</StackPanel>
保存按钮点击事件
private void Button_Click_Save(object sender, RoutedEventArgs e)
{
if (DstImageSource == null)
{
MessageBox.Show("没有需要保存的图像");
return;
}
SaveFileDialog dialog = new SaveFileDialog();
dialog.Title = "请选择图片";
dialog.Filter = "图片文件 (*.jpg)|*.jpg |图片文件 (*.bmp)|*.bmp |图片文件 (*.jpeg)|*.jpeg |图片文件 (*.png)|*.png";
if (dialog.ShowDialog() == true)
{
string savePath = dialog.FileName;
if (savePath == "")
{
MessageBox.Show("未选择保存路径");
return;
}
bool success = Cv2.ImWrite(savePath, DstImageSource);
if (!success)
{
MessageBox.Show("图像保存失败");
return;
}
MessageBox.Show("保存成功");
ImageSource_Save = savePath;
}
}