如何在没有大内存分配的情况下显示快速更新的图像?

Wil*_*ins 13 c# wpf managed-c++ image-processing

我在超声波机器上有一个WPF应用程序,它以超过每秒30帧的速度显示用C++生成的超声图像.

据我所知,在WPF中显示图像的正常过程是为您的图像创建一个BitmapSource,并为您的图像设置Source,然后使其无效并显示.

由于BitmapSources没有实现IDisposable,使用这种方法迫使我每秒创建30个BitmapSources.对于具有32bppArgb格式的640x480图像,这是每秒分配大约30MB /秒的内存,然后每10秒进行一次垃圾处理,导致可见的延迟.显然不是一个可以接受的解决方案.

我目前的解决方案是:

在C++中:我在托管C++中创建一个System.Drawing.Bitmap(WinForms位图),从指针填充图片做一个memcpy,使用Graphics对象做我需要的一些额外的绘图,并将其传递给C#/ WPF在ImageReceived事件期间.

在C#中, Image.Source被设置为BitmapBuffer生成的源,这是一种访问位图源的原始数据的黑客方式:请参阅此链接.我做了一个CopyMemory的P/Invoke来将数据从Bitmap.Scan0复制到BitmapBuffer中.然后我使Image无效以更新屏幕,然后Dispose()Drawing.Bitmap对象释放内存.

虽然这种方法已经工作了一段时间,但它看起来非常糟糕,我发现很难相信没有其他"适当"的方法可以做到这一点,而不是通过反思.

问题:有更好的方法吗?

Sco*_*ott 12

如果你使用最新的WPF位检查WriteableBitmap,你将不得不做更多的腿部工作,但你真的会快速更新.

做一个快速的谷歌,你会得到一些样品.

  • 回顾一下这个,我相信多年后,我最终更倾向于这个解决方案.我保留了一个WriteableBitmap,从超声机器的图像缓冲区向BackBuffer执行Buffer.BlockCopy.因此,随着时间的推移没有内存使用(没有分配任何内容),即使使用较慢的双核处理器,也有大约60FPS的可靠性能. (2认同)
  • 重要提示:如果您使用上述 MSDN 链接中的示例,请确保更改“int pBackBuffer = (int)writeableBitmap.BackBuffer;”行 使用(长)。否则,它将在 64 位系统中崩溃。 (2认同)

bsn*_*eze 9

这里是我编写的一些代码*用于WPF BitmapSource和GDI Bitmap之间的别名(共享内存)(对于我自己的项目)

显然,你需要根据自己的需要进行调整,最终可能最终会产生一种不那么"hacky"的感觉.

class AliasedBitmapSource : BitmapSource {
    private Bitmap source;
    public AliasedBitmapSource(Bitmap source) {
        this.source = source;
        this.pixelHeight = source.Height;
        this.pixelWidth = source.Width;
        this.dpiX = source.HorizontalResolution;
        this.dpiY = source.VerticalResolution;
    }

    public override event EventHandler DownloadCompleted;
    public override event EventHandler<ExceptionEventArgs> DownloadFailed;
    public override event EventHandler<ExceptionEventArgs> DecodeFailed;

    protected override Freezable CreateInstanceCore() {
        throw new NotImplementedException();
    }

    private readonly double dpiX;
    public override double DpiX {
        get {
            return dpiX;
        }
    }

    private readonly double dpiY;
    public override double DpiY {
        get {
            return dpiY;
        }
    }

    private readonly int pixelHeight;
    public override int PixelHeight {
        get {
            return pixelHeight;
        }
    }

    private readonly int pixelWidth;
    public override int PixelWidth {
        get {
            return pixelWidth;
        }
    }

    public override System.Windows.Media.PixelFormat Format {
        get {
            return PixelFormats.Bgra32;
        }
    }

    public override BitmapPalette Palette {
        get {
            return null;
        }
    }

    public unsafe override void CopyPixels(Int32Rect sourceRect, Array pixels, int stride, int offset) {
        BitmapData sourceData = source.LockBits(
        sourceRect.ToRectangle(),
        ImageLockMode.ReadWrite,
        System.Drawing.Imaging.PixelFormat.Format32bppArgb);

        fixed (byte* _ptr = &((byte[])pixels)[0]) {
            byte* dstptr = _ptr;
            byte* srcptr = (byte*)sourceData.Scan0;

            for (int i = 0; i < pixels.Length; ++i) {
                *dstptr = *srcptr;
                ++dstptr;
                ++srcptr;
            }
        }

        source.UnlockBits(sourceData);
    }
}

public static class Extensions {
    public static Rectangle ToRectangle(this Int32Rect me) {
        return new Rectangle(
        me.X,
        me.Y,
        me.Width,
        me.Height);
    }

    public static Int32Rect ToInt32Rect(this Rectangle me) {
        return new Int32Rect(
        me.X,
        me.Y,
        me.Width,
        me.Height);
    }
}
Run Code Online (Sandbox Code Playgroud)

*由"写"我的意思是"在10分钟内聚集在一起"