Более быстрый алгоритм контрастности для растрового изображения - PullRequest
5 голосов
/ 24 января 2012

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

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

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

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

Код:

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;
}

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

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

Ответы [ 2 ]

10 голосов
/ 24 января 2012

В зависимости от машины, на которой вы работаете, ваша техника может быть довольно медленной.Если вы используете систему 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]];
}
3 голосов
/ 25 января 2012

@ 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, вы обнаружите, что это работает лучше.

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

...