Почему рендеринг растровых изображений с помощью XNA происходит медленно при получении изображения с веб-камеры? - PullRequest
3 голосов
/ 09 марта 2012

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

1 Ответ

1 голос
/ 31 августа 2012

Хорошо. Я не знаю, в чем именно проблема. Но я могу дать вам несколько комментариев.
Во-первых, установите частоту кадров в игре XNA, равную или меньшую частоте кадров вашей веб-камеры. По умолчанию 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;

}

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

...