【问题标题】:fast converting Bitmap to BitmapSource wpf快速将位图转换为位图源 wpf
【发布时间】:2015-08-23 23:50:57
【问题描述】:

我需要在Image 组件上以 30Hz 的频率绘制图像。 我使用此代码:

public MainWindow()
    {
        InitializeComponent();

        Messenger.Default.Register<Bitmap>(this, (bmp) =>
        {
            ImageTarget.Dispatcher.BeginInvoke((Action)(() =>
            {
                var hBitmap = bmp.GetHbitmap();
                var drawable = System.Windows.Interop.Imaging.CreateBitmapSourceFromHBitmap(
                  hBitmap,
                  IntPtr.Zero,
                  Int32Rect.Empty,
                  BitmapSizeOptions.FromEmptyOptions());
                DeleteObject(hBitmap);
                ImageTarget.Source = drawable;
            }));
        });
    }

问题是,使用这段代码,我的 CPU 使用率大约是 80%,如果没有转换,大约是 6%。

那么为什么转换位图这么长呢?
有没有更快的方法(使用不安全的代码)?

【问题讨论】:

  • 没有转换显示什么?不显示任何位图时 CPU 消耗约为 6%?
  • 是的,相机发送带有新帧的事件,但没有转换,也没有任何显示。
  • 那么你怎么知道不是所有的 80% 的 CPU 消耗都只是用于每秒显示 30 个 BitmapSource,而转换完全不需要时间?
  • 你可能是对的,我测试了 whitout 显示(但保持转换),它的使用率约为 40%。 (这很正常,但有点高)。之后,我添加了一个 GC.Collect();在 ImageTarget.Source = drawable 之后;我的 CPU 使用率约为 45%。所以 80% 可能是内存泄漏。

标签: c# wpf bitmap copy bitmapsource


【解决方案1】:

这是一种(根据我的经验)至少比CreateBitmapSourceFromHBitmap 快四倍的方法。

它要求您为生成的 BitmapSource 设置正确的PixelFormat。

public static BitmapSource Convert(System.Drawing.Bitmap bitmap)
{
    var bitmapData = bitmap.LockBits(
        new System.Drawing.Rectangle(0, 0, bitmap.Width, bitmap.Height),
        System.Drawing.Imaging.ImageLockMode.ReadOnly, bitmap.PixelFormat);

    var bitmapSource = BitmapSource.Create(
        bitmapData.Width, bitmapData.Height,
        bitmap.HorizontalResolution, bitmap.VerticalResolution,
        PixelFormats.Bgr24, null,
        bitmapData.Scan0, bitmapData.Stride * bitmapData.Height, bitmapData.Stride);

    bitmap.UnlockBits(bitmapData);

    return bitmapSource;
}

【讨论】:

    【解决方案2】:

    我在克莱门斯回答之前回答了自己:

    [DllImport("kernel32.dll", EntryPoint = "CopyMemory", SetLastError = false)]
    public static extern void CopyMemory(IntPtr dest, IntPtr src, uint count);
    
    WriteableBitmap writeableBitmap = new WriteableBitmap(1280, 1024, 96.0, 96.0, PixelFormats.Bgr24, null);
    
    public MainWindow()
    {
        InitializeComponent();
    
        ImageTarget.Source = writeableBitmap;
    
        Messenger.Default.Register<Bitmap>(this, (bmp) =>
        {
            ImageTarget.Dispatcher.BeginInvoke((Action)(() =>
            {
                BitmapData data = bmp.LockBits(new System.Drawing.Rectangle(0, 0, bmp.Width, bmp.Height), ImageLockMode.ReadOnly, bmp.PixelFormat);
                writeableBitmap.Lock();
                CopyMemory(writeableBitmap.BackBuffer, data.Scan0,
                           (writeableBitmap.BackBufferStride * bmp.Height));
                writeableBitmap.AddDirtyRect(new Int32Rect(0, 0, bmp.Width, bmp.Height));
                writeableBitmap.Unlock();
                bmp.UnlockBits(data);
            }));
        });
    }
    

    现在我的 CPU 使用率大约是 15%

    【讨论】:

    • 重用一个 WriteableBitmap 甚至比每次都创建一个新的要好。您可能还应该显示 CopyMemory 的 DllImport 声明。
    • 您是否尝试过 Lock/AddDirtyRect/Unlock 序列是否真的比 WritePixels 快?根据我的经验,它或多或少是相同的,后者会为您节省一些代码和 DllImport。
    • 我用 WritePixel 和 CopyMemory 进行了测试,看起来一样,但 WritePixel 更具可读性。我要切换到这个解决方案
    • @Epitouille 在 CopyMemory 行中显示错误消息“system.accessviolationexception 试图读取或写入受保护的内存”
    • 直接复制到BackBuffer比WritePixels稍微快一点。 WritePixels 本质上只是一步完成了 Lock、MemCpy、AddDirtyRect 和 Unlock。但它也会对输入缓冲区执行一系列健全性检查,这会减慢速度。这对您的用例可能无关紧要,但如果您试图挤出最后几毫秒的性能,请直接复制到 BackBuffer!
    猜你喜欢
    • 1970-01-01
    • 2014-12-03
    • 2022-01-18
    • 1970-01-01
    • 1970-01-01
    • 2019-12-17
    • 2013-11-30
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多