Domanda

Nel mio C # (3,5) applicazione che ho bisogno di ottenere i valori medi di colore per i canali rosso, verde e blu di una bitmap. Preferibilmente senza l'utilizzo di una libreria esterna. Può essere fatto? Se é cosi, come? Grazie in anticipo.

Il tentativo di rendere le cose un po 'più preciso: ogni pixel nel bitmap ha un certo valore di colore RGB. Mi piacerebbe ottenere i valori medi RGB per tutti i pixel dell'immagine.

È stato utile?

Soluzione

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 prove di questo codice ... (forse ho tagliare alcune curve)

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

Altri suggerimenti

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 usa DrawImage copiare il bitmap originale in un bitmap 1-pixel. I valori RGB del pixel 1 saranno poi rappresentano le medie per l'intero originale. GetPixel è relativamente lento, ma solo quando lo si utilizza su un grande Bitmap, pixel per pixel. Chiamarlo una volta qui non disperatevi.

Utilizzando LockBits è davvero veloce, ma alcuni utenti di Windows hanno politiche di sicurezza che impediscono l'esecuzione di codice "non sicuro". Dico questo perché questo fatto appena mi ha morso sul sedere di recente.

Aggiorna : con InterpolationMode impostato HighQualityBicubic, questo metodo richiede circa il doppio del tempo come una media con LockBits; con HighQualityBilinear, richiede solo leggermente più lungo LockBits. Quindi, a meno che gli utenti hanno una politica di sicurezza che vieta unsafe codice, sicuramente non utilizzare il mio metodo.

Aggiornamento 2: con il passare del tempo, ora mi rendo conto perché questo approccio non funziona affatto. Anche gli algoritmi di interpolazione di altissima qualità incorporano solo pochi pixel vicini, quindi non c'è un limite a quanto un'immagine può essere schiacciato verso il basso senza perdere informazioni. E schiacciando un immagine verso il basso per un pixel è ben al di là di questo limite, non importa quale si utilizza l'algoritmo.

L'unico modo per farlo sarebbe quello di ridurre l'immagine in passi (forse contrazione è della metà ogni volta) fino ad arrivare fino alla dimensione di un pixel. Non posso esprimere a parole quello che sarebbe stato uno spreco totale di tempo a scrivere qualcosa di simile, quindi sono contento di me stesso mi sono fermato quando ho pensato di esso. :)

, nessuno vota per questa risposta più -. Potrebbe essere la mia idea più stupida mai

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);
}
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top