Быстрое сложение изображений в C#

198
22 апреля 2018, 20:42

Основная задача: мне необходимо в реальном времени (или около того) сложение двух Grayscale FullHD 8bit изображений (которые будут использованы как R и G каналы) с камеры (60fps, производитель Imaging Source) в одно FullHD 24bit изображение (R,G,coef*G). Необходимо все полученные изображения отображать на экране (в виде realtime трансляции). Язык разработки - c#. Ограничение на coef: 0 < coef < 1.

Заступорился на создании самой функции быстрого сложения. Необходима "очень быстрая" отработка. Подразумеваю "<=0.01с на получение результирующего изображения".

Попытка формулировки вопроса: Какой специфический алгоритм обработки нужно использовать, чтобы решить подобную задачу, а именно проблему скорости? Через указатели я добился отработки примерно в 0.05с, что все еще долго.Буду рад полезным ссылкам (примеры, обучалки и прочее). Функция, которой произвожу обработку:

    private unsafe Bitmap Create_RGB_BMP_FastUnsafe(Bitmap pBMP1, Bitmap pBMP2, bool GreenFirst)
    {
        int width = 1920;
        int height = 1080;
        int WH = width * height;
        var pRes = new Bitmap(width, height, System.Drawing.Imaging.PixelFormat.Format24bppRgb);
        BitmapData bd_red, bd_green, bd_res;
        if (GreenFirst)
        {
            bd_green = pBMP1.LockBits(new Rectangle(0, 0, width, height), ImageLockMode.ReadOnly,
                PixelFormat.Format8bppIndexed);
            bd_red = pBMP2.LockBits(new Rectangle(0, 0, width, height), ImageLockMode.ReadOnly,
                PixelFormat.Format8bppIndexed);
            bd_res = pRes.LockBits(new Rectangle(0, 0, width, height), ImageLockMode.ReadWrite,
               PixelFormat.Format24bppRgb);//greenfirst=true;
        }
        else
        {
            bd_red = pBMP1.LockBits(new Rectangle(0, 0, width, height), ImageLockMode.ReadOnly,
                PixelFormat.Format8bppIndexed);
            bd_green = pBMP2.LockBits(new Rectangle(0, 0, width, height), ImageLockMode.ReadOnly,
                PixelFormat.Format8bppIndexed);
            bd_res = pRes.LockBits(new Rectangle(0, 0, width, height), ImageLockMode.ReadWrite,
               PixelFormat.Format24bppRgb);//greenfirst=false;
        }
        try
        {
            int stride1 = bd_red.Stride;
            int sk;
            byte* curpos_red = (byte*)bd_red.Scan0;
            byte* curpos_green = (byte*)bd_green.Scan0;
            byte* _currentPX = (byte*)bd_res.Scan0;
            for (sk = 0; sk < WH; sk++)
            {
                *_currentPX = (byte)(*(curpos_green) * 0.1); ++_currentPX;
                *_currentPX = *(curpos_green++); ++_currentPX;
                *_currentPX = *(curpos_red++); ++_currentPX;
            }
        }
        finally
        {
            pBMP1.UnlockBits(bd_red);
            pBMP2.UnlockBits(bd_green);
            pRes.UnlockBits(bd_res);
        }
        return pRes;
    }

P.S.: Кое-что из того, что я уже прочитал, перед тем, как спросить у вас, профессионалы)...

  1. https://stackoverflow.com/questions/8687637/fastest-image-processing-library
  2. https://stackoverflow.com/questions/5101986/iterate-over-pixels-of-an-image-with-emgu-cv/5107490#5107490
  3. https://m.habrahabr.ru/company/newprolab/blog/328422/
  4. https://m.habrahabr.ru/company/intel/blog/256533/
  5. https://www.codeproject.com/Articles/33838/Image-Processing-using-C
Answer 1

Что замедляет работу кода (что-то может быть не очень актуально, т.к. я не знаю точно, какой код генерирует шарп):

Необязательное здесь использование вещественной арифметики. Можно масштабировать коэффициент и после целочисленного умножения на него делить на 256 (возможно, нужно приводить к int для корректного умножения и потом к байту)

 cf256 = (int) 256 * coeff
 *_currentPX = (*(curpos_green) * сf256 >> 8); ++_currentPX;

Прыжки по памяти в три разных места - от этого полностью не избавиться, но в развертывание циклов может в какой-то мере помочь (возможно, кодогенератор сам умеет это делать, как некоторые плюсовые компиляторы в режиме суровой оптимизации)

Задача числомолотильная отлично подходит для MMX/SSE2/AVX2 блоков процессора. При этом несколько пикселов (сколько выходных пикселов влезет в 8/16/32 байта регистра SIMD) обрабатываются одновременно - при лобовом использовании можно ожидать ускорения раза в 4 (для SSE2). Можно ли это использовать в шарпе? Генерируется ли код для SIMD обработки?

(При использовании SIMD отказываться от float арифметики не обязательно)

Собственно, перечисленное реализовано опытными людьми в OpenCV, поэтому при возможности стоит эту библиотеку использовать.

Есть еще вариант, если это будет единственная вычислительно тяжелая задача - написать функцию на C/C++ с использованием SIMD инструкций (интринсики или вообще компилятор обеспечит автоматическую векторизацию) и вызывать её из C# (вероятно, нужно будет делать DLL).

И напоследок - если обработка 60 кадров в секунду нужна только для отображения - не разумно ли ограничиться обработкой и выводом 30 кадров?

READ ALSO
Unity Coroutines C#

Unity Coroutines C#

Как сделать универсальную корутину для запуска какой то функции через определённое время?

216
Смысл помещения DateTime в контейнер

Смысл помещения DateTime в контейнер

В исходном коде одной программы, внутри NinjectModule, я нашёл такую строку:

176
Бегущая пунктирная рамка у блока

Бегущая пунктирная рамка у блока

Как сделать так, чтобы граница блока была не статичной, а бегала по направлению часовой стрелки? Понимаю, что как-то должно быть с помощью...

217
Съезжают элементы при заполнении формы

Съезжают элементы при заполнении формы

Вот какая проблема при отображении сайта на мобильном устройствеЕсть форма обратной связи, после которой идут КОНТАКТЫ, НО, при заполнении...

190