2015-12-14 7 views
5

Я пытаюсь воссоздать очень простые функции GDI +, такие как масштабирование и поворот изображения. Причина в том, что некоторые функции GDI не могут выполняться на нескольких потоках (я нашел работу с использованием процессов, но не хотел этого делать), и обработка тысяч изображений на одном потоке не была почти разрезанием. Также мои изображения оттенки серого, поэтому пользовательская функция должна будет беспокоиться только о одном значении вместо 4.Как функционирует GDI + так быстро?

Независимо от того, какую функцию я пытаюсь воссоздать, даже если она очень оптимизирована, она всегда в несколько раз медленнее, несмотря на то, что он значительно упрощен по сравнению с тем, что делает GDI (я работаю на 1D массиве байтов, по одному байту на пиксель)

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

Что делает это возможным? Есть ли способ сопоставить его с помощью вашей собственной функции?

+0

Как вы проходите через каждый пиксель? –

+2

[Интерфейс графического устройства] (https://en.wikipedia.org/wiki/Graphics_Device_Interface) должен быть быстрым. Он написан на родном C/C++ и может даже использовать аппаратную функцию графического адаптера для рисования, например. линия. Это будет быстрее, чем ваша итерация на пиксель в C#. Вы можете попытаться достичь почти такой же производительности, если узнаете, как использовать эти функции (например, управляемый DirectX). – Sinatr

+0

Это всего лишь цикл for, который проходит через массив байтов. каждый байт в массиве представляет интенсивность в пикселе. Я думал, что GDI вообще не касается GPU. Если да, то это определенно объяснит это, но я прочитал, что это не так. – Frobot

ответ

3

Код GDI + написан на C/C++ или, возможно, частично в сборке. Некоторые вызовы GDI + могут использовать GDI, старый и хорошо оптимизированный API. Вам будет трудно соответствовать производительности, даже если вы знаете все трюки манипуляции с пикселями.

+0

Yup. По сути, он «обманывает», обращаясь к менее управляемым, более эффективным API-интерфейсам и операциям ОС под капотом. 'List .Sort()' делает то же самое. ;) – Haney

+0

Да, я думаю, это сводится к тому, чтобы быть высоко оптимизированным API, созданным профессионалами, используя некоторые трюки, а также не управляемые. Я все равно буду бить его скорость, используя пользовательскую функцию для нескольких потоков, но не так сильно, как ожидалось. Спасибо за вклад всех – Frobot

+0

@Haney: Из любопытства, что делает 'List .Sort()' вызов? - Я не знаю, что собственный API Windows предоставляет функцию сортировки. –

2

Я добавляю свой собственный ответ вместе с моим кодом, чтобы помочь кому-либо еще, кто может это сделать.

Из комбинации указателей и с использованием приближения синуса и косинуса вместо вызова внешней функции для вращения я пришел довольно близко, чтобы достичь скорости GDI. Никакие внешние функции не вызываются вообще.

Он по-прежнему занимает около 50% больше времени, чем GDI, но моя более ранняя реализация заняла более 10 раз дольше, чем GDI. И когда вы рассматриваете многопоточность, этот метод может быть в 10 раз быстрее, чем GDI. Эта функция может вращать изображение 300x400 за 3 миллисекунды на моей машине.

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

private unsafe byte[] rotate(byte[] input, int inputWidth, int inputHeight, int cx, int cy, double angle) 
    { 
     byte[] result = new byte[input.Length]; 

     int 
      tx, ty, ix, iy, x1, y1; 
     double 
      px, py, fx, fy, sin, cos, v; 
     byte a, b; 

     //Approximate Sine and Cosine of the angle 
     if (angle < 0) 
      sin = 1.27323954 * angle + 0.405284735 * angle * angle; 
     else 
      sin = 1.27323954 * angle - 0.405284735 * angle * angle; 
     angle += 1.57079632; 
     if (angle > 3.14159265) 
      angle -= 6.28318531; 
     if (angle < 0) 
      cos = 1.27323954 * angle + 0.405284735 * angle * angle; 
     else 
      cos = 1.27323954 * angle - 0.405284735 * angle * angle; 
     angle -= 1.57079632; 


     fixed (byte* pInput = input, pResult = result) 
     { 
      byte* pi = pInput; 
      byte* pr = pResult; 

      for (int x = 0; x < inputWidth; x++) 
       for (int y = 0; y < inputHeight; y++) 
       { 
        tx = x - cx; 
        ty = y - cy; 
        px = tx * cos - ty * sin + cx; 
        py = tx * sin + ty * cos + cy; 
        ix = (int)px; 
        iy = (int)py; 
        fx = px - ix; 
        fy = py - iy; 

        if (ix < inputWidth && iy < inputHeight && ix >= 0 && iy >= 0) 
        { 
         //keep in array bounds 
         x1 = ix + 1; 
         y1 = iy + 1; 
         if (x1 >= inputWidth) 
          x1 = ix; 
         if (y1 >= inputHeight) 
          y1 = iy; 

         //bilinear interpolation using pointers 
         a = *(pInput + (iy * inputWidth + ix)); 
         b = *(pInput + (y1 * inputWidth + ix)); 
         v = a + ((*(pInput + (iy * inputWidth + x1)) - a) * fx); 
         pr = (pResult + (y * inputWidth + x)); 
         *pr = (byte)(v + (((b + ((*(pInput + (y1 * inputWidth + x1)) - b) * fx)) - v) * fy)); 
        } 
       } 
     } 

     return result; 
    } 
+0

Я вижу пару очень незначительных твиков, которые вы могли бы сделать, например, перемещение 'tx = x - cx;' и двух связанных терминов 'tx * cos' и' tx * sin' из внутреннего гнезда петли, но последнее требуют больше темпов, поэтому вам нужно будет проверить, стоит ли что-то подобное. Я также задаюсь вопросом, может ли переключение на нечеткое замыкание логической оценки в вашем 'if' может дать небольшой импульс. Но в целом я думаю, что вы близки к тому, что можно достичь на этом уровне (если не существует совершенно другого подхода, о котором я не знаю). –

+0

Еще одна вещь, которую вы могли бы попробовать, использовать [матрицу преобразования] (https://en.wikipedia.org/wiki/Transformation_matrix), чтобы применить поворот к изображению. Вы также можете использовать пакет NuGet [System.Numerics.Vectors] (https://www.nuget.org/packages/System.Numerics.Vectors), чтобы получить аппаратные ускоренные версии некоторых методов Matrix, чтобы сделать их еще быстрее. –

 Смежные вопросы

  • Нет связанных вопросов^_^