Почему рендеринг растрового изображения с XNA медленный при захвате изображения с веб-камеры?

Несколько недель я работал над захватом изображения с веб-камеры и рендерингом его в форме Windows, но все время боролся с проблемами скорости. Мне нужна частота кадров не менее 10 Гц, чтобы иметь возможность обновлять фоновый процесс.

Я начал использовать pictureBox, но решение, к которому я пришел, - это создать 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? Но я не понимаю, как другое изображение может повлиять на скорость этого преобразования.


person bialad    schedule 09.03.2012    source источник


Ответы (1)


В порядке. Я точно не знаю, в чем проблема. Но я могу дать вам несколько комментариев.
- Во-первых, установите частоту кадров игры XNA равную или меньшую, чем fps вашей веб-камеры. По умолчанию XNA работает со скоростью 60 кадров в секунду, поэтому вы вызываете метод GetTexture () дважды для каждого кадра веб-камеры, если вы используете 30 кадров в секунду. В коде инициализации:

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;

}

Я тестировал этот код и отлично работает. Надеюсь на эту помощь.

person EdgarT    schedule 31.08.2012
comment
LOL, сразу замечу, что этот вопрос был задан в марте. - person EdgarT; 31.08.2012