使用 C# 绘制一系列位图的最快方法

发布于 2024-09-25 10:14:17 字数 1749 浏览 4 评论 0原文

我正在构建一个应用程序,该应用程序从相机捕获视频帧(30fps @ 640x480),处理它们,然后将它们显示在 Windows 窗体上。我最初使用的是 DrawImage(参见下面的代码),但性能很糟糕。即使禁用处理步骤,我在 2.8GHz Core 2 Duo 机器上可以获得的最佳帧率是 20fps。 Windows 窗体上启用了双缓冲,否则我会感到撕裂。

注意:使用的图像是 Format24bppRgb 格式的位图。我知道对于 Format32bppArgb 格式的图像,DrawImage 应该会更快,但我受到帧捕获器输出的格式的限制。

private void CameraViewForm_Paint(object sender, PaintEventArgs e)
{
    Graphics g = e.Graphics;

    // Maximize performance
    g.CompositingMode = CompositingMode.SourceOver;
    g.PixelOffsetMode = PixelOffsetMode.HighSpeed;
    g.CompositingQuality = CompositingQuality.HighSpeed;
    g.InterpolationMode = InterpolationMode.NearestNeighbor;
    g.SmoothingMode = SmoothingMode.None;

    g.DrawImage(currentFrame, displayRectangle);
}

我尝试使用带有纹理和 Spites 的 Managed DirectX 9(见下文),但性能更差。我对 DirectX 编程非常陌生,因此这可能不是最好的 DirectX 代码。

private void CameraViewForm_Paint(object sender, PaintEventArgs e)
{
    device.Clear(ClearFlags.Target, Color.Black, 1.0f, 0);

    device.BeginScene();

    Texture texture = new Texture(device, currentFrame, Usage.None, Pool.Managed);
    Rectangle textureSize;

    using (Surface surface = texture.GetSurfaceLevel(0))
    {
        SurfaceDescription surfaceDescription = surface.Description;
        textureSize = new Rectangle(0, 0, surfaceDescription.Width, surfaceDescription.Height);
    }
    Sprite sprite = new Sprite(device);

    sprite.Begin(SpriteFlags.None);
    sprite.Draw(texture, textureSize, new Vector3(0, 0, 0), new Vector3(0, 0, 0), Color.White);
    sprite.End();

    device.EndScene();

    device.Present();

    sprite.Dispose();
    texture.Dispose();
}

我需要它在 XP、Vista 和 Windows 7 上工作。我不知道是否值得尝试 XNA 或 OpenGL。这看起来应该是一件很容易完成的事情。

I'm building an application that captures video frames from a camera (30fps @ 640x480), processes them, and then displays them on a Windows Form. I was initially using DrawImage (see code below) but the performance was terrible. Even with the processing step disabled the best I can get is 20fps on a 2.8GHz Core 2 Duo machine. Double buffering is enabled on the Windows form otherwise I get tearing.

Note: The image used is a Bitmap of format Format24bppRgb. I know that DrawImage is supposed to be faster with a Format32bppArgb formatted image but I am restricted by the format that comes out of the frame grabber.

private void CameraViewForm_Paint(object sender, PaintEventArgs e)
{
    Graphics g = e.Graphics;

    // Maximize performance
    g.CompositingMode = CompositingMode.SourceOver;
    g.PixelOffsetMode = PixelOffsetMode.HighSpeed;
    g.CompositingQuality = CompositingQuality.HighSpeed;
    g.InterpolationMode = InterpolationMode.NearestNeighbor;
    g.SmoothingMode = SmoothingMode.None;

    g.DrawImage(currentFrame, displayRectangle);
}

I tried using Managed DirectX 9 with Textures and Spites (see below) but the performance was even worse. I'm very new to DirectX programming so this may not be the best DirectX code.

private void CameraViewForm_Paint(object sender, PaintEventArgs e)
{
    device.Clear(ClearFlags.Target, Color.Black, 1.0f, 0);

    device.BeginScene();

    Texture texture = new Texture(device, currentFrame, Usage.None, Pool.Managed);
    Rectangle textureSize;

    using (Surface surface = texture.GetSurfaceLevel(0))
    {
        SurfaceDescription surfaceDescription = surface.Description;
        textureSize = new Rectangle(0, 0, surfaceDescription.Width, surfaceDescription.Height);
    }
    Sprite sprite = new Sprite(device);

    sprite.Begin(SpriteFlags.None);
    sprite.Draw(texture, textureSize, new Vector3(0, 0, 0), new Vector3(0, 0, 0), Color.White);
    sprite.End();

    device.EndScene();

    device.Present();

    sprite.Dispose();
    texture.Dispose();
}

I need this to work on XP, Vista and Windows 7. I don't know if it's worth trying XNA or OpenGL. This seems like it should be a very simple thing to accomplish.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

蓝颜夕 2024-10-02 10:14:18

答案就在你的面前。这是对一个老问题的迟到答案,但有人可能需要答案,所以......

而不是在绘图循环内声明新的纹理和矩形(这对资源来说非常紧张)为什么不在范围之外创建纹理?例如,尝试以下操作,而不是您的纹理:

Texture texture;
Rectangle textureSize;
private void InitiateTexture()
{
    texture = new Texture(device, new Bitmap("CAR.jpg"), Usage.None, Pool.Managed);

    using (Surface surface = texture.GetSurfaceLevel(0))
    {
        SurfaceDescription surfaceDescription = surface.Description;
        textureSize = new Rectangle(0, 0, 
                          surfaceDescription.Width, 
                          surfaceDescription.Height);
    }
}
protected override void OnPaint(System.Windows.Forms.PaintEventArgs e)
{
    device.Clear(ClearFlags.Target, Color.DarkSlateBlue, 1.0f, 0);
    device.BeginScene();

    Sprite sprite = new Sprite(device);

    sprite.Begin(SpriteFlags.None);
    sprite.Draw(texture, textureSize, 
    new Vector3(0, 0, 0), 
    new Vector3(0, 0, 0), Color.White);
    sprite.End();
    device.EndScene();
    device.Present();
}

然后,如果您需要启动多个纹理,请将其放入一个列表中,然后从该列表中进行绘制。它节省了使用资源的时间,然后在每次绘制时释放它们。

The answer is right in front of you. This is a late answer to an old question, but someone might need the answer so...

Instead of declaring new textures and rectangles inside your draw loop (which is quite intense on the resources) why not create a texture outside the scope? Instead of your one, for example, try this:

Texture texture;
Rectangle textureSize;
private void InitiateTexture()
{
    texture = new Texture(device, new Bitmap("CAR.jpg"), Usage.None, Pool.Managed);

    using (Surface surface = texture.GetSurfaceLevel(0))
    {
        SurfaceDescription surfaceDescription = surface.Description;
        textureSize = new Rectangle(0, 0, 
                          surfaceDescription.Width, 
                          surfaceDescription.Height);
    }
}
protected override void OnPaint(System.Windows.Forms.PaintEventArgs e)
{
    device.Clear(ClearFlags.Target, Color.DarkSlateBlue, 1.0f, 0);
    device.BeginScene();

    Sprite sprite = new Sprite(device);

    sprite.Begin(SpriteFlags.None);
    sprite.Draw(texture, textureSize, 
    new Vector3(0, 0, 0), 
    new Vector3(0, 0, 0), Color.White);
    sprite.End();
    device.EndScene();
    device.Present();
}

and then if you need to initiate multiple textures, do it into a list, then paint from that list. It saves having to use resources then free them each paint.

葬心 2024-10-02 10:14:18

CameraViewForm 是您的整个查看器窗口吗?如果是这样,则在“画图”上,将重新绘制整个窗口,包括所有按钮、进度条等。这将或多或少昂贵,具体取决于表单上的控件数量以及您为桌面启用的视觉效果各种操作系统中的元素(例如窗口栏透明度)。

尝试对整个表单进行单缓冲,但通过在调用 GreateGraphics() 时或在面板上调用 Invalidate() 之前声明一个新的 BufferedGraphicsContext,为面板(我假设您从中获得 displayRectangle)提供自己的 BufferedGraphicsContext。这允许面板与表单分开进行双缓冲。

Is CameraViewForm your entire viewer window? If so, then on Paint, the entire window is redrawn, including all buttons, progress bars, etc. This will be more or less expensive depending on the number of controls on your form, and the visual doo-dads you have enabled for desktop elements in various OSes (e.g. window bar transparency).

Try single-buffering the entire form, but giving the Panel (from which I assume you get displayRectangle) its own BufferedGraphicsContext, by declaring a new one when you call GreateGraphics() or before calling Invalidate() on the Panel. This allows the Panel to be double-buffered separately from the Form.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文