2010-06-25 3 views
6

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

Я видел this article, который выступает за выполнение операций с пикселями. Не быстро.

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

Я также использую EmguCV. Я замечаю, что OpenCV (который покрывает Emgu) seems to have a contrast function - есть ли способ доступа к этому через Emgu? На данный момент все, что я могу сделать в Emgu, нормализует гистограмму, которая меняет контраст, но не с какой-либо степенью контроля с моей стороны.

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

+0

Почему вы говорите, что " этот парень "ошибается? – DkAngelito

+0

@DkAngelito Вау, это уже давно. Если память используется, подход ColorMatrix не может смещать значения в сторону/в сторону середины, что и есть на самом деле. Если это поможет, принятый ответ MusicGenesis ниже, по-видимому, привлекает консенсус как оптимальный. –

+0

Ссылка "This guy" теперь нарушена. – Chad

ответ

26

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

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

Update: Если вы собираетесь изменить код в этом образце, обратите внимание, что этот фрагмент:

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(); 

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

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; в ваших инструкциях с использованием класса, и для этого требуется проверка опции проекта allow unsafe code (на вкладке «Свойства сборки» для проекта).

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

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

+2

Насколько я понимаю, вы отвечаете за то, что вы намереваетесь называть вещи, где вы работаете? –

+0

Правильно, как * У меня есть работа. :) – MusiGenesis

+0

+1 bravo

2

@MusiGenesis,

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

byte B = row[columnOffset]; 

я понял, что это произошло потому, что не было никакой стандартизации глубины цвета, поэтому если изображение 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); 

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

Спасибо за сообщение.

Jib

+1

Вместо этого вы можете использовать' PixelFormat.Format32bppArgb', так как это формат растрового формата .NET по умолчанию. – MusiGenesis

-1

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

+1

Это та самая ошибка, которую Боб делает в статье, на которую я ссылаюсь в своем вопросе: Цветовые матрицы не подходят для контрастных изменений. Это связано с тем, что изменения сделаны относительно среднего серого, а не черного или белого. –

+0

Просто исправить это, 'new float [] {0.1f, 0, 0, 0, 0}, new float [] {0, 0.1f, 0, 0, 0}, new float [] {0 , 0, 0.1f, 0, 0}, new float [] {0, 0, 0, 1, 0}, новый плавающий [] {0.8f, 0.8f, 0.8f, 0, 1} ' работа в приведенном выше примере. Пятая строка добавляет смещение, которое можно отрегулировать. Это (0.8) дает эффект «с высоким ключом». 0,2 очень низкий контраст. – nick66

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