2013-04-25 70 views
6

Tôi cần hiển thị hình ảnh trực tiếp trên điều khiển WPF. Tôi đang tìm cách nhanh nhất để làm điều này bằng cách sử dụng WPF.Cách tạo BitmapImage từ mảng byte pixel (hiển thị video trực tiếp)

Tôi đang chụp ảnh từ máy ảnh bằng API dll (AVT).

Hình ảnh được ghi bởi dll và máy ảnh tăng một cuộc gọi lại với IntPtr đến cấu trúc Hình ảnh được gọi là tFrame (được mô tả bên dưới). Dữ liệu pixel được lưu trữ tại ImageBuffer propertie với một InPtr đến một mảng byte.

Tôi biết cách tạo Bitmap từ mảng byte pixel, nhưng không phải là BitmapImage. Vì vậy, nó có thể tạo ra một Bitmap và sau đó tạo ra một BitmapImagem từ nó. Here there is a way to create a BitmapImage from a Bitmap on the memory. Nhưng tôi muốn tạo trực tiếp BitmapImage từ nguồn dữ liệu (tFrame). Làm thế nào tôi có thể làm điều đó?

Tôi biết rằng BitmapImage có phương pháp CopyPixels, nhưng nó laks của một 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; 
} 

Đây là cách tôi tạo bitmap từ mảng byte pixel. Điều này đã được sử dụng ở phiên bản WinForm của phần mềm.

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:

Nhờ dr.mo, bây giờ tôi có thể hiển thị 60 FPS 1024x1024 hình ảnh với ~ 3% sử dụng CPU! Những gì tôi đang làm là:

//@ 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

SEE này: http://stackoverflow.com/a/15290190/1507182 – Obama

+0

@ DarkSquirrel42. Xin lỗi, liên kết bị hỏng. – Pedro77

Trả lời

11

Điều này sẽ làm điều này. nó siêu nhanh.

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

và gọi nó như thế này: xamlImage.Source = MakeBitmapSource.FromNativePointer (tFrame.ImageBuffer, tFrame.Width, tFrame.Height, tFrame.BitDepth/8); –

+0

Rất đẹp. Tôi sẽ kiểm tra nó ngay bây giờ! – Pedro77

+0

bạn cũng có thể cập nhật backbuffer từ một chuỗi không phải UI! chỉ cần đảm bảo đóng băng wbm và gọi wbm.AddDirtyRect từ chuỗi giao diện người dùng khi bạn đã hoàn tất. tôi sử dụng nó để hiển thị video webcam. hoạt động tốt. –