Si el código en esa muestra funciona para usted, puede acelerarlo masivamente (en órdenes de magnitud) utilizando Bitmap.LockBits
, que devuelve un objeto BitmapData
que permite el acceso a los datos de píxeles del mapa de bits mediante punteros. Existen numerosas muestras en la web y en StackOverflow que muestran cómo usar LockBits.
Bitmap.SetPixel()
y Bitmap.GetPixel()
son los métodos más lentos conocidos por la humanidad, y ambos utilizan la clase Color
, que es la clase más lenta conocida por la humanidad. Deberían haber sido nombrados Bitmap.GetPixelAndByGodYoullBeSorryYouDid()
y Bitmap.SetPixelWhileGettingCoffee
como una advertencia para los desarrolladores incautos.
Actualización: Si va a modificar el código en esa muestra, cabe destacar que este trozo:
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();
puede ser sustituida por la siguiente:
Bitmap NewBitmap = (Bitmap)Image.Clone();
Actualización 2: Aquí está la versión de LockBits del método AdjustContrast (con algunas otras mejoras de velocidad):
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;
}
NOTA: este código requiere using System.Drawing.Imaging;
en las instrucciones de uso de su clase, y requiere que se verifique la opción allow unsafe code
del proyecto (en la pestaña Propiedades de compilación del proyecto).
Uno de los motivos por los que GetPixel y SetPixel son tan lentos para las operaciones de píxel por píxel es que la sobrecarga de la llamada al método en sí misma comienza a ser un factor importante. Normalmente, mi ejemplo de código aquí sería considerado un candidato para la refactorización, ya que podría escribir sus propios métodos SetPixel y GetPixel que usan un objeto BitmapData existente, pero el tiempo de procesamiento para los cálculos dentro de las funciones sería muy pequeño en relación con el método de cada llamada. Es por eso que eliminé también las llamadas Clamp
en el método original.
Otra forma de acelerar esto sería simplemente convertirla en una función "destructiva" y modificar el parámetro Bitmap pasado en lugar de hacer una copia y devolver la copia modificada.
¿Por qué dice que " ese tipo "se equivoca? – DkAngelito
@DkAngelito Wow, esto es de hace mucho tiempo. Si la memoria sirve, el enfoque de ColorMatrix no puede desplazar los valores lejos/hacia el punto medio, que es lo que realmente es un ajuste de contraste. Si ayuda, la respuesta aceptada por MusicGenesis a continuación parece haber atraído el consenso como óptimo. –
El enlace "Este chico" ahora está roto. – Chad