Как создать BitmapImage из массива пиксельных байтов (отображение живого видео)

Мне нужно отображать живые изображения в элементе управления WPF. Я ищу самый быстрый способ сделать это с помощью WPF.

Я собираю изображения с камеры с помощью API dll (AVT).

Изображение записывается dll, и камера поднимает обратный вызов с помощью IntPtr в структуру изображения, называемую tFrame (описано ниже). Данные пикселя хранятся в элементе ImageBuffer с помощью InPtr для байтового массива.

Я знаю, как создать Bitmap из массива пиксельных байтов, но не BitmapImage. Таким образом, можно создать Bitmap, а затем создать BitmapImagem из него. Здесь есть способ создать BitmapImage из растрового изображения в памяти. Но я хочу создать BitmapImage непосредственно из источника данных (tFrame). Как я могу это сделать?

Я знаю, что BitmapImage имеет метод CopyPixels, но он не имеет значения SetPixels.

public struct tFrame
{
 public IntPtr AncillaryBuffer;
 public **** AncillaryBufferSize;
 public **** AncillarySize;
 public tBayerPattern BayerPattern;
 public **** BitDepth;
 public tFrameCtx Context;
 public tImageFormat Format;
 public **** FrameCount;
 public **** Height;
 public IntPtr ImageBuffer;
 public **** ImageBufferSize;
 public **** ImageSize;
 public **** RegionX;
 public **** RegionY;
 public tErr Status;
 public **** TimestampHi;
 public **** TimestampLo;
 public **** Width;
}

Вот как я создаю Bitmap из массива пиксельных байтов. Это было использовано в версии программного обеспечения WinForm.

private void CreateBitmap(tFrame frame)
{
 //This sample is for a 8bpp captured image
 PixelFormat pxFormat = PixelFormat.Format8bppIndexed;
 //STRIDE
 //[https://stackoverflow.com/questions/1983781/why-does-bitmapsource-create-throw-an-argumentexception/1983886#1983886][3]
 //float bitsPerPixel = System.Drawing.Image.GetPixelFormatSize(format);
 int bitsPerPixel = ((int)pxFormat >> 8) & 0xFF;
 //Number of bits used to store the image data per line (only the valid data)
 int validBitsPerLine = ((int)frame.Width) * bitsPerPixel;
 //4 bytes for every int32 (32 bits)
 int stride = ((validBitsPerLine + 31) / 32) * 4;
 Bitmap bmp = new Bitmap((int)frame.Width, (int)frame.Height, stride, pxFormat, frame.ImageBuffer);
}

ИЗМЕНИТЬ 1:

Благодаря dr.mo теперь я могу отображать 60 изображений FPS 1024x1024 с использованием ~ 3% процессора! Что я делаю:

//@ UI Thread
public WriteableBitmap wbm = new WriteableBitmap(1024, 1024, (******)96, (******)96, System.Windows.Media.PixelFormats.Gray8, null);
this.wbBackBuffer = this.wbm.BackBuffer;
//This can be called by a timer in the UI thread or at the grab Thread for every image, the CPU usage is almost the same.
void UpdateDisplayImage()
{
wbm.Lock();
wbm.AddDirtyRect(new Int32Rect(0, 0, wbm.PixelWidth, wbm.PixelHeight));
wbm.Unlock();
}
//@ Grab Thread
//Update the backbuffer with new camera image data.
UpdateBackBuffer(...);
/// <summary>
/// [http://msdn.microsoft.com/en-us/library/system.windows.media.imaging.writeablebitmap.aspx]
/// </summary>
public void UpdateBackBuffer(IntPtr pData, int w, int h, int ch)
{
 //Can not acess wbm from outside UI thread
 //CopyMemory(wbm.BackBuffer, pData, (****)(w * h * ch));
 //I dont know if it is safe to write to it buffer like this:
 CopyMemory(this.wbBackBuffer, pData, (****)(w * h * ch));
}
1 ответ

Это должно сделать трюк. это супер быстро.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.Drawing;
using System.Runtime.InteropServices;
using System.IO;
using System.ComponentModel;
public class MakeBitmapSource
{
 [DllImport("kernel32.dll", EntryPoint = "RtlMoveMemory")]
 public static extern void CopyMemory(IntPtr Destination, IntPtr Source, **** Length);
 public static BitmapSource FromNativePointer(IntPtr pData, int w, int h, int ch)
 {
 PixelFormat format = PixelFormats.Default;
 if (ch == 1) format = PixelFormats.Gray8; //grey scale image 0-255
 if (ch == 3) format = PixelFormats.Bgr24; //RGB
 if (ch == 4) format = PixelFormats.Bgr32; //RGB + alpha
 WriteableBitmap wbm = new WriteableBitmap(w, h, 96, 96, format, null);
 CopyMemory(wbm.BackBuffer, pData, (****)(w * h * ch));
 wbm.Lock();
 wbm.AddDirtyRect(new Int32Rect(0, 0, wbm.PixelWidth, wbm.PixelHeight));
 wbm.Unlock();
 return wbm;
 }
 public static BitmapSource FromArray(byte[] data, int w, int h, int ch)
 {
 PixelFormat format = PixelFormats.Default;
 if (ch == 1) format = PixelFormats.Gray8; //grey scale image 0-255
 if (ch == 3) format = PixelFormats.Bgr24; //RGB
 if (ch == 4) format = PixelFormats.Bgr32; //RGB + alpha
 WriteableBitmap wbm = new WriteableBitmap(w, h, 96, 96, format, null);
 wbm.WritePixels(new Int32Rect(0, 0, w, h), data, ch * w, 0);
 return wbm;
 }
}

licensed under cc by-sa 3.0 with attribution.