2013-04-25 2 views
6

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

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

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

Я знаю, как создать растровое изображение из массива пиксельных байтов, но не BitmapImage. Таким образом, можно создать Bitmap, а затем создать BitmapImagem из него. Here there is a way to create a BitmapImage from a Bitmap on the memory. Но я хочу создать BitmapImage непосредственно из источника данных (tFrame). Как я могу это сделать?

Я знаю, что у BitmapImage есть метод CopyPixels, но он имеет множество SetPixels.

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

Вот как я создаю растровое изображение из массива пиксельных байтов. Это было использовано в версии программного обеспечения 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); 
} 

EDIT 1:

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

//@ UI Thread 
public WriteableBitmap wbm = new WriteableBitmap(1024, 1024, (double)96, (double)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, (uint)(w * h * ch)); 
    //I dont know if it is safe to write to it buffer like this: 
    CopyMemory(this.wbBackBuffer, pData, (uint)(w * h * ch)); 
} 
+0

msdn.microsoft.com/en-gb/library/system.windows.interop.imaging.createbitmapsourcefrommemorysection.aspx – DarkSquirrel42

+0

СМ это: http://stackoverflow.com/a/15290190/1507182 – Obama

+0

@ DarkSquirrel42. Извините, ссылка не работает. – Pedro77

ответ

11

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

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, uint 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, (uint)(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; 
    } 
} 
+0

и назовите его так: xamlImage.Source = MakeBitmapSource.FromNativePointer (tFrame.ImageBuffer, tFrame.Width, tFrame.Height, tFrame.BitDepth/8); –

+0

Очень приятно. Я собираюсь проверить это сейчас! – Pedro77

+0

вы также можете обновить backbuffer из потока, отличного от UI! просто забудьте заморозить wbm и вызвать wbm.AddDirtyRect из потока пользовательского интерфейса, как только вы закончите. Я использую это для отображения видео в веб-камерах. прекрасно работает. –