2012-01-24 6 views
5

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

Я пытаюсь получить обновления в реальном времени, чтобы мой образ, когда пользователь перетаскивает слайдер. Яркость и гамма-алгоритмы являются приемлемой скоростью (около 170 мс). Но контрастный алгоритм составляет около 380 мс.

В основном моя форма - это окно с инструментами с ползунками. Каждый раз, когда изображение обновляется, оно отправляет событие родительскому объекту, который перерисовывает новое изображение. Окно инструмента сохраняет исходное немодифицированное изображение в памяти, поэтому я всегда имею доступ к его байтам. Поэтому в основном я делаю это каждый раз, когда изменяется событие ValueChanged для слайдера (например, слайдер Contrast).

  • LockBits из рабочего (назначения) растрового изображения в качестве Format24bppRgb (исходное растровое изображение в Format32bppPArgb)
  • Marshal.Copy биты в байте [] массив
  • Проверьте, какую операцию я делаю (что слайдер был выбран)
  • Используйте следующий код для контраста:

код:

double newValue = 0; 
double c = (100.0 + contrast)/100.0; 

c *= c; 

for (int i = 0; i < sourcePixels.Length; i++) 
{ 
    newValue = sourcePixels[i]; 

    newValue /= 255.0; 
    newValue -= 0.5; 
    newValue *= c; 
    newValue += 0.5; 
    newValue *= 255; 

    if (newValue < 0) 
     newValue = 0; 
    if (newValue > 255) 
     newValue = 255; 

    destPixels[i] = (byte)newValue; 
} 

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

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

+0

Возможный дубликат [Настроить контраст изображения на C# эффективно] (http://stackoverflow.com/questions/3115076/adjust-the-contrast-of-an-image-in-c-sharp-efficiently) – Magnus

+0

Небезопасный код в вопросе, с которым вы связались, когда я тестирую его с тем же изображением, что и с моим кодом, занимает более 900ms за рутину. Конечно, я модифицировал его так, чтобы он не клонировал или не создавал новые растровые изображения, это всего лишь вложенный цикл с указателями и математикой с плавающей запятой. Это слишком медленно. –

+1

Возможно, вы можете изменить свой код, чтобы использовать указатели вместо этого в небезопасном коде. – Magnus

ответ

10

В зависимости от машины вы используете это, ваша техника может быть довольно медленной. Если вы используете систему ARM без FPU, каждая из этих операций займет довольно много времени. Поскольку вы применяете одну и ту же операцию к каждому байту, более быстрая техника заключалась бы в создании таблицы поиска с 256 входами для уровня контрастности, а затем перевода каждого байта изображения через таблицу. Ваш цикл будет выглядеть так:

byte contrast_lookup[256]; 
double newValue = 0; 
double c = (100.0 + contrast)/100.0; 

c *= c; 

for (int i = 0; i < 256; i++) 
{ 
    newValue = (double)i; 
    newValue /= 255.0; 
    newValue -= 0.5; 
    newValue *= c; 
    newValue += 0.5; 
    newValue *= 255; 

    if (newValue < 0) 
     newValue = 0; 
    if (newValue > 255) 
     newValue = 255; 
    contrast_lookup[i] = (byte)newValue; 
} 

for (int i = 0; i < sourcePixels.Length; i++) 
{ 
    destPixels[i] = contrast_lookup[sourcePixels[i]]; 
} 
+1

Гораздо быстрее. Это ускорило скорость от примерно 380 мс до примерно 155 мс. Спасибо! –

+1

Стоит отметить, что вы также можете применить этот же подход к вашим алгоритмам яркости и гаммы, чтобы сделать их еще быстрее. – Seph

+0

Я так и думал, и я собирался попробовать. Этот метод очень хорошо масштабируется с растровыми изображениями высокого разрешения, такими как файлы 2976x1536, с которыми я имею дело. –

3

@BitBank отвечает на ваш вопрос, как просили, я хотел бы добавить, что если вы после выполнения вы должны рассмотреть ваш код, который должен получить пиксельные данные и настройки его впоследствии.

Полный рабочий код с использованием указателей (реквизит @BitBank на коде в for цикла):

private unsafe void ApplyContrast(double contrast, Bitmap bmp) 
{ 
    byte[] contrast_lookup = new byte[256]; 
    double newValue = 0; 
    double c = (100.0 + contrast)/100.0; 

    c *= c; 

    for (int i = 0; i < 256; i++) 
    { 
     newValue = (double)i; 
     newValue /= 255.0; 
     newValue -= 0.5; 
     newValue *= c; 
     newValue += 0.5; 
     newValue *= 255; 

     if (newValue < 0) 
      newValue = 0; 
     if (newValue > 255) 
      newValue = 255; 
     contrast_lookup[i] = (byte)newValue; 
    } 

    var bitmapdata = bmp.LockBits(new Rectangle(0, 0, bmp.Width, bmp.Height), 
     System.Drawing.Imaging.ImageLockMode.ReadWrite, System.Drawing.Imaging.PixelFormat.Format32bppArgb); 

    int PixelSize = 4; 

    for (int y = 0; y < bitmapdata.Height; y++) 
    { 
     byte* destPixels = (byte*)bitmapdata.Scan0 + (y * bitmapdata.Stride); 
     for (int x = 0; x < bitmapdata.Width; x++) 
     { 
      destPixels[x * PixelSize] = contrast_lookup[destPixels[x * PixelSize]]; // B 
      destPixels[x * PixelSize + 1] = contrast_lookup[destPixels[x * PixelSize + 1]]; // G 
      destPixels[x * PixelSize + 2] = contrast_lookup[destPixels[x * PixelSize + 2]]; // R 
      //destPixels[x * PixelSize + 3] = contrast_lookup[destPixels[x * PixelSize + 3]]; //A 
     } 
    } 
    bmp.UnlockBits(bitmapdata); 
} 

Если вы настраиваете ваши данные изображения пикселей с помощью Marshal.Copy вы найдете это работает лучше.

Это должно работать быстрее, чем ваш текущий код, а также уменьшает площадь памяти, которая хороша при работе с очень большими изображениями.

+0

Это может быть немного быстрее, если вы нанесете масштаб и вернетесь к 255. I.E. newValue = (double) i; newValue - = 128; newValue * = c; newValue + = 128; – DkAngelito

+0

Еще одним большим улучшением было бы рассчитать x * PixelSize один раз вместо 6 раз во внутреннем для – DkAngelito