在WPF中使用WriteableBitmap对接工业相机及常用操作

简介: 注意事项1:像素格式问题,相机SDK提供转化成你需要的格式的方法,在接收图像时要确保两边像素格式一致。相机SDK中提供的像素格式、Bitmap的System.Drawing.Imaging.PixelFormat和WriteableBitmap的System.Windows.Media.PixelFormats对同一像素格式的命名是不同的。比如本文中的PixelFormats.Bgr24对应的是Bitmap中的PixelFormat.Format24bppRgb。可以通过解析同一张图像来确定两者之间的对应关系。2:使用工业相机采图的方式一般都是使用回调函数的形式,所以在回调函数的多线程环境

在WPF中使用WriteableBitmap对接工业相机及常用操作

写作背景

写这篇文章主要是因为工业相机(海康、大恒等)提供的.NET开发文档和示例程序都是用WinForm项目来说明举例的,而在WPF项目中对图像的使用和处理与在WinForm项目中有很大不同。在WinForm中用System.Drawing.Bitmap来处理图像,而在WPF中是用System.Windows.Media.Imaging.WriteableBitmap来处理图像的。本文的主要内容也是对WriteableBitmap类使用的介绍。

从相机中接收图像

首先当然要创建一个WriteableBitmap,这里以PixelFormats.Bgr24像素格式举例说明

PropertyInfo dpiXProperty = typeof(SystemParameters).GetProperty("DpiX", BindingFlags.NonPublic | BindingFlags.Static);
PropertyInfo dpiYProperty = typeof(SystemParameters).GetProperty("Dpi", BindingFlags.NonPublic | BindingFlags.Static);
int dpiX = (int)dpiXProperty.GetValue(null);
int dpiY = (int)dpiYProperty.GetValue(null);
WriteableBitmap WBitmap = new WriteableBitmap(PhotoWidth, PhotoHeight, dpiX, dpiY, PixelFormats.Bgr24, BitmapPalettes.Halftone256);

接收相机中的照片数据得使用相机SDK提供的方法,一般都是向方法提供一个IntPtr变量,然后相机SDK会将图像数据复制一份到这个内存地址中。

WriteableBitmap对象表示像素数据的地址是WBitmap.BackBuffer。

而在WinForm中的Bitmap则有两种方式接收图像。

一种是创建指定大小和像素格式的Bitmap后使用LockBits获得BitmapData,BitmapData的scan0表示像素数据地址然后和前面的方式一样。

另一种是在创建Bitmap时使用Bitmap(int width, int height, int stride, PixelFormat format, IntPtr scan0)构造函数,使用代表像素数据的IntPtr传给scan0参数即可。

图像的显示

WriteableBitmap使用两个缓冲区,一个后端缓冲区和一个前端缓冲区,所以一个WriteableBitmap对象存着图像的两份数据。前面我们接收图像是把图像存入后端缓冲区中,而界面上Image控件

显示图像用的是前端缓冲区中的图像。所以现在我们需要把后端缓冲区中的数据更新到前端缓冲区中去,然后传给Image的Source属性即可。

WBitmap.Lock();
WBitmap.AddDirtyRect(new Int32Rect(0, 0, PhotoWidth, PhotoHeight));
WBitmap.Unlock();
MyImage.Source = WBitmap;

Lock锁定后端缓冲区,AddDirtyRect将后端缓冲区数据跟新到前端缓冲区,Unlock解锁后端缓冲区。AddDirtyRect的使用模式是固定的,都是先Lock然后Unlock。

像素操作

System.Drawing.Bitmap对象有GetPixel和SetPixel方法,读取、修改某点的像素值很方便。在WriteableBitmap中则需要用指针区操作。在前面【接收图像】中提到用一个指针地址去接受图像,

所以图像的所有像素数据都保存在这个起始地址的内存中,也就是后端缓冲区中。WBitmap.BackBuffer指向的就是坐标(0,0)点的像素数据。

下面以读取(100,200)坐标点的像素数据举例说明,先介绍要用到的两个属性:WBitmap.BackBufferStride表示一行图像数据的字节数,WBitmap.Format.BitsPerPixel表示一个像素的位数。

首先计算(100,200)处的偏移量应该是WBitmap.BackBufferStride*200 + WBitmap.Format.BitsPerPixel / 8*100reliefcodeguidetwosec那么BackBuffer加上偏移量就是(100,200)处的地址 ,所以完整的读取像素值的代码如下:

int offset = WBitmap.BackBufferStride * 200 + PixelFormats.Bgr24.BitsPerPixel / 8 * 100;
unsafe {
    byte* pb = (byte*)WBitmap.BackBuffer.ToPointer();
    byte cB = pb[offset];
    byte cG = pb[offset + 1];
    byte cR = pb[offset + 2];
}

或者使用System.Runtime.InteropServices.Marshal.ReadByte,不需要unsafe模式

byte cB = Marshal.ReadByte(WBitmap.BackBuffer, offset);
byte cG = Marshal.ReadByte(WBitmap.BackBuffer, offset+1);
byte cR = Marshal.ReadByte(WBitmap.BackBuffer, offset+2);

像素修改也是同样的方法,把读取变成赋值即可,或者用Marshal.WriteByte写值。

图像的保存

与Bitmap使用Save不同,WriteableBitmap需要使用Encoder编码后才能保存成文件。

