2012-01-24 28 views
5

ho uno strumento con controlli a cursore trackbar utilizzati per regolare di un'immagine di luminosità, contrasto, gamma, eccalgoritmo di contrasto più veloce per una bitmap

Sto cercando di ottenere aggiornamenti in tempo reale per la mia immagine mentre l'utente trascina il cursore. Gli algoritmi di luminosità e gamma sono una velocità accettabile (circa 170 ms). Ma l'algoritmo di contrasto è di circa 380 ms.

Fondamentalmente il mio modulo è una finestra degli strumenti con cursori. Ogni volta che l'immagine viene aggiornata, invia un evento al genitore che ridisegna la nuova immagine. La finestra degli strumenti mantiene l'immagine originale non modificata bloccata in memoria, quindi ho sempre accesso ai suoi byte. In pratica, lo faccio ogni volta che viene modificato l'evento ValueChanged per un cursore (come il cursore Contrast).

  • LockBits della bitmap di lavoro (destinazione) come Format24bppRgb (bitmap originale è in Format32bppPArgb)
  • Marshal.Copy i bit a un byte [] array
  • Controllare che il funzionamento che sto facendo (che slitta è stato scelto)
  • utilizzare il seguente codice per il Contrasto:

codice:

double newValue = 0; 
double c = (100.0 + contrast)/100.0; 

c *= c; 

for (int i = 0; i < sourcePixels.Length; i++) 
{ 
    newValue = sourcePixels[i]; 

    newValue /= 255.0; 
    newValue -= 0.5; 
    newValue *= c; 
    newValue += 0.5; 
    newValue *= 255; 

    if (newValue < 0) 
     newValue = 0; 
    if (newValue > 255) 
     newValue = 255; 

    destPixels[i] = (byte)newValue; 
} 

Ho letto una volta sull'utilizzo di numeri interi anziché di valori in virgola mobile per aumentare la velocità di contrasto, ma non sono riuscito a trovare di nuovo l'articolo.

Ho provato a utilizzare codice non sicuro (puntatori) ma in realtà ho notato una diminuzione della velocità. Presumo che fosse perché il codice stava usando nested per loops per iterare xey invece di un singolo loop.

+0

possibile duplicato di [Regola il contrasto di un'immagine in C# in modo efficiente] (http://stackoverflow.com/questions/3115076/aggiudi-il-contrasto-di-animage-in-c-sharp-efficientemente) – Magnus

+0

Il codice non sicuro nella domanda a cui ti sei collegato, quando lo testo con la stessa immagine che stavo usando con il mio codice, richiede oltre 900ms per routine. Ovviamente l'ho modificato in modo che non clonasse o crei nuove bitmap, è solo il ciclo annidato con puntatori e matematica a virgola mobile. È troppo lento –

+1

Forse è possibile modificare il codice per utilizzare i puntatori invece nel codice non sicuro. – Magnus

risposta

10

A seconda della macchina su cui si sta eseguendo, la tecnica potrebbe essere piuttosto lenta. Se stai usando un sistema ARM senza FPU, ognuna di queste operazioni richiederebbe un po 'di tempo. Poiché si applica la stessa operazione a ogni byte, una tecnica più rapida sarebbe quella di creare una tabella di ricerca a 256 voci per il livello di contrasto e quindi tradurre ogni byte dell'immagine attraverso la tabella. Il tuo ciclo sarebbe quindi simile:

byte contrast_lookup[256]; 
double newValue = 0; 
double c = (100.0 + contrast)/100.0; 

c *= c; 

for (int i = 0; i < 256; i++) 
{ 
    newValue = (double)i; 
    newValue /= 255.0; 
    newValue -= 0.5; 
    newValue *= c; 
    newValue += 0.5; 
    newValue *= 255; 

    if (newValue < 0) 
     newValue = 0; 
    if (newValue > 255) 
     newValue = 255; 
    contrast_lookup[i] = (byte)newValue; 
} 

for (int i = 0; i < sourcePixels.Length; i++) 
{ 
    destPixels[i] = contrast_lookup[sourcePixels[i]]; 
} 
+1

Molto più veloce. Ciò ha ridotto la velocità da circa 380 a circa 155ms. Grazie! –

+1

Vale la pena notare che è possibile applicare lo stesso approccio anche agli algoritmi di luminosità e gamma per renderli più veloci. – Seph

+0

Ho pensato così e stavo per provarlo. Questo metodo si adatta molto bene con bitmap ad alta risoluzione come i file 2976x1536 con cui ho a che fare. –

3

@BitBank risponde alla tua domanda come richiesto, ho voluto aggiungere che se siete dopo le prestazioni si dovrebbe considerare il codice che sta ottenendo i dati dei pixel e l'impostazione in seguito.

codice di lavoro completa utilizzando i puntatori (puntelli per @BitBank sul codice for anello):

private unsafe void ApplyContrast(double contrast, Bitmap bmp) 
{ 
    byte[] contrast_lookup = new byte[256]; 
    double newValue = 0; 
    double c = (100.0 + contrast)/100.0; 

    c *= c; 

    for (int i = 0; i < 256; i++) 
    { 
     newValue = (double)i; 
     newValue /= 255.0; 
     newValue -= 0.5; 
     newValue *= c; 
     newValue += 0.5; 
     newValue *= 255; 

     if (newValue < 0) 
      newValue = 0; 
     if (newValue > 255) 
      newValue = 255; 
     contrast_lookup[i] = (byte)newValue; 
    } 

    var bitmapdata = bmp.LockBits(new Rectangle(0, 0, bmp.Width, bmp.Height), 
     System.Drawing.Imaging.ImageLockMode.ReadWrite, System.Drawing.Imaging.PixelFormat.Format32bppArgb); 

    int PixelSize = 4; 

    for (int y = 0; y < bitmapdata.Height; y++) 
    { 
     byte* destPixels = (byte*)bitmapdata.Scan0 + (y * bitmapdata.Stride); 
     for (int x = 0; x < bitmapdata.Width; x++) 
     { 
      destPixels[x * PixelSize] = contrast_lookup[destPixels[x * PixelSize]]; // B 
      destPixels[x * PixelSize + 1] = contrast_lookup[destPixels[x * PixelSize + 1]]; // G 
      destPixels[x * PixelSize + 2] = contrast_lookup[destPixels[x * PixelSize + 2]]; // R 
      //destPixels[x * PixelSize + 3] = contrast_lookup[destPixels[x * PixelSize + 3]]; //A 
     } 
    } 
    bmp.UnlockBits(bitmapdata); 
} 

Se stai impostando i dati pixel di immagine utilizzando Marshal.Copy troverete questo comporta meglio.

Questo dovrebbe funzionare più velocemente del codice corrente, e si riduce anche l'ingombro di memoria che è buono quando si tratta di immagini molto grandi.

+0

Potrebbe anche essere un po 'più veloce se si scala da e torna a 255. I.E. newValue = (double) i; newValue - = 128; newValue * = c; newValue + = 128; – DkAngelito

+0

Un altro grande miglioramento sarebbe quello di calcolare x * PixelSize una volta invece di 6 volte nell'interno per – DkAngelito