2010-03-30 17 views
8

Quiero recuperar el color de la mayoría en una imagen de fondo en .NET. ¿Es posible?¿Cómo obtener un color de mayoría en una imagen?

+0

¿Puede aclarar el significado de 'mayoría de color'? ¿Te refieres al color más utilizado en una imagen? el color promedio de una imagen; el valor "más alto" de color equivalente RGB? – Seidr

+3

Probablemente necesites aclarar a qué te refieres con "color de la mayoría". Para la mayoría de las imágenes, es poco probable que un solo color constituya una mayoría apropiada. Supongo que te refieres al color más frecuente (píxel por píxel) o al componente dominante (ya sea R, G o B). – MusiGenesis

+0

¿Necesita velocidad o precisión? ¿Desea también el color más utilizado, el color promedio o el color de tendencia? ¿Necesitas RGB mezclado o sperated? –

Respuesta

6

Puede recorrer todos los píxeles de la imagen y utilizar el método getPixel para determinar el valor RGB. A continuación, puede tener un diccionario que almacena el valor ARGB junto con un recuento. A continuación, puede ver qué valor ARGB está presente más en la imagen.

var list = new Dictionary<int, int>(); 
Bitmap myImage = (Bitmap)Bitmap.FromFile("C:/test.jpeg"); 
for (int x = 0; x < myImage.Width; x++) 
{ 
    for (int y = 0; y < myImage.Height; y++) 
    { 
     int rgb = myImage.GetPixel(x, y).ToArgb(); 
     if (!list.ContainsKey(rgb)) 
      list.Add(rgb, 1); 
     else 
      list[rgb]++; 
    } 
} 

Como se indicó, esto no simpatiza con colores similares. Si quisiera un color de mayoría más 'general', podría tener un umbral de similitud. p.ej. en lugar de:

if (!list.ContainsKey(rgb)) 
     list.Add(rgb, 1); 
    else 
     list[rgb]++; 

que podría hacer:

var added = false; 
for (int i = 0; i < 10; i++) 
{ 
    if (list.ContainsKey(rgb+i)) 
    { 
     list[rgb+i]++; 
     added = true; 
     break; 
    } 
    if (list.ContainsKey(rgb-i)) 
    { 
     list[rgb-i]++; 
     added = true; 
     break; 
    } 
} 
if(!added) 
    list.Add(rgb, 1); 

Se podría hasta el umbral de 10 a todo lo que sea necesario.

+0

Si se trata de un jpeg de "color verdadero" ... podrían ser toneladas de valores en el diccionario con muy pocos recuentos por tecla. –

+0

De acuerdo. Necesitarás algún tipo de agrupamiento o binning para trabajar decentemente. Con este podría tener 2 píxeles perfectamente rojos y una tonelada de variantes sutiles de negro y salir con el rojo como color de la mayoría. – RandomEngy

+0

Bueno, técnicamente hablando, el rojo sería el color de la mayoría. – CeejeeB

3

Suponiendo que usted caracteriza el color de cada píxel usando RGB (versus, por ejemplo, CMYK), podría construir una matriz en 3D (una dimensión cada una para R, G y B). Luego, decida el número de bandejas que desea tener en cada dimensión: mientras más bandejas, mayor será la diferenciación entre matices similares.

Una vez hecho esto, simplemente itere a través de una representación de mapa de bits de su imagen sumando los # píxeles que caen en cada una de las celdas en su matriz 3d. La celda con la suma más alta será el color predominante.

Es probable que desee que su algoritmo sea fácilmente configurable para las # bandejas en cada dimensión para que pueda ajustar cuánto discrimina entre colores similares.

image = new Bitmap("C:\\test.bmp", true); 
int x, y; 
// Loop through the images pixels to product 3d histogram 
for(x=0; x<image.Width; x++) 
{ 
    for(y=0; y<image.Height; y++) 
    { 
     Color pixelColor = image.GetPixel(x, y); 

     // Increment color count in appropriate cell of your 3d histogram here 
     ... 
    } 
} 
5

Esto devolverá el color promedio de la imagen.

static Color AverageColor(string fileName) 
{ 
    using (var bmp = new Bitmap(fileName)) 
    { 
     int width = bmp.Width; 
     int height = bmp.Height; 
     int red = 0; 
     int green = 0; 
     int blue = 0; 
     int alpha = 0; 
     for (int x = 0; x < width; x++) 
      for (int y = 0; y < height; y++) 
      { 
       var pixel = bmp.GetPixel(x, y); 
       red += pixel.R; 
       green += pixel.G; 
       blue += pixel.B; 
       alpha += pixel.A; 
      } 

     Func<int, int> avg = c => c/(width * height); 

     red = avg(red); 
     green = avg(green); 
     blue = avg(blue); 
     alpha = avg(alpha); 

     var color = Color.FromArgb(alpha, red, green, blue); 

     return color; 
    } 
} 
+0

No creo que el resultado califique como "color de la mayoría", ya que puede que no haya ni siquiera un píxel que tenga un color medio. – Kaniu

+0

Muy cierto ... y es por eso que pedí más detalles en mi comentario bajo la pregunta. Sin respuesta, esta es mi entrada. –

4

Esto devolverá el color promedio de la imagen con el acceso inseguro al puntero. Nota: el código solo está adaptado para 24bppRgb, podría adaptarse para otros formatos de píxeles.

unsafe static Color GetColor(string filename) 
    { 
     using (var image = (Bitmap)Bitmap.FromFile(filename)) 
     { 
      if (image.PixelFormat != PixelFormat.Format24bppRgb) throw new NotSupportedException(String.Format("Unsupported pixel format: {0}", image.PixelFormat)); 

      var pixelSize = 3; 
      var bounds = new Rectangle(0, 0, image.Width, image.Height); 
      var data = image.LockBits(bounds, ImageLockMode.ReadOnly, image.PixelFormat); 

      long r = 0; 
      long g = 0; 
      long b = 0; 

      for (int y = 0; y < data.Height; ++y) 
      { 
       byte* row = (byte*)data.Scan0 + (y * data.Stride); 
       for (int x = 0; x < data.Width; ++x) 
       { 
        var pos = x * pixelSize; 
        b += row[pos]; 
        g += row[pos + 1]; 
        r += row[pos + 2]; 
       } 
      } 

      r = r/(data.Width * data.Height); 
      g = g/(data.Width * data.Height); 
      b = b/(data.Width * data.Height); 
      image.UnlockBits(data); 

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

GIST: http://gist.github.com/349210

+0

+1 por rendimiento. Llamar a GetPixel en cada píxel es extremadamente lento. – RandomEngy

+1

Extremadamente lento es un poco exagerado. Pude usar el método GetPixel para analizar cuadros de video a 15 fps 800x600. Entonces, si el póster solo necesita analizar una sola imagen, GetPixel es más que adecuado. – CeejeeB

Cuestiones relacionadas