【问题标题】:Rendering bitmap with XNA slow when grabbing image from webcam, why?从网络摄像头抓取图像时,使用 XNA 渲染位图很慢,为什么?
【发布时间】:2012-03-09 09:41:23
【问题描述】:

我已经花了几个星期来抓取网络摄像头图像并将其渲染到 Windows 窗体上,但一直在与速度问题作斗争。我需要至少 10 Hz 的帧速率才能更新我的后台进程。

我开始使用图片框,但我最终得到的解决方案是在我的表单中创建一个 XNA 面板,然后通过使用脚本将位图转换为 Texture2D 将图像渲染为背景精灵在这里找到。

我现在遇到但无法解决的问题是;当我通过调用下面的位图构造函数在代码中加载位图时,一切运行顺利,我可以获得高 fps。这是我在测试期间所做的,并且对结果非常满意。

Bitmap image = new Bitmap(320, 240);

但是,一旦我发送了从网络摄像头获取的位图,由于某种我无法理解的原因,渲染需要更长的时间。据我对位图的了解,图像的格式相同,只是像素的颜色不同。我检查的格式是大小(320*240)、分辨率(96)和像素格式(Format32bppArgb)。我错过了什么吗?

这就是我从网络摄像头抓取图像的方式:

VideoCaptureDevices = new FilterInfoCollection(FilterCategory.VideoInputDevice);

        FinalVideoSource = new VideoCaptureDevice(VideoCaptureDevices[0].MonikerString);
        FinalVideoSource.DesiredFrameSize = new Size(320, 240);
        FinalVideoSource.DesiredFrameRate = fps;
        FinalVideoSource.NewFrame += new NewFrameEventHandler(FinalVideoSource_NewFrame);

void FinalVideoSource_NewFrame(object sender, NewFrameEventArgs eventArgs)
    {
        // create bitmap from frame
        image = eventArgs.Frame.Clone(new Rectangle(0, 0, 320, 240), PixelFormat.Format32bppArgb);
...

这是我在 XNA 中的绘图功能:

protected override void Draw()
    {
        backTexture = GetTexture(GraphicsDevice, image);                

        GraphicsDevice.Clear(Microsoft.Xna.Framework.Color.CornflowerBlue);

        // TODO: Add your drawing code here
        sprites.Begin();
        Vector2 pos = new Vector2(0, 0);
        sprites.Draw(backTexture, pos, Microsoft.Xna.Framework.Color.White);
        sprites.End();
    }


private Texture2D GetTexture(GraphicsDevice dev, System.Drawing.Bitmap bmp)
    {
        int[] imgData = new int[bmp.Width * bmp.Height];
        Texture2D texture = new Texture2D(dev, bmp.Width, bmp.Height);

        unsafe
        {
            // lock bitmap
            System.Drawing.Imaging.BitmapData origdata =
                bmp.LockBits(new System.Drawing.Rectangle(0, 0, bmp.Width, bmp.Height), System.Drawing.Imaging.ImageLockMode.ReadOnly, bmp.PixelFormat);

            uint* byteData = (uint*)origdata.Scan0;

            // Switch bgra -> rgba
            for (int i = 0; i < imgData.Length; i++)
            {
                byteData[i] = (byteData[i] & 0x000000ff) << 16 | (byteData[i] & 0x0000FF00) | (byteData[i] & 0x00FF0000) >> 16 | (byteData[i] & 0xFF000000);
            }

            // copy data
            System.Runtime.InteropServices.Marshal.Copy(origdata.Scan0, imgData, 0, bmp.Width * bmp.Height);

            byteData = null;

            // unlock bitmap
            bmp.UnlockBits(origdata);
        }

        texture.SetData(imgData);

        return texture;
    }

如果有人能帮我解决这个问题,我将不胜感激,因为我现在陷入困境。这里的社区很棒,我在没有事先询问的情况下成功地走到了这一步,这太棒了,因为我之前没有使用 C# 或 XNA 的经验。考虑到这一点,我意识到我可能遗漏了一些简单的东西,或者只是以错误的方式接近它。

我已将其范围缩小到位图图像加载。使用新构建的位图时,我唯一改变的是在 XNA 中处理之前简单地覆盖来自网络摄像头的位图。

我现在的问题是,我是否错过了位图的构造方式,可以解释渲染速度的巨大差异?转换到 Texture2D 是这里的问题吗?但我看不出不同的图像会如何影响转换的速度。

【问题讨论】:

    标签: c# forms bitmap xna texture2d


    【解决方案1】:

    好的。我不知道到底是什么问题。但是我可以给你一些cmets。
    -首先,将 XNA 游戏的帧速率设置为等于或小于您的网络摄像头 fps。 默认情况下,XNA 以 60fps 运行,因此如果您使用 30fps,您将为网络摄像头的每一帧调用两次 GetTexture() 方法。 在初始化代码中:

    TargetElapsedTime = TimeSpan.FromSeconds(1f/webcam fps)
    

    如果这不起作用... 您可以尝试使用此代码将位图转换为纹理。

    protected override void Draw()
    {
        //Unset the texture from the GraphicsDevice
        for (int i = 0; i < 16; i++)
                {
                    if (Game.GraphicsDevice.Textures[i] == backTexture)
                    {
                        Game.GraphicsDevice.Textures[i] = null;
                        break;
                    }
                }
    
        backTexture.SetData<byte>(image.GetBytes());                
    
        GraphicsDevice.Clear(Microsoft.Xna.Framework.Color.CornflowerBlue);
    
        // TODO: Add your drawing code here
        sprites.Begin();
        Vector2 pos = new Vector2(0, 0);
        sprites.Draw(backTexture, pos, Microsoft.Xna.Framework.Color.White);
        sprites.End();
    
    }
    
    
    public static byte[] GetBytes(this Bitmap bitmap)
    {
        var data = bitmap.LockBits(new System.Drawing.Rectangle(0, 0, bitmap.Width, bitmap.Height), 
            System.Drawing.Imaging.ImageLockMode.ReadOnly, bitmap.PixelFormat);
    
        // calculate the byte size: for PixelFormat.Format32bppArgb (standard for GDI bitmaps) it's the hight * stride
        int bufferSize = data.Height * data.Stride; // stride already incorporates 4 bytes per pixel
    
        // create buffer
        byte[] bytes = new byte[bufferSize];
    
        // copy bitmap data into buffer
        Marshal.Copy(data.Scan0, bytes, 0, bytes.Length);
    
        // unlock the bitmap data
        bitmap.UnlockBits(data);
    
        return bytes;
    
    }
    

    我正在测试这段代码并且工作正常。希望对您有所帮助。

    【讨论】:

    • 大声笑,我只是注意到这个问题是在 3 月提出的。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-06-29
    • 1970-01-01
    • 2020-07-06
    • 1970-01-01
    • 2019-12-15
    • 1970-01-01
    • 2015-01-12
    相关资源
    最近更新 更多