【问题标题】:How to create a BitmapImage from a pixel byte array (live video display)如何从像素字节数组创建 BitmapImage(实时视频显示)
【发布时间】:2013-04-19 16:26:55
【问题描述】:

我需要在 WPF 控件上显示实时图像。我正在寻找使用 WPF 的最快方法。

我正在使用相机的 dll API (AVT) 从相机捕捉图像。

图像由 dll 写入,相机通过 IntPtr 向名为 tFrame 的图像结构发起回调(如下所述)。像素数据存储在 ImageBuffer 属性中,是一个字节数组的 InPtr。

我知道如何从像素字节数组创建位图,但不知道位图图像。因此可以创建一个 Bitmap,然后从中创建一个 BitmapImagem。 Here there is a way to create a BitmapImage from a Bitmap on the memory。但我想直接从数据源 (tFrame) 创建 BitmapImage。我该怎么做?

我知道 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);
}

编辑 1:

感谢 dr.mo,现在我能够以大约 3% 的 CPU 使用率显示 60 FPS 1024x1024 图像! 我正在做的是:

//@ 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));
}

【问题讨论】:

  • msdn.microsoft.com/en-gb/library/system.windows.interop.imaging.createbitmapsourcefrommemorysection.aspx
  • @DarkSquirrel42。抱歉,链接已损坏。
  • @奥巴马。谢谢,我会测试那个解决方案。

标签: c# wpf video-capture bitmapimage


【解决方案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, 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;
    }
}

【讨论】:

  • 并这样称呼它:xamlImage.Source = MakeBitmapSource.FromNativePointer(tFrame.ImageBuffer,tFrame.Width,tFrame.Height,tFrame.BitDepth/8);
  • 非常好。我现在要测试它!
  • 您也可以从非 UI 线程更新后备缓冲区!只需确保冻结 wbm 并在完成后从 UI 线程调用 wbm.AddDirtyRect。我用它来显示网络摄像头视频。效果很好。
  • 不,我没有,但我知道它并且我敢打赌它会更慢,因为它涉及每一步的图像创建。使用 WB,您只需复制数据,无需在每次更新视频帧时创建新图像。加上问题是 1. 你没有字节数组,但有一个本机指针 (IntPtr) 2. 你可以使用 WB 在非 UI 线程中更新 - 这对于你想要做的事情来说是必不可少的。这是我遇到的在 WPF 中显示位图的绝对最快的方法。
  • @Erti-Chris Eelmaa 是的,一旦将 WB 分配给源,您就可以更新它!您所做的是冻结 WB,获取对它的本机 BackBuffer 属性的引用,并在您想要的任何地方更新它,包括非 UI 线程。但是,除非您在 UI 线程中调用 AddDirtyRect 函数,否则图像不会更新。就是这样。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-03-29
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-01-03
  • 1970-01-01
相关资源
最近更新 更多