Эффективное растровое изображение для OnnxRuntime Tensor в C# - PullRequest
2 голосов
/ 19 июня 2020

Я использую Microsoft OnnxRuntime для обнаружения и классификации объектов в изображениях, и я хочу применить его к видео в реальном времени. Для этого мне нужно преобразовать каждый кадр в тензор OnnxRuntime. Прямо сейчас я реализовал метод, который занимает около 300 мс:

public Tensor<float> ConvertImageToFloatTensor(Bitmap image)
    {
        // Create the Tensor with the appropiate dimensions  for the NN
        Tensor<float> data = new DenseTensor<float>(new[] { 1, image.Width, image.Height, 3 });

        // Iterate over the bitmap width and height and copy each pixel
        for (int x = 0; x < image.Width; x++)
        {
            for (int y = 0; y < image.Height; y++)
            {
                Color color = image.GetPixel(x, y);

                data[0, y, x, 0] = color.R / (float)255.0;
                data[0, y, x, 1] = color.G / (float)255.0;
                data[0, y, x, 2] = color.B / (float)255.0;
            }
        }

        return data;
    }

Мне нужно, чтобы этот код работал как можно быстрее, так как я представляю ограничивающие прямоугольники вывода детектора как слой поверх видео. . Кто-нибудь знает более быстрый способ сделать это обсуждение?

1 Ответ

3 голосов
/ 27 июня 2020

на основе ответов davidtbernal ( Быстрая работа с растровыми изображениями в C#) и FelipeDurar ( Изображение в оттенках серого из двоичных данных ), вы сможете быстрее получать доступ к пикселям с помощью LockBits и немного «небезопасного» кода.

public Tensor<float> ConvertImageToFloatTensorUnsafe(Bitmap image)
{
    // Create the Tensor with the appropiate dimensions  for the NN
    Tensor<float> data = new DenseTensor<float>(new[] { 1, image.Width, image.Height, 3 });    
    
    BitmapData bmd = image.LockBits(new Rectangle(0, 0, image.Width, image.Height), System.Drawing.Imaging.ImageLockMode.ReadOnly, image.PixelFormat);
    int PixelSize = 3;

    unsafe
    {
        for (int y = 0; y < bmd.Height; y++)
        {
            // row is a pointer to a full row of data with each of its colors
            byte* row = (byte*)bmd.Scan0 + (y * bmd.Stride);
            for (int x = 0; x < bmd.Width; x++)
            {           
                // note the order of colors is BGR
                data[0, y, x, 0] = row[x*PixelSize + 2] / (float)255.0;
                data[0, y, x, 1] = row[x*PixelSize + 1] / (float)255.0;
                data[0, y, x, 2] = row[x*PixelSize + 0] / (float)255.0;
            }
        }

        image.UnlockBits(bmd);
    }
    return data;
}

Я сравнил этот фрагмент кода со средним значением за 1000 запусков и получил примерно трехкратное улучшение производительности по сравнению с исходным кодом, но результаты могут отличаться.

Также обратите внимание, что я использовал 3 канала на пиксель, поскольку в вашем исходном ответе используются только эти значения. Если вы используете растровое изображение 32bpp, вы можете изменить PixelSize на 4, и последний канал должен быть альфа-каналом (row [x * PixelSize + 3])

...