2009-07-01 17 views
14

Nella mia applicazione C# (3.5) ho bisogno di ottenere i valori di colore medi per i canali rosso, verde e blu di una bitmap. Preferibilmente senza usare una libreria esterna. Può essere fatto? Se é cosi, come? Grazie in anticipo.Come calcolare i valori cromatici rgb medi di una bitmap

Provare a rendere le cose un po 'più precise: ogni pixel nella bitmap ha un determinato valore di colore RGB. Mi piacerebbe ottenere i valori RGB medi per tutti i pixel nell'immagine.

+2

Bene, il metodo ingenuo sarebbe quello di andare pixel per pixel e ottenere i valori RGB, che sono sicuro non è quello che stai chiedendo. Puoi elaborare il tipo di media che stai cercando? –

+0

Hai ragione. Spero sia meglio ora. – Mats

+0

L'andare pixel per pixel può essere fatto in modo diverso - vedi le risposte. Mi chiedo se la GPU potrebbe aiutare. –

risposta

20

Il modo più veloce è quello di utilizzare codice non sicuro:

BitmapData srcData = bm.LockBits(
      new Rectangle(0, 0, bm.Width, bm.Height), 
      ImageLockMode.ReadOnly, 
      PixelFormat.Format32bppArgb); 

int stride = srcData.Stride; 

IntPtr Scan0 = srcData.Scan0; 

long[] totals = new long[] {0,0,0}; 

int width = bm.Width; 
int height = bm.Height; 

unsafe 
{ 
    byte* p = (byte*) (void*) Scan0; 

    for (int y = 0; y < height; y++) 
    { 
    for (int x = 0; x < width; x++) 
    { 
     for (int color = 0; color < 3; color++) 
     { 
     int idx = (y*stride) + x*4 + color; 

     totals[color] += p[idx]; 
     } 
    } 
    } 
} 

int avgB = totals[0]/(width*height); 
int avgG = totals[1]/(width*height); 
int avgR = totals[2]/(width*height); 

Attenzione: non ho la prova questo codice ... (forse ho tagliare alcune curve)

Questo codice anche assume un'immagine a 32 bit. Per immagini a 24 bit. Modificare il x * 4 a x * 3

+0

I * ha rubato * il codice sopra, l'ho risolto avvolto in una funzione per rispondere ad un'altra domanda SO su http://stackoverflow.com/questions/6177499/how-to-determine-the-background-color- of-document-when-there-are-3-options-using/6185448 # 6185448 – Till

+3

Puoi spiegare perché è molto più veloce? –

+0

@ C.Ross Credo che sia perché convertiamo tutti i dati dell'immagine in byte e accediamo alla matrice di byte invece di invocare e funzione per determinare il rbg in un dato punto. Costo di questa altezza * larghezza + imagedata-> byte. Costo di altra altezza * larghezza * imagedata-> rbg. –

7

Questo genere di cose funzionerà ma potrebbe non essere abbastanza veloce per essere così utile.

public static Color getDominantColor(Bitmap bmp) 
{ 

     //Used for tally 
     int r = 0; 
     int g = 0; 
     int b = 0; 

    int total = 0; 

    for (int x = 0; x < bmp.Width; x++) 
    { 
      for (int y = 0; y < bmp.Height; y++) 
      { 
       Color clr = bmp.GetPixel(x, y); 

       r += clr.R; 
       g += clr.G; 
       b += clr.B; 

       total++; 
      } 
    } 

    //Calculate average 
    r /= total; 
    g /= total; 
    b /= total; 

    return Color.FromArgb(r, g, b); 
} 
+0

Sarà lento. Provalo e bastaQuello che in realtà GetPixel() blocca la bitmap e recupera un singolo pixel. –

+0

funziona perfettamente – garik

11

Ecco un modo molto più semplice:

Bitmap bmp = new Bitmap(1, 1); 
Bitmap orig = (Bitmap)Bitmap.FromFile("path"); 
using (Graphics g = Graphics.FromImage(bmp)) 
{ 
    // updated: the Interpolation mode needs to be set to 
    // HighQualityBilinear or HighQualityBicubic or this method 
    // doesn't work at all. With either setting, the results are 
    // slightly different from the averaging method. 
    g.InterpolationMode = InterpolationMode.HighQualityBicubic; 
    g.DrawImage(orig, new Rectangle(0, 0, 1, 1)); 
} 
Color pixel = bmp.GetPixel(0, 0); 
// pixel will contain average values for entire orig Bitmap 
byte avgR = pixel.R; // etc. 

In sostanza, si utilizza DrawImage per copiare il bitmap originale in un bitmap 1-pixel. I valori RGB di quel 1 pixel rappresenteranno quindi le medie per l'intero originale. GetPixel è relativamente lento, ma solo quando lo si utilizza su una grande Bitmap, pixel per pixel. Chiamarlo una volta qui non è un problema.

L'utilizzo di LockBits è davvero veloce, ma alcuni utenti di Windows dispongono di criteri di sicurezza che impediscono l'esecuzione di codice "non sicuro". Lo dico perché questo fatto mi ha un po 'preso di recente.

Aggiornamento: con InterpolationMode impostato su HighQualityBicubic, questo metodo richiede circa il doppio del calcolo della media con LockBits; con HighQualityBilinear, richiede solo un po 'più lungo di LockBits. Quindi, a meno che i tuoi utenti non dispongano di una politica di sicurezza che vieta il codice unsafe, sicuramente non usare il mio metodo.

Aggiornamento 2: con il passare del tempo, ora capisco perché questo approccio non funziona affatto. Anche gli algoritmi di interpolazione di alta qualità incorporano solo pochi pixel vicini, quindi c'è un limite a quanto un'immagine può essere schiacciata senza perdere informazioni. E schiacciare un'immagine fino a un pixel è ben oltre questo limite, indipendentemente dall'algoritmo che si utilizza.

L'unico modo per farlo sarebbe quello di restringere l'immagine in passaggi (magari ridurla di metà ogni volta) fino a ridurla alla dimensione di un pixel. Non riesco a esprimere a parole ciò che sarebbe una totale perdita di tempo scrivere qualcosa del genere, quindi sono contento di essermi fermato quando ci ho pensato. :)

Per favore, nessuno vota più per questa risposta - potrebbe essere la mia più stupida idea di sempre.

+0

lol, interessante. Mi chiedo se è più veloce del metodo LockBits? (Avrei tempo da solo i due ma sono LENTRO! Lol) +1 – CodeAndCats

+0

+1 Genio puro! –

+0

Non ho potuto resistere nonostante la richiesta esplicita di non upvotare. Ho imparato molto leggendo la tua risposta. – prokilomer

Problemi correlati