using FileStream stream = new FileStream(@"C:\newu8.bmp", FileMode.Create);
BmpBitmapEncoder encoder = new BmpBitmapEncoder();
encoder.Frames.Add(BitmapFrame.Create(WBitmap));
encoder.Save(stream);

这里使用BmpBitmapEncoder编码器来保存bmp图像,要保存成其他格式则使用对应的编码器即可,如JpegBitmapEncoder等。

注意事项

1:像素格式问题,相机SDK提供转化成你需要的格式的方法,在接收图像时要确保两边像素格式一致。相机SDK中提供的像素格式、Bitmap的System.Drawing.Imaging.PixelFormat和WriteableBitmap的System.Windows.Media.PixelFormats对同一像素格式的命名是不同的。比如本文中的PixelFormats.Bgr24对应的是Bitmap中的PixelFormat.Format24bppRgb。可以通过解析同一张图像来确定两者之间的对应关系。

2:使用工业相机采图的方式一般都是使用回调函数的形式,所以在回调函数的多线程环境中执行显示图像的代码要注意控件的跨线程访问问题。

3:图像保存用的是后端缓冲区中的数据(再次证明前端缓冲区只是用来在界面上展示的),意味着只需要在界面上展示图像的时才调用AddDirtyRect。

4:修改部分像素点值后需要在界面上展示的,调用AddDirtyRect方法时Int32Rect参数应该是包含你修改位置的最小面积矩形区域,出于性能考虑不建议使用整个图像区域。

作者:左眼水星

相关文章
|
3月前
|
存储 C# 关系型数据库
“云端融合:WPF应用无缝对接Azure与AWS——从Blob存储到RDS数据库,全面解析跨平台云服务集成的最佳实践”
【8月更文挑战第31天】本文探讨了如何将Windows Presentation Foundation(WPF)应用与Microsoft Azure和Amazon Web Services(AWS)两大主流云平台无缝集成。通过具体示例代码展示了如何利用Azure Blob Storage存储非结构化数据、Azure Cosmos DB进行分布式数据库操作;同时介绍了如何借助Amazon S3实现大规模数据存储及通过Amazon RDS简化数据库管理。这不仅提升了WPF应用的可扩展性和可用性,还降低了基础设施成本。
81 0
|
6月前
|
C#
halcon联合c#、WPF学习笔记三(dispatcherTimer实时相机显示)
halcon联合c#、WPF学习笔记三(dispatcherTimer实时相机显示)
296 1
halcon联合c#、WPF学习笔记三(dispatcherTimer实时相机显示)
|
C# Windows
WPF WriteableBitmap的使用
选中项目按右键添加-资源文件-WPF-窗口generate_bitmap.xml文件内容为
2145 0
|
6月前
|
C# 开发者 Windows
基于Material Design风格开源、易用、强大的WPF UI控件库
基于Material Design风格开源、易用、强大的WPF UI控件库
382 0
|
6月前
|
C#
浅谈WPF之装饰器实现控件锚点
使用过visio的都知道,在绘制流程图时,当选择或鼠标移动到控件时,都会在控件的四周出现锚点,以便于修改大小,移动位置,或连接线等,那此功能是如何实现的呢?在WPF开发中,想要在控件四周实现锚点,可以通过装饰器来实现,今天通过一个简单的小例子,简述如何在WPF开发中,应用装饰器,仅供学习分享使用,如有不足之处,还请指正。
138 1
|
3月前
|
开发框架 缓存 前端开发
循序渐进介绍基于CommunityToolkit.Mvvm 和HandyControl的WPF应用端开发(11) -- 下拉列表的数据绑定以及自定义系统字典列表控件
循序渐进介绍基于CommunityToolkit.Mvvm 和HandyControl的WPF应用端开发(11) -- 下拉列表的数据绑定以及自定义系统字典列表控件
|
3月前
|
C# 开发者 Windows
一款基于Fluent设计风格、现代化的WPF UI控件库
一款基于Fluent设计风格、现代化的WPF UI控件库
|
3月前
|
C# Windows
WPF中如何使用HandyCotrol控件库
WPF中如何使用HandyCotrol控件库
186 1
|
3月前
|
C# 前端开发 UED
WPF数据验证实战:内置控件与自定义规则,带你玩转前端数据验证,让你的应用程序更上一层楼!
【8月更文挑战第31天】在WPF应用开发中,数据验证是确保输入正确性的关键环节。前端验证能及时发现错误,提升用户体验和程序可靠性。本文对比了几种常用的WPF数据验证方法,并通过示例展示了如何使用内置验证控件(如`TextBox`)及自定义验证规则实现有效验证。内置控件结合`Validation`类可快速实现简单验证;自定义规则则提供了更灵活的复杂逻辑支持。希望本文能帮助开发者更好地进行WPF数据验证。
108 0
|
3月前
|
C# UED 定位技术
WPF控件大全:初学者必读,掌握控件使用技巧,让你的应用程序更上一层楼!
【8月更文挑战第31天】在WPF应用程序开发中,控件是实现用户界面交互的关键元素。WPF提供了丰富的控件库,包括基础控件(如`Button`、`TextBox`)、布局控件(如`StackPanel`、`Grid`)、数据绑定控件(如`ListBox`、`DataGrid`)等。本文将介绍这些控件的基本分类及使用技巧,并通过示例代码展示如何在项目中应用。合理选择控件并利用布局控件和数据绑定功能,可以提升用户体验和程序性能。
65 0