Повышение скорости алгоритма коррекции видео на основе заблокированного растрового изображения, getpixel и оптимизированных вспомогательных функций.

Я написал этот код, чтобы удалить раздражающие шаблоны в видео из-за неисправности камеры. Проблема в том, что для кодирования двухминутного видео этому алгоритму требуется более 2 часов. Я хочу значительно сократить необходимое время.

Алгоритм выполняет итерацию по каждому изображению, просматривает каждые 4 пикселя оттуда, создает среднее значение и, если среднее значение ниже порогового значения, устанавливает для текущего пикселя белый цвет. Я мог бы использовать шаг = 2 и установить матрицу 2x2 в белый цвет, но это ухудшает качество изображения и увеличивает скорость только вдвое.

Я уже добавил lockbitmap, lockbits и улучшил вспомогательные функции.

До и после фрагмента using (d2 = reader.ReadVideoFrame()) у меня есть программа для чтения и записи видео aforge на основе ffmpeg.

using (d2 = reader.ReadVideoFrame())
{
    LockBitmap lockBitmap = new LockBitmap(d2);
    lockBitmap.LockBits();

    Color v = Color.FromArgb(240, 237, 241);
    for (int x = 0; x < lockBitmap.Width-1; x = x + 1)
    {
        for (int y = 0; y < lockBitmap.Height-1; y = y + 1)
        {
            Color dus = durchschnitt(lockBitmap.GetPixel(x, y),
                lockBitmap.GetPixel(x + 1, y),
                lockBitmap.GetPixel(x, y + 1),
                lockBitmap.GetPixel(x + 1, y + 1));
            if (abstand(dus, v) < 50)
            {
                lockBitmap.SetPixel(x, y, Color.White);
            }
        }
    }
    lockBitmap.UnlockBits();
}

Вспомогательные функции:

private Color durchschnitt(Color c1, Color c2, Color c3, Color c4)
{
    return Color.FromArgb((int)((c1.R + c2.R + c3.R + c4.R) / 4),
        (int)((c1.G + c2.G + c3.G + c4.G) / 4),
        (int)((c1.B + c2.B + c3.B + c4.B) / 4));
}

а также

private double abstand(Color c1, Color c2)
{
    return Math.Sqrt(Math.Pow(c2.R - c1.R, 2) + Math.Pow(c2.G - c1.G, 2) + Math.Pow(c2.B - c1.B, 2));
}

LockBitmap находится здесь.


person Fabien Biller    schedule 14.10.2018    source источник


Ответы (1)


lockBits работает не так.

Короче говоря, вам нужно заблокировать биты, чтобы получить доступ к строке сканирования через указатели. Лучше всего использовать 32bpp для целочисленного доступа. Вы можете вычислить пиксель в непрерывном массиве следующим образом.

Вам нужно будет украсить свой класс или метод с помощью unsafe ключевое слово, а также установить параметр сборки проекта для использования небезопасного кода, вы используете указатели.

var w = bmp.Width;
var h = bmp.Height;

// lock the array for direct access
var data = bmp.LockBits(new Rectangle(0, 0, w, h), ImageLockMode.ReadWrite, PixelFormat.Format32bppPArgb);
var scan0Ptr = (int*)data.Scan0;

// get the stride
var stride = data.Stride / 4;

// scan all x
for (var x = 0; x < w; x++)
{
   var pX = scan0Ptr + x;

   // scan all y
   for (var y = 0; y < h; y++)
   {
      // this is now your pixel *p, which essentially is a pointer to
      // to a memory which contains your pixel
      var p = pX + y * stride;

      // or for better access to X and Y
      var p = scan0Ptr + x + y * stride;

      // or alternatively you can just access you pixel with the following notation  
      *(scan0Ptr + x + y * stride) // <== just use this like any variable 

      // example how to convert pixel *p to a Color
      var color = Color.FromArgb(*p);

      // example Convert a Color back to *p and update the pixel
      *p = Color.White.ToArgb();
   }
}
// unlock the bitmap
bmp.UnlockBits(data);

Я оставлю остальные детали на ваше усмотрение, однако это даст вам наилучшую производительность (минус некоторые микрооптимизации).

Наконец, сокращение вызовов внешних методов также даст вам прирост производительности; однако, если вам действительно нужно, вы можете помочь CLR, используя MethodImplOptions Enum для AggressiveInlining

Например

[MethodImpl(MethodImplOptions.AggressiveInlining)]
private Color durchschnitt(Color c1, Color c2, Color c3, Color c4)

Также вы можете повысить производительность, переместив компоненты:

var r = ((*p >> 16) & 255);
var g = ((*p >> 8) & 255);
var b = ((*p >> 0) & 255);

И вы могли бы, вероятно, использовать многопоточность для этой рабочей нагрузки.

person TheGeneral    schedule 14.10.2018
comment
Используя ваши рекомендации, я достиг 1 часа для видео 2:10 1920 x 1080 со скоростью 52,26 кадров в секунду. Да, unsafe необходимо. - person Fabien Biller; 14.10.2018
comment
@FabienBiller, следующим шагом будет заставить это происходить на нескольких ядрах. - person TheGeneral; 14.10.2018
comment
У меня есть 8 и cuda. У вас есть идеи? - person Fabien Biller; 14.10.2018
comment
Может быть, лучше задать другой вопрос, но да, определенно многопоточный или графический процессор, вероятно, съест это. - person TheGeneral; 14.10.2018
comment
@FabienBiller Также я начал микрооптимизировать его, вы могли бы многое сбросить, убедившись, что каждая строка кода эффективна в цикле, т. Е. Существует много преобразований в цвет и т. Д. - person TheGeneral; 14.10.2018
comment
сейчас я на 49-й минуте удалил все FromArgb. - person Fabien Biller; 14.10.2018
comment
@FabienBiller теперь я думаю, что вы можете разбить его пополам или больше с несколькими потоками или даже больше на cuda - person TheGeneral; 14.10.2018
comment
С Parallel.Foreach я сократил это время до 12 минут. Посмотрим, что умеет CUDA. Кстати: сколько элементов, которые я использую List‹Bitmap›, должно быть в нем одновременно? В настоящее время я использую 30. Это делает мой процессор пиковым, хотя с памятью все в порядке. - person Fabien Biller; 14.10.2018
comment
@FabienBiller Свойство MaxDegreeOfParallelism влияет на количество одновременных операций, выполняемых вызовами методов Parallel, которые передаются этому экземпляру ParallelOptions. Положительное значение свойства ограничивает количество одновременных операций заданным значением. Если он равен -1, количество одновременно выполняемых операций не ограничено. - person TheGeneral; 14.10.2018
comment
По умолчанию For и ForEach будут использовать столько потоков, сколько предоставляет базовый планировщик, поэтому изменение значения MaxDegreeOfParallelism по умолчанию ограничивает только количество одновременно используемых задач. - person TheGeneral; 14.10.2018
comment
@FabienBiller Так что не ограничивайте то, что входит в цикл for, если у вас нет проблем с памятью, и не устанавливайте MaxDegreeOfParallelism, однако поэкспериментируйте и посмотрите, что лучше всего подходит для вас. - person TheGeneral; 14.10.2018