Come calcolare i valori di colore rgb medi di una bitmap

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.

Cercando di 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.

Il modo più veloce è utilizzare un 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 testato questo codice ... (potrei aver tagliato qualche angolo)

Questo codice assorbe anche un'immagine a 32 bit. Per immagini a 24 bit. Cambia x * 4 a x * 3

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. 

Fondamentalmente, si utilizza DrawImage per copiare la bitmap originale in una bitmap da 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 impiega circa il doppio 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 ridurre l’immagine a passi (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.

Questo tipo 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); }