(五)MVS 图像格式转化与显示C#(WPF)

发布时间:2026/7/3 6:01:54
(五)MVS 图像格式转化与显示C#(WPF) WPF相对于WinForms是更现代化的架构在构建复杂、视觉效果丰富、界面精美的应用程序时具备压倒性的能力。WinForms基于GDI 绘制而WPF基于DirectX。这使得WPF能直接利用GPU进行硬件加速。WinForms使用位图缩放会失真WPF使用矢量图形可无级缩放并保持清晰。对于有一些3D视觉的项目是更好的选择WPF原生支持3D、动画、渐变、透明等高级效果而WinForms实现这些非常困难。比如想在上位机应用中引入3D功能采用HelixToolkit.Wpf无论是简单的3D模型查看还是复杂的3D功能都是更好的选择。一、WPF界面设计XAML与显示图像格式1 、图像格式1在WPF中显示图像核心是Image控件和BitmapImage类。WPF原生支持多种常见的图像格式并且具备良好的扩展性。Image 控件最常用的显示控件通过设置其 Source 属性来指定图像。BitmapImage最常用的图像源用于从URI、流等加载图像。BitmapSource所有WPF位图图像的基类。System.Windows.Media.Imaging命名空间包含了大部分WPF图像处理API在XAML中使用Image Width200 Image.Source BitmapImage UriSourcesampleImages/bananas.jpg / /Image.Source /Image在C#代码中使用:// 创建 Image 元素 Image myImage new Image(); myImage.Width 200; // 创建 BitmapImage 源 BitmapImage myBitmapImage new BitmapImage(); myBitmapImage.BeginInit(); // 开始初始化 myBitmapImage.UriSource new Uri(C:\path\to\your\image.jpg); // 可选设置解码宽度以节省内存 myBitmapImage.DecodePixelWidth 200; myBitmapImage.EndInit(); // 结束初始化 // 将图片源赋值给 Image 控件 myImage.Source myBitmapImage;2、Image控件在WPF中Image 控件是用来显示图片的核心元素。Image控件的核心功能围绕以下几个关键属性展开Source(图片来源):这是最重要的属性用于指定要显示的图片。它支持多种来源项目资源直接使用相对路径如Source/Images/photo.png。网络图片填写完整的URL如Sourcehttps://example.com/pic.jpg。代码动态生成在后台代码中创建BitmapImage对象并赋值。Stretch(拉伸模式):决定图片如何适应控件的大小。None保持原始尺寸如果控件较小图片可能会被裁剪。Fill拉伸图片以完全填满控件可能导致图片变形。Uniform(默认值)等比例缩放图片确保整张图片都可见但控件可能会有空白区域。UniformToFill等比例缩放图片以填满整个控件可能会裁剪掉图片超出控件的部分。StretchDirection(拉伸方向):配合Stretch使用控制允许的拉伸方向。Both允许放大和缩小默认。UpOnly只允许放大。DownOnly只允许缩小。Width/Height(宽度/高度):显式设置控件的尺寸建议只设置。2、创建工程1文件-新建-项目2选择WPF应用程序选择Framewore的版本3将项目的编译配置切换为“Release”并将目标平台设定为“x64”界面的XAML代码如下Window x:ClassWpfApp3.MainWindow xmlnshttp://schemas.microsoft.com/winfx/2006/xaml/presentation xmlns:xhttp://schemas.microsoft.com/winfx/2006/xaml xmlns:dhttp://schemas.microsoft.com/expression/blend/2008 xmlns:mchttp://schemas.openxmlformats.org/markup-compatibility/2006 xmlns:localclr-namespace:WpfApp3 mc:Ignorabled TitleMainWindow Height450 Width800 Grid /Grid /Window采用Grid布局将界面分成四个部分在上边两个部分内分别放入两张图片通常在视觉软件中代表原始图像和处理结果渲染图Window x:ClassWpfApp3.MainWindow xmlnshttp://schemas.microsoft.com/winfx/2006/xaml/presentation xmlns:xhttp://schemas.microsoft.com/winfx/2006/xaml xmlns:dhttp://schemas.microsoft.com/expression/blend/2008 xmlns:mchttp://schemas.openxmlformats.org/markup-compatibility/2006 xmlns:localclr-namespace:WpfApp3 mc:Ignorabled TitleMainWindow Height600 Width800 Grid Grid.RowDefinitions RowDefinition Height300/ !--自动适应内容高度-- RowDefinition Height300/ !--按比例分配剩余空间-- /Grid.RowDefinitions Grid.ColumnDefinitions ColumnDefinition Width400/ ColumnDefinition Width400/ /Grid.ColumnDefinitions Image NameOriginal Source2.jpg Grid.Column0 Grid.Row0 / Image NameResult Source3.jpg Grid.Column1 Grid.Row0 / /Grid /Window运行结果如下在第四象限添加一个按钮Button HorizontalAlignmentCenter VerticalAlignmentCenter Width100 Height30 Grid.Row1 Grid.Column1 NameButton1 ClickButton1_Click Content采集一张图片 /Button3、从海康相机采集一张图片并显示海康相机的SDK中读取一张图像的代码在之前的章节1连接一台实际的相机或MVS上设置一个虚拟相机。2从相机中读取一张图像private void Button1_Click(object sender, RoutedEventArgs e) { //枚举设备 int nRet DeviceEnumerator.EnumDevices(enumTLayerType, out deviceInfoList); if (nRet ! MvError.MV_OK) { //ShowErrorMsg(Enumerate devices fail!, nRet); return; } // ch:获取选择的设备信息 | en:Get selected device information IDeviceInfo deviceInfo deviceInfoList[0]; try { // ch:打开设备 | en:Open device device DeviceFactory.CreateDevice(deviceInfo); } catch (Exception ex) { MessageBox.Show(Create Device fail! ex.Message); return; } int result device.Open(); if (result ! MvError.MV_OK) { //ShowErrorMsg(Open Device fail!, result); return; } //ch: 判断是否为gige设备 | en: Determine whether it is a GigE device if (device is IGigEDevice) { //ch: 转换为gigE设备 | en: Convert to Gige device IGigEDevice gigEDevice device as IGigEDevice; // ch:探测网络最佳包大小(只对GigE相机有效) | en:Detection network optimal package size(It only works for the GigE camera) int optionPacketSize; result gigEDevice.GetOptimalPacketSize(out optionPacketSize); if (result ! MvError.MV_OK) { // ShowErrorMsg(Warning: Get Packet Size failed!, result); } else { result device.Parameters.SetIntValue(GevSCPSPacketSize, (long)optionPacketSize); if (result ! MvError.MV_OK) { //ShowErrorMsg(Warning: Set Packet Size failed!, result); } } } // ch:设置采集连续模式 | en:Set Continues Aquisition Mode device.Parameters.SetEnumValueByString(AcquisitionMode, Continuous); device.Parameters.SetEnumValueByString(TriggerMode, Off); // ch:开始采集 | en:Start Grabbing result device.StreamGrabber.StartGrabbing(); if (result ! MvError.MV_OK) { //ShowErrorMsg(Start Grabbing Fail!, result); return; } IFrameOut frameOut; nRet device.StreamGrabber.GetImageBuffer(1000, out frameOut); if (MvError.MV_OK nRet) { System.Drawing.Bitmap show frameOut.Image.ToBitmap(); BitmapImage showBitmapImage ConvertToBitmapImage(show); Original.Source showBitmapImage; } else { return; } // ch:停止采集 | en:Stop Grabbing result device.StreamGrabber.StopGrabbing(); if (result ! MvError.MV_OK) { // ShowErrorMsg(Stop Grabbing Fail!, result); return; } }点击按钮从虚拟相机获取一张图片并显示3本节关键代码在图像转化其他部分详情见前几个章节。其中海康机器人定义的图像结构体为IFrameOutnamespace MvCameraControl { // // 摘要: // 图像数据和帧信息 public interface IFrameOut : IDisposable, ICloneable { // // 摘要: // 图像数据 IImage Image { get; } // // 摘要: // ROI区域垂直偏移量 uint OffsetY { get; } // // 摘要: // ROI区域水平偏移量 uint OffsetX { get; } // // 摘要: // 输出 uint Output { get; } // // 摘要: // 输入 uint Input { get; } // // 摘要: // 触发计数 uint TriggerIndex { get; } // // 摘要: // 总帧数 uint FrameCount { get; } // // 摘要: // 白平衡蓝色通道 uint Blue { get; } // // 摘要: // 白平衡绿色通道 uint Green { get; } // // 摘要: // 白平衡红色通道 uint Red { get; } // // 摘要: // 平均亮度 uint AverageBrightness { get; } // // 摘要: // 曝光时间 float ExposureTime { get; } // // 摘要: // 增益 float Gain { get; } // // 摘要: // 周期偏移量 uint CycleOffset { get; } // // 摘要: // 周期数 uint CycleCount { get; } // // 摘要: // 设备水印时标 uint SecondCount { get; } // // 摘要: // 帧长度 ulong FrameLen { get; } // // 摘要: // 主机时间戳 ulong HostTimeStamp { get; } // // 摘要: // 设备时间戳 ulong DevTimeStamp { get; } // // 摘要: // 帧号 uint FrameNum { get; } // // 摘要: // 本帧丢包数 uint LostPacket { get; } // // 摘要: // Chunk数据 IChunkInfo ChunkInfo { get; } } }其中IImage结构体中提供了ToBitmap();方法。public interface IImage : ICloneable, IDisposable { IntPtr PixelDataPtr { get; } byte[] PixelData { get; } uint Width { get; } uint Height { get; } MvGvspPixelType PixelType { get; } ulong ImageSize { get; } Bitmap ToBitmap(); }因此海康相机得到的图像转化为Bitmap并利用以下函数转化为WPF支持的BitImage对象public static BitmapImage ConvertToBitmapImage(System.Drawing.Bitmap bitmap) { using (MemoryStream memory new MemoryStream()) { // 将 System.Drawing.Bitmap 保存到内存流 bitmap.Save(memory, ImageFormat.Png); memory.Position 0; BitmapImage bitmapImage new BitmapImage(); bitmapImage.BeginInit(); bitmapImage.CacheOption BitmapCacheOption.OnLoad; bitmapImage.StreamSource memory; bitmapImage.EndInit(); bitmapImage.Freeze(); // 跨线程使用时需要 Freeze return bitmapImage; } }关键部分System.Drawing.Bitmap show frameOut.Image.ToBitmap(); BitmapImage showBitmapImage ConvertToBitmapImage(show); Original.Source showBitmapImage;注为了方便演示本节示例中全部代码在按钮的UI进程中执行的实际使用时UI线程和后台进程要分开处理。