Отрегулируйте контрастность изображения в C # эффективно - PullRequest
9 голосов
/ 25 июня 2010

Есть ли эффективный способ регулировки контрастности изображения в C #?

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

Я уже использую цветовые матрицы в некоторых местах и ​​нахожу их быстрыми.Есть ли способ настроить контраст, используя их?(Примечание: Этот парень ошибается.)

Я также использую EmguCV.Я замечаю, что OpenCV (который обертывает Emgu) , кажется, имеет функцию контраста - есть ли способ получить доступ к этому через Emgu?В настоящий момент все, что я могу сделать в Эмгу, это нормализовать гистограмму, которая меняет контраст, но не с какой-либо степенью контроля с моей стороны.

У кого-нибудь есть идеи?

Ответы [ 3 ]

30 голосов
/ 25 июня 2010

Если код в этом примере работает для вас, вы можете значительно его ускорить (на порядки), используя Bitmap.LockBits, который возвращает объект BitmapData, который позволяет получить доступ к пиксельным данным растрового изображения через указатели.В Интернете и в StackOverflow имеется множество примеров, показывающих, как использовать LockBits.

Bitmap.SetPixel() и Bitmap.GetPixel() - самые медленные методы, известные человечеству, и оба они используют класс Color, которыйсамый медленный класс, известный человечеству.Они должны были называться Bitmap.GetPixelAndByGodYoullBeSorryYouDid() и Bitmap.SetPixelWhileGettingCoffee в качестве предупреждения для неосторожных разработчиков.

Обновление: Если вы собираетесь изменить код в этом примере, обратите внимание, что этот блок:

System.Drawing.Bitmap TempBitmap = Image;
System.Drawing.Bitmap NewBitmap = new System.Drawing.Bitmap(TempBitmap.Width,
    TempBitmap.Height);
System.Drawing.Graphics NewGraphics = 
    System.Drawing.Graphics.FromImage(NewBitmap);
NewGraphics.DrawImage(TempBitmap, new System.Drawing.Rectangle(0, 0, 
    TempBitmap.Width, TempBitmap.Height), 
    new System.Drawing.Rectangle(0, 0, TempBitmap.Width, TempBitmap.Height),
    System.Drawing.GraphicsUnit.Pixel);
NewGraphics.Dispose();

можно заменить на это:

Bitmap NewBitmap = (Bitmap)Image.Clone();

Обновление 2: Вот версия LockBits метода AdjustContrast (с некоторыми другими улучшениями скорости)):

public static Bitmap AdjustContrast(Bitmap Image, float Value)
{
    Value = (100.0f + Value) / 100.0f;
    Value *= Value;
    Bitmap NewBitmap = (Bitmap)Image.Clone();
    BitmapData data = NewBitmap.LockBits(
        new Rectangle(0, 0, NewBitmap.Width, NewBitmap.Height), 
        ImageLockMode.ReadWrite,
        NewBitmap.PixelFormat);
    int Height = NewBitmap.Height;
    int Width = NewBitmap.Width;

    unsafe
    {
        for (int y = 0; y < Height; ++y)
        {
            byte* row = (byte*)data.Scan0 + (y * data.Stride);
            int columnOffset = 0;
            for (int x = 0; x < Width; ++x)
            {
                byte B = row[columnOffset];
                byte G = row[columnOffset + 1];
                byte R = row[columnOffset + 2];

                float Red = R / 255.0f;
                float Green = G / 255.0f;
                float Blue = B / 255.0f;
                Red = (((Red - 0.5f) * Value) + 0.5f) * 255.0f;
                Green = (((Green - 0.5f) * Value) + 0.5f) * 255.0f;
                Blue = (((Blue - 0.5f) * Value) + 0.5f) * 255.0f;

                int iR = (int)Red;
                iR = iR > 255 ? 255 : iR;
                iR = iR < 0 ? 0 : iR;
                int iG = (int)Green;
                iG = iG > 255 ? 255 : iG;
                iG = iG < 0 ? 0 : iG;
                int iB = (int)Blue;
                iB = iB > 255 ? 255 : iB;
                iB = iB < 0 ? 0 : iB;

                row[columnOffset] = (byte)iB;
                row[columnOffset + 1] = (byte)iG;
                row[columnOffset + 2] = (byte)iR;

                columnOffset += 4;
            }
        }
    }

    NewBitmap.UnlockBits(data);

    return NewBitmap;
}

ПРИМЕЧАНИЕ: этот код требует using System.Drawing.Imaging; в выражениях using вашего класса и требует, чтобы опция проекта allow unsafe code была отмечена (на вкладке Build Properties для проекта).

Одна из причин, по которой GetPixel и SetPixel настолько медленны для попиксельных операций, состоит в том, что издержки самого вызова метода начинают становиться огромным фактором.Как правило, мой пример кода здесь будет рассматриваться как кандидат на рефакторинг, поскольку вы можете написать свои собственные методы SetPixel и GetPixel, которые используют существующий объект BitmapData, но время обработки математических операций внутри функций будет очень небольшим по сравнению с издержками методакаждого звонка.Вот почему я также удалил вызовы Clamp в исходном методе.

Еще один способ ускорить это - просто сделать его «деструктивной» функцией и изменить переданный параметр Bitmap вместосоздание копии и возврат измененной копии.

2 голосов
/ 16 декабря 2010

@ MusiGenesis

Просто хотел отметить, что я использовал этот метод для редактора изображений, который я писал. Это работает хорошо, но иногда этот метод вызывает AccessViolationException в этой строке:

byte B = row[columnOffset];

Я понял, что это потому, что не было стандартизации BitDepth, поэтому, если изображение было 32-битным цветом, я получал эту ошибку. Поэтому я изменил эту строку:

BitmapData data = NewBitmap.LockBits(new Rectangle(0, 0, NewBitmap.Width, NewBitmap.Height),  ImageLockMode.ReadWrite, NewBitmap.PixelFormat);

до:

BitmapData data = NewBitmap.LockBits(new Rectangle(0, 0, NewBitmap.Width, NewBitmap.Height), ImageLockMode.ReadWrite, PixelFormat.Format32bppRgb); 

Надеюсь, это поможет, так как, кажется, искоренил мою проблему.

Спасибо за пост.

Консольный

0 голосов
/ 22 февраля 2011

Я немного опоздал, но использую цветовую реализацию матрицы, поскольку она будет оптимизирована для таких преобразований и намного проще, чем манипулирование пикселями самостоятельно: http://www.geekpedia.com/tutorial202_Using-the-ColorMatrix-in-Csharp.html

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...