Pregunta

En mi C # (3.5) la aplicación que necesita para obtener los valores medios de color para los canales rojo, verde y azul de un mapa de bits. Preferiblemente sin necesidad de utilizar una biblioteca externa. Se puede hacer esto? ¿Si es así, cómo? Gracias de antemano.

Tratar de hacer las cosas un poco más preciso: Cada píxel en el mapa de bits tiene un cierto valor de color RGB. Me gustaría obtener los valores RGB promedio para todos los píxeles de la imagen.

¿Fue útil?

Solución

La forma más rápida es mediante el uso de código no seguro:

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

Cuidado: No he probado este código ... (que puede haber cortar algunas esquinas)

Este código también asssumes una imagen de 32 bits. Para las imágenes de 24 bits. Cambie la x * 4 a x * 3

Otros consejos

Aquí está una manera mucho más 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.

Básicamente, se utiliza DrawImage para copiar el mapa de bits original en un mapa de bits de 1 píxel. Los valores RGB de que 1 pixel representará entonces los promedios para todo el original. GetPixel es relativamente lento, pero sólo cuando lo utiliza en un gran mapa de bits, pixel a pixel. Llamándola una vez aquí es que no es problema.

El uso de LockBits es, en efecto rápido, pero algunos usuarios de Windows tienen políticas de seguridad que impiden la ejecución de código "no seguro". Menciono esto porque este hecho sólo me mordió en el trasero recientemente.

Actualizar : con InterpolationMode establece en HighQualityBicubic, este método tiene el doble de tiempo como un promedio con LockBits; con HighQualityBilinear, sólo se necesita un poco más que LockBits. Así que a menos que sus usuarios tienen una política de seguridad que prohíbe unsafe código, definitivamente no usar mi método.

Actualización 2: con el paso del tiempo, ahora me doy cuenta por qué este enfoque no funciona en absoluto. Incluso los algoritmos de interpolación de más alta calidad incorporan sólo unos pocos píxeles vecinos, por lo que hay un límite a la cantidad de una imagen puede ser aplastado abajo sin perder información. Y aplastando una imagen a un píxel es mucho más allá de este límite, no importa qué algoritmo que utiliza.

La única manera de hacer esto sería la de reducir la imagen en pasos (tal vez la reducción a la mitad cada vez) hasta que hacerlo hasta el tamaño de un píxel. No puedo expresar en palabras simples lo que sería una absoluta pérdida de tiempo escribir algo como esto, así que estoy contento de haber dejado de mí mismo cuando me había ocurrido. :)

Por favor, nadie voto para esta respuesta más -. Podría ser mi estúpida idea cada vez

Este tipo de cosas funciona, pero puede que no sea lo suficientemente rápido como para que sea útil.

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);
}
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top