Если код в этом примере работает для вас, вы можете ускорить его массово (по порядку) с помощью 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 вместо создания копии и возврата измененной копии.
Почему вы говорите, что " этот парень "ошибается? – DkAngelito
@DkAngelito Вау, это уже давно. Если память используется, подход ColorMatrix не может смещать значения в сторону/в сторону середины, что и есть на самом деле. Если это поможет, принятый ответ MusicGenesis ниже, по-видимому, привлекает консенсус как оптимальный. –
Ссылка "This guy" теперь нарушена. – Chad