2010-06-25 12 views
6

Esiste un modo efficace per regolare il contrasto di un'immagine in C#?Regola il contrasto di un'immagine in C# in modo efficiente

Ho visto this article che consiglia di eseguire un'operazione per pixel. Non veloce

Sto usando matrici di colori già in posti e li trovo rapidi. C'è un modo per regolare il contrasto usando questi? (Nota: This guy si sbaglia.)

Sto anche utilizzando EmguCV. Ho notato che OpenCV (che Emgu esegue) seems to have a contrast function - esiste un modo per accedere a questo tramite Emgu? Al momento tutto quello che posso fare in Emgu è normalizzare l'istogramma, che cambia il contrasto, ma non con alcun grado di controllo da parte mia.

Qualcuno ha qualche idea?

+0

Perché dici che " quel tipo "si sbaglia? – DkAngelito

+0

@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. –

+0

Il link "Questo ragazzo" è ora rotto. – Chad

risposta

26

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.

+2

Immagino che tu abbia il compito di nominare cose in cui lavori? –

+0

Giusto, come * I * ho un lavoro. :) – MusiGenesis

+0

+1 bravo

2

@MusiGenesis,

Volevo solo sottolineare che ho usato questo metodo per un editor di immagini che ho scritto.Funziona bene, ma a volte questo metodo provoca un'AccessViolationException su questa linea:

byte B = row[columnOffset]; 

ho capito che era perché non c'era la standardizzazione di profondità in bit, quindi se un'immagine è rimasta a 32 bit di colore mi è stato sempre questo errore. Così ho cambiato questa linea:

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

a:

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

Spero che questo aiuti come sembra aver sradicato il mio problema.

Grazie per il post.

Jib

+1

Potrebbe essere preferibile utilizzare 'PixelFormat.Format32bppArgb', poiché questo è il formato predefinito di bitmap .NET. – MusiGenesis

-1

Sono un po 'in ritardo, ma uso un'implementazione matrice di colori in quanto questi saranno ottimizzati per queste trasformazioni, ed è molto più facile che la manipolazione dei pixel da soli: http://www.geekpedia.com/tutorial202_Using-the-ColorMatrix-in-Csharp.html

+1

Questo è lo stesso errore che Bob fa nell'articolo a cui mi collego nella mia domanda: le matrici di colore non sono adatte per le alterazioni di contrasto. Questo perché le modifiche sono fatte rispetto al grigio medio, non al nero o al bianco. –

+0

Solo per correggere questo, 'new float [] {0.1f, 0, 0, 0, 0}, new float [] {0, 0.1f, 0, 0, 0}, new float [] {0 , 0, 0.1f, 0, 0}, nuovo float [] {0, 0, 0, 1, 0}, nuovo float [] {0.8f, 0.8f, 0.8f, 0, 1} ' fa il lavoro nell'esempio sopra. La quinta riga aggiunge un offset che può essere regolato. Questo (0.8) dà un effetto "high key". 0,2 è un contrasto molto basso. – nick66

Problemi correlati