Question

Dans mon application C # (3.5) Je dois obtenir les valeurs de couleur moyenne pour les canaux rouge, vert et bleu d'un bitmap. De préférence sans utiliser une bibliothèque externe. Cela peut-il être fait? Si c'est le cas, comment? Merci à l'avance.

Essayer de faire les choses un peu plus précis: Chaque pixel du bitmap a une certaine valeur de couleur RVB. Je voudrais obtenir les valeurs RVB moyennes pour tous les pixels de l'image.

Était-ce utile?

La solution

Le plus rapide est en utilisant le code dangereux:

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);

Attention: Je n'ai pas testé ce code ... (j'ai coupé quelques coins)

Ce code asssumes également une image 32 bits. Pour les images 24 bits. Changer le x * 4 x * 3

Autres conseils

Voici une façon beaucoup plus simple:

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.

En gros, vous utilisez DrawImage pour copier le bitmap d'origine dans un Bitmap 1 pixel. Les valeurs RVB de ce pixel 1 représenteront alors les moyennes pour l'original entier. GetPixel est relativement lent, mais seulement lorsque vous l'utilisez sur une grande Bitmap, pixel par pixel. Appelant une fois ici est pas trop grave.

Utilisation LockBits est en effet rapide, mais certains utilisateurs de Windows ont des politiques de sécurité qui empêchent l'exécution de code « dangereux ». Je mentionne cela parce que ce fait me mordait le derrière récemment.

Mise à jour : avec InterpolationMode réglé sur HighQualityBicubic, cette méthode prend environ deux fois plus longtemps que la moyenne avec LockBits; avec HighQualityBilinear, il ne prend que légèrement plus long que LockBits. Donc, à moins que vos utilisateurs ont une politique de sécurité qui interdit le code unsafe, certainement ne pas utiliser ma méthode.

Mise à jour 2: avec le passage du temps, je me rends compte maintenant pourquoi cette approche ne fonctionne pas du tout. Même les algorithmes d'interpolation la plus haute qualité intègrent seulement quelques pixels voisins, donc il y a une limite à la quantité d'une image peut être vers le bas sans perdre écrasa l'information. Et une image squashing à un seul pixel est bien au-delà de cette limite, peu importe quel algorithme que vous utilisez.

La seule façon de le faire serait de réduire l'image dans les étapes (rétrécissement peut-être de moitié à chaque fois) jusqu'à ce que vous obtenez à la taille d'un pixel. Je ne peux pas exprimer en quelques mots ce qu'est une pure perte de temps à écrire quelque chose comme ça serait, donc je suis content que je me suis arrêté quand je pensais. :)

S'il vous plaît, personne ne vote pour cette réponse plus -. Il pourrait être mon idée plus stupide jamais

Ce genre de chose fonctionne, mais il ne peut pas être assez rapide pour être très 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);
}
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top