Как отображать быстро обновляемые изображения без большого выделения памяти?
У меня есть приложение WPF на ультразвуковом аппарате, которое отображает ультразвуковые изображения, сгенерированные в C++, со скоростью более 30 кадров в секунду.
Из того, что я понимаю, обычный процесс отображения изображений в WPF - это создание BitmapSource для вашего изображения и установка Source для вашего изображения, что затем приводит к его аннулированию и отображению.
Поскольку BitmapSources не реализуют IDisposable, использование этого метода заставило меня создавать 30 BitmapSources в секунду. Для изображения 640x480 с форматом 32bppArgb это примерно 30 МБ / с памяти, которая выделяется в секунду, а затем мусор удаляется каждые 10 секунд, вызывая видимую задержку. Очевидно, не приемлемое решение.
Мое настоящее решение:
В C++: я создаю System.Drawing.Bitmap (растровое изображение WinForms) в Managed C++, делаю memcpy из указателя, чтобы заполнить рисунок, использую объект Graphics для создания необходимого мне дополнительного рисунка и передаю его в C#/WPF во время события ImageReceived.
В C# Image.Source установлен источник, созданный BitmapBuffer, который является хакерским способом доступа к необработанным данным источника точечного рисунка: см. Эту ссылку. Я делаю P/Invoke CopyMemory, чтобы скопировать данные из Bitmap.Scan0 в BitmapBuffer. Затем я аннулирую изображение для обновления экрана и Dispose() объект Drawing.Bitmap, чтобы освободить память.
Хотя этот метод работал некоторое время, он кажется очень хакерским, и мне трудно поверить, что нет другого "подходящего" способа сделать это, кроме как с помощью рефлексии.
Вопрос: есть ли лучший способ?
2 ответа
Вот код, который я написал * для псевдонимов (совместного использования памяти) между WPF BitmapSource и GDI Bitmap (для моего собственного проекта)
Очевидно, что вам нужно настроить его под свои нужды, в конце концов, скорее всего, он получит менее "хакерское" чувство.
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);
}
}
* под "написал" я имею в виду "бросили вместе за 10 минут"
Если вы используете последние WPF-биты, проверьте WriteableBitmap, вам придется проделать большую часть работы, но вы действительно быстро обновитесь.
Сделайте быстрый Google, и вы получите несколько образцов.