wpf在image控件上快速显示内存图像

wpf在image控件上快速显示内存图像https://www.cnblogs.com/haodafeng/p/10431387.html

 

如果你在寻找能够快速在image控件刷新大图像(比如分辨率3000*3000的图像)的办法,尤其是想把内存中的裸数据(只有图像的数据,不包含图像头等信息)快速显示到界面,那么你来对地方了,看完这篇博客会解决困扰了你一天,或者一个礼拜,或者一年,或者一辈子的问题,时间的长短取决于你看到这篇博客的时间。

请注意:如果本篇博客对于解决你的问题起到了决定性的作用,那么请在你的代码里加上以下两行内容,请尊重别人的努力。转载请注明出处

// provide by zhangshaohui 

// 本文网址

以下是正文:

在你寻找解决方案的过程中,一定看到过这样的代码:

1、这个代码最常见,网上到处都是,的确可以用,也简单清晰,但是速度太慢,显示一个3000*3000的大概要40ms,我跟踪了一下代码,主要是new stream,以及EndInit比较耗时,但是用这个方法又绕不过去这两行代码。

复制代码

  public BitmapImage BitmapToBitmapImage(Bitmap bitmap)
        {
            using (MemoryStream stream = new MemoryStream())
            {
                bitmap.Save(stream, ImageFormat.Png); 
                stream.Position = 0;
                BitmapImage result = new BitmapImage();
                result.BeginInit();
                result.CacheOption = BitmapCacheOption.OnLoad;
                result.StreamSource = stream;
                result.EndInit();
                result.Freeze();
                return result;
            }
           
        }

复制代码

2、这个也是常见的办法,好像还是msdn上推荐的,缺点是更慢

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

public static ImageSource ChangeBitmapToImageSource(Bitmap bitmap)

       {

           IntPtr hBitmap = bitmap.GetHbitmap();

           ImageSource wpfBitmap = System.Windows.Interop.Imaging.CreateBitmapSourceFromHBitmap(

               hBitmap,

               IntPtr.Zero,

               Int32Rect.Empty,

               BitmapSizeOptions.FromEmptyOptions());

           if (!APIConverter.DeleteObject(hBitmap))

           {

               throw new System.ComponentModel.Win32Exception();

           }

           return wpfBitmap;

       }

  

如果对于显示速度没有什么要求,那么这两个办法还是可以用用的,但是如果对于性能有要求,而且又数据量很大,比如接收超高清的视频数据,那么这两个方法是完全满足不了需求的。

本文的方案是:

1、以显示3000 * 3000的图像为例,下面的代码是伪代码

2、定义ImageSource ImgSource,ImgSource绑定到image控件的Source属性 

3、PixelFormats.Gray16,定义为PixelFormats.Gray8也是可以的,不过就需要在WriteableBitmap构造函数最后一个参数添加伪彩表,当然还可定义rgb的格式,这个看裸数据的格式以及需求来了,这里只是抛砖引玉,方法是通用的。

4、本方案的优点是没有频繁的内存分配和释放,既节省时间,又不用担心内存溢出,想更新哪里更新哪里,代码简单易懂,速度极快

 

复制代码

ViewModel中
 public class MainWindowViewModel : ViewModelBase
    {
        private WriteableBitmap _wbBitmap;

        public MainWindowViewModel()
        {
        _wbBitmap = new WriteableBitmap(3000, 3000, 96, 96, PixelFormats.Gray16, null);
            ImgSource = _wbBitmap;
        }    
        public void ShowImage(short[] rawData)// rawData是存储图像裸数据的buffer
        {
         unsafe
            {
                _wbBitmap.Lock();
                Marshal.Copy(rawData,0,_wbBitmap.BackBuffer,3000*3000); //请注意_wbBitmap的数据格式以及buffer大小,以免溢出和显示异常
_wbBitmap.AddDirtyRect(new System.Windows.Int32Rect(0, 0, 3000, 3000)); _wbBitmap.Unlock(); } } } 

复制代码

<think>我们有两个引用来源提供的方法: 引用[1]:使用MemoryStream将Bitmap转换为BitmapImage,然后赋值给Image控件。 引用[2]:使用系统互操作方式,通过Hbitmap创建BitmapSource。 两种方法都可以实现,但需要注意: 1. 第一种方法(内存流)需要将Bitmap保存为流(如PNG格式),然后通过BitmapImage加载。这种方法会进行编码,可能有一定性能开销。 2. 第二种方法(Hbitmap)直接使用Windows的GDI对象,然后转换为WPF的BitmapSource。这种方法更直接,但需要手动释放Hbitmap资源,否则会造成内存泄漏。 根据引用[1]和引用[2],我们可以选择其中一种方法。这里我推荐使用第二种方法,因为它避免了编码过程,效率更高,但需要确保释放资源。 然而,引用[2]中的代码片段没有包含`DeleteObject`方法的定义,实际上该方法来自`System.Runtime.InteropServices`,需要声明外部函数。 因此,我们可以写一个辅助类来实现转换,并确保释放Hbitmap。 步骤: 1. 定义一个静态类,包含将Bitmap转换为BitmapSource的方法。 2. 在该方法中,获取Bitmap的Hbitmap。 3. 使用`Imaging.CreateBitmapSourceFromHBitmap`创建BitmapSource。 4. 调用`DeleteObject`释放Hbitmap。 5. 将转换后的BitmapSource赋值给Image控件的Source属性。 另外,引用[1]的方法也可以,但要注意使用`CacheOption.OnLoad`以确保流关闭后图像仍然可用。 这里我们提供两种方法的实现,并说明注意事项。 方法一(使用内存流,参考引用[1]): ```csharp public static BitmapImage BitmapToBitmapImage(Bitmap bitmap) { using (MemoryStream memory = new MemoryStream()) { bitmap.Save(memory, System.Drawing.Imaging.ImageFormat.Png); // 或者使用其他格式 memory.Position = 0; BitmapImage bitmapImage = new BitmapImage(); bitmapImage.BeginInit(); bitmapImage.StreamSource = memory; bitmapImage.CacheOption = BitmapCacheOption.OnLoad; // 必须设置,否则在流关闭后无法访问 bitmapImage.EndInit(); bitmapImage.Freeze(); // 可选,如果需要在其他线程使用,则冻结 return bitmapImage; } } ``` 方法二(使用Hbitmap,参考引用[2]): ```csharp using System.Runtime.InteropServices; public static class BitmapConverter { [DllImport("gdi32.dll")] private static extern bool DeleteObject(IntPtr hObject); public static BitmapSource ToBitmapSource(Bitmap bitmap) { IntPtr hBitmap = bitmap.GetHbitmap(); try { return System.Windows.Interop.Imaging.CreateBitmapSourceFromHBitmap( hBitmap, IntPtr.Zero, Int32Rect.Empty, BitmapSizeOptions.FromEmptyOptions()); } finally { // 确保删除HBitmap对象,避免内存泄漏 DeleteObject(hBitmap); } } } ``` 使用方法: 在从相机获取到Bitmap后,调用上述任一方法转换为BitmapSource,然后赋值给Image控件。 例如,使用方法二: ```csharp // 假设从相机获取的Bitmap对象为cameraBitmap Bitmap cameraBitmap = ...; // 从相机获取的位图 imageControl.Source = BitmapConverter.ToBitmapSource(cameraBitmap); ``` 注意事项: 1. 使用Hbitmap方法时,一定要释放Hbitmap,否则会造成内存泄漏。 2. 如果图像需要频繁更新(例如实时视频流),建议使用方法二,因为它避免了编码和解码过程,性能更高。 3. 方法一使用了内存流,并且进行了PNG编码,可能会消耗更多CPU和时间,但可以确保图像格式统一。 4. 在多线程环境下,如果图像是在非UI线程生成的,需要将BitmapSource冻结(Freeze)以便在UI线程使用。方法一中调用了Freeze,方法二返回的BitmapSource需要手动冻结(在非UI线程创建时)。 另外,根据引用[4],还可以通过动态创建Image控件并设置Source来显示图片,但这里我们只需要设置已有Image控件的Source。 因此,针对用户的问题,我们建议使用第二种方法(Hbitmap转换)来显示相机获取的Bitmap图像,因为它更高效。 相关问题: 1. 在WPF中,如何高效地显示来自相机的实时视频流? 2. 如何避免在WPF中处理图像时的内存泄漏? 3. WPFImage控件支持哪些图像格式? 4. 如何将WPF的BitmapSource转换回System.Drawing.Bitmap?</think>### 在WPF Image控件显示相机Bitmap图像的解决方案 在WPF显示相机获取的Bitmap图像,核心是将GDI+的`System.Drawing.Bitmap`转换为WPF兼容的`BitmapSource`。以下是两种可靠方法: --- #### **方法1:使用Hbitmap转换(推荐高性能场景)** ```csharp using System.Runtime.InteropServices; using System.Windows; using System.Windows.Media.Imaging; public static class BitmapConverter { [DllImport("gdi32.dll")] private static extern bool DeleteObject(IntPtr hObject); public static BitmapSource ToBitmapSource(Bitmap bitmap) { IntPtr hBitmap = bitmap.GetHbitmap(); try { return Imaging.CreateBitmapSourceFromHBitmap( hBitmap, IntPtr.Zero, Int32Rect.Empty, BitmapSizeOptions.FromEmptyOptions() ); } finally { DeleteObject(hBitmap); // 关键!避免内存泄漏 } } } // 调用示例 Bitmap cameraBitmap = GetCameraBitmap(); // 从相机获取的Bitmap imageControl.Source = BitmapConverter.ToBitmapSource(cameraBitmap); ``` **优点**:直接操作内存句柄,转换效率高,适合实时视频流[^2]。 --- #### **方法2:使用MemoryStream转换** ```csharp public static BitmapImage ConvertToBitmapImage(Bitmap bitmap) { using (MemoryStream memory = new MemoryStream()) { bitmap.Save(memory, ImageFormat.Png); // 推荐PNG避免压缩失真 memory.Position = 0; BitmapImage bitmapImage = new BitmapImage(); bitmapImage.BeginInit(); bitmapImage.StreamSource = memory; bitmapImage.CacheOption = BitmapCacheOption.OnLoad; // 关键! bitmapImage.EndInit(); bitmapImage.Freeze(); // 跨线程安全 return bitmapImage; } } // 调用示例 Bitmap cameraBitmap = GetCameraBitmap(); imageControl.Source = ConvertToBitmapImage(cameraBitmap); ``` **优点**:自动处理图像格式,适合静态图像[^1]。 --- ### **关键注意事项** 1. **内存泄漏预防**: - 方法1中必须调用`DeleteObject`释放Hbitmap - 方法2中确保`using`释放MemoryStream 2. **线程安全**: - 相机数据通常在非UI线程获取,需通过`Dispatcher.BeginInvoke`更新UI: ```csharp Dispatcher.BeginInvoke(() => { imageControl.Source = BitmapConverter.ToBitmapSource(cameraBitmap); }); ``` 3. **性能优化**: - 实时视频场景优先使用方法1 - 启用`BitmapImage.DecodePixelWidth`缩小渲染尺寸 - 避免频繁创建新对象(复用BitmapSource) > 示例完整代码已在[^1][^2]中验证,实际使用时需添加异常处理。 --- ###
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值