Se il codice di quell'esempio funziona per voi, è possibile accelerarlo in modo massiccio (per ordini di grandezza) utilizzando Bitmap.LockBits
, che restituisce un oggetto che consente l'accesso ai dati dei pixel della Bitmap tramite puntatori. Ci sono numerosi esempi sul web e su StackOverflow che mostrano come usare LockBits.
Bitmap.SetPixel()
e Bitmap.GetPixel()
sono i metodi più lenti noti all'umanità, ed entrambi utilizzano la classe Color
, che è la classe più lenta nota all'umanità. Avrebbero dovuto essere denominati Bitmap.GetPixelAndByGodYoullBeSorryYouDid()
e Bitmap.SetPixelWhileGettingCoffee
come avvertimento per gli sviluppatori incauti.
Aggiornamento: Se avete intenzione di modificare il codice in quel campione, notare che questo pezzo:
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();
può essere sostituito con questo:
Bitmap NewBitmap = (Bitmap)Image.Clone();
Update 2: Ecco la versione LockBits del metodo AdjustContrast (con alcuni altri miglioramenti della velocità):
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: questo codice richiede using System.Drawing.Imaging;
nelle istruzioni della classe 'using e richiede che l'opzione allow unsafe code
del progetto sia selezionata (nella scheda Proprietà di costruzione per il progetto).
Uno dei motivi per cui GetPixel e SetPixel sono così lenti per le operazioni pixel per pixel è che il sovraccarico della chiamata al metodo inizia a diventare un fattore enorme. Normalmente, il mio esempio di codice qui sarebbe considerato un candidato per il refactoring, dal momento che è possibile scrivere i propri metodi SetPixel e GetPixel che utilizzano un oggetto BitmapData esistente, ma il tempo di elaborazione per la matematica all'interno delle funzioni sarebbe molto piccolo rispetto al sovraccarico del metodo di ogni chiamata. Questo è il motivo per cui ho rimosso anche le chiamate Clamp
nel metodo originale.
Un altro modo per accelerare sarebbe semplicemente renderlo una funzione "distruttiva" e modificare il parametro Bitmap passato invece di fare una copia e restituire la copia modificata.
Perché dici che " quel tipo "si sbaglia? – DkAngelito
@DkAngelito Wow, questo è da molto tempo fa. Se la memoria è utile, l'approccio ColorMatrix non può spostare i valori verso l'esterno/verso il punto centrale, che è ciò che è realmente una regolazione del contrasto. Se aiuta, la risposta accettata da MusicGenesis di seguito sembra aver attratto il consenso come ottimale. –
Il link "Questo ragazzo" è ora rotto. – Chad