2010-12-13 18 views
9

Me gustaría obtener el color más común de una imagen. Uso Java y quiero tener el color más predominante. ¿Hay alguna biblioteca de cbir java para hacer esto?Obteniendo el color más común de una imagen

Gracias

+0

¿Qué es exactamente lo que quiere decir con "predominante"? – Thomas

+0

¿El color más común en la imagen? –

Respuesta

2

Puede recorrer la BufferedImage (dos bucles - uno entre 0 y ancho, y una altura de 0 a), y obtener la llamada getRgb(x, y). Luego cuente cada valor diferente. Puede usar un Map para eso (clave = color, valor = número de ocurrencias).

+0

¿De verdad? Podría haber hasta 16,581,375 colores. – dogbane

+0

¿Entonces te preocupas por la memoria? – Bozho

+2

Sí. Podría ser mi PC, pero obtuve un OOM después de aproximadamente 4 millones de colores en mi mapa. – dogbane

9

¿Qué precisión desea que sea? Puede usar el enfoque de Bozhos y recorrer toda la imagen, pero esto podría ser lento para imágenes grandes. Hay 16777216 posibles valores RGB y mantener contadores para ellos en un Mapa no es muy eficiente.

Una alternativa es volver a muestrear la imagen usando getScaledInstance para reducirla a una versión más pequeña, p. Ej. una imagen de 1x1 y luego use getRGB para obtener el color de ese píxel. Puede experimentar con diferentes algoritmos de remuestreo como SCALE_REPLICATE y SCALE_AREA_AVERAGING para ver qué funciona mejor para usted.

+0

Tenga en cuenta que este enfoque daría un resultado diferente al enfoque de Bozhos. En este último, el color que aparece con mayor frecuencia en la imagen se determina mientras su enfoque intenta encontrar algo como "el color promedio", una noción mal definida, pero está claro que el color devuelto puede no aparecer en ninguna parte del original. imagen.No digo que un enfoque sea mejor que otro, supongo que el póster original debe aclarar qué es lo que está buscando. – Thomas

+0

Sí, entiendo eso, por lo tanto, por qué pregunté cuánta precisión se requería. Si usa 'ReplicateScaleFilter', obtendrá un color que aparecerá en la imagen original porque" omite filas y columnas de píxeles para reducirlas ". No hace ninguna "mezcla" como el 'AreaAveragingScaleFilter'. – dogbane

+1

¿De dónde sacaste el número 16581375? Si estamos hablando de 8 bits por canal, hay 2^24 = 16777216 posibles valores RGB. – Jesper

3

¿Qué sucede si considera que su imagen es una gran variedad lineal de píxeles, y después de eso todo lo que tiene que hacer es ordenarlo? Cuando lo haya ordenado, puede contar la parte más larga de los mismos valores.

3

Dependiendo de qué tan exacto necesite el valor de color, es posible que desee considerar "cubos de color" que recogen colores similares para evitar problemas de memoria. Esto significaría dividir el espacio de color en "intervalos" de colores, donde todos los colores que son similares (es decir, muy juntos) se cuentan como el mismo color. Al cambiar el tamaño del intervalo, tiene un medio para manipular directamente el equilibrio entre precisión y consumo de memoria.


Editar: Lo que quieres es básicamente un histograma (mira eso). Probablemente existan soluciones estándar bien establecidas para calcular de manera eficiente una de ellas.

+0

Sí, solo cuente cada color (bastante fácil de usar el valor del color como el índice de una matriz de enteros) que puede incrementar rápidamente. La matriz será más pequeña que la imagen que se analiza y el incremento de un entero es bastante barato en la mayoría (¿todos?) De los lenguajes de programación. – Eno

1

Calcularía el matiz de cada píxel y luego la cardinalidad de cada matiz (crea un histograma). Quizás ponderando por saturación. Luego, aplique un filtro de paso bajo y encuentre el máximo. Por último, conviértalo del tono a RGB.

Esto supone que si tuviera solo el plano rojo de una imagen, desearía que el resultado fuera "rojo", no un tono de rosa.

4

Gracias por las respuestas. Aquí hay un ejemplo práctico del método de Bozho. También filtra blanco/grises/negro.

import java.awt.image.BufferedImage; 
import java.io.File; 
import java.util.Collections; 
import java.util.Comparator; 
import java.util.HashMap; 
import java.util.Iterator; 
import java.util.LinkedList; 
import java.util.List; 
import java.util.Map; 
import javax.imageio.ImageIO; 
import javax.imageio.ImageReader; 
import javax.imageio.stream.ImageInputStream; 


public class ImageTester { 


    public static void main(String args[]) throws Exception { 
     File file = new File("C:\\Users\\Andrew\\Desktop\\myImage.gif"); 
     ImageInputStream is = ImageIO.createImageInputStream(file); 
     Iterator iter = ImageIO.getImageReaders(is); 

     if (!iter.hasNext()) 
     { 
      System.out.println("Cannot load the specified file "+ file); 
      System.exit(1); 
     } 
     ImageReader imageReader = (ImageReader)iter.next(); 
     imageReader.setInput(is); 

     BufferedImage image = imageReader.read(0); 

     int height = image.getHeight(); 
     int width = image.getWidth(); 

     Map m = new HashMap(); 
     for(int i=0; i < width ; i++) 
     { 
      for(int j=0; j < height ; j++) 
      { 
       int rgb = image.getRGB(i, j); 
       int[] rgbArr = getRGBArr(rgb);     
       // Filter out grays....     
       if (!isGray(rgbArr)) {     
         Integer counter = (Integer) m.get(rgb); 
         if (counter == null) 
          counter = 0; 
         counter++;         
         m.put(rgb, counter);     
       }     
      } 
     }   
     String colourHex = getMostCommonColour(m); 
     System.out.println(colourHex); 
    } 


    public static String getMostCommonColour(Map map) { 
     List list = new LinkedList(map.entrySet()); 
     Collections.sort(list, new Comparator() { 
       public int compare(Object o1, Object o2) { 
       return ((Comparable) ((Map.Entry) (o1)).getValue()) 
        .compareTo(((Map.Entry) (o2)).getValue()); 
       } 
     });  
     Map.Entry me = (Map.Entry)list.get(list.size()-1); 
     int[] rgb= getRGBArr((Integer)me.getKey()); 
     return Integer.toHexString(rgb[0])+" "+Integer.toHexString(rgb[1])+" "+Integer.toHexString(rgb[2]);   
    }  

    public static int[] getRGBArr(int pixel) { 
     int alpha = (pixel >> 24) & 0xff; 
     int red = (pixel >> 16) & 0xff; 
     int green = (pixel >> 8) & 0xff; 
     int blue = (pixel) & 0xff; 
     return new int[]{red,green,blue}; 

    } 
    public static boolean isGray(int[] rgbArr) { 
     int rgDiff = rgbArr[0] - rgbArr[1]; 
     int rbDiff = rgbArr[0] - rgbArr[2]; 
     // Filter out black, white and grays...... (tolerance within 10 pixels) 
     int tolerance = 10; 
     if (rgDiff > tolerance || rgDiff < -tolerance) 
      if (rbDiff > tolerance || rbDiff < -tolerance) { 
       return false; 
      }     
     return true; 
    } 
} 
+0

parece una forma costosa de hacer iterando sobre cada píxel. Muere fácilmente por una foto de 5MP – Taranfx

+0

Si la imagen es masiva, cambie el tamaño primero. –

0

código Andrew Dyster está trabajando respuesta fino, rápido en Android

import java.util.Collections; 
import java.util.Comparator; 
import java.util.HashMap; 
import java.util.LinkedList; 
import java.util.List; 
import java.util.Map; 

import android.graphics.Bitmap; 

public class ImageTester { 

    public interface ImageColor { 
     void onImageColor(int r, int g, int b); 
    } 

    @SuppressWarnings({ "unchecked", "rawtypes" }) 
    public static void getMostCommonColour(final Bitmap image, 
      final ImageColor heColor) { 
     new Thread(new Runnable() { 
      private int rgb; 

      @Override 
      public void run() { 
       int height = image.getHeight(); 
       int width = image.getWidth(); 
       Map m = new HashMap(); 
       int boderWid = width/4; 
       int borderHeigh = height/4; 

       for (int i = boderWid; i < width - boderWid;) { 
        for (int j = borderHeigh; j < height - borderHeigh;) { 
         try { 
          rgb = image.getPixel(i, j); 

         } catch (Exception e) { 
          continue; 
         }finally{ 
          i += 20; 
          j += 20; 
         } 
         int[] rgbArr = getRGBArr(rgb); 
         // Filter out grays.... 
         if (!isGray(rgbArr)) { 
          Integer counter = (Integer) m.get(rgb); 
          if (counter == null) 
           counter = 0; 
          counter++; 
          m.put(rgb, counter); 

         } 

        } 
       } 
       List list = new LinkedList(m.entrySet()); 
       Collections.sort(list, new Comparator() { 
        public int compare(Object o1, Object o2) { 
         return ((Comparable) ((Map.Entry) (o1)).getValue()) 
           .compareTo(((Map.Entry) (o2)).getValue()); 
        } 
       }); 
       Map.Entry me = (Map.Entry) list.get(list.size() - 1); 
       int[] rgb = getRGBArr((Integer) me.getKey()); 
       heColor.onImageColor(rgb[0], rgb[1], rgb[2]); 

      } 
     }).start(); 
    } 

    public static int[] getRGBArr(int pixel) { 
     int red = (pixel >> 16) & 0xff; 
     int green = (pixel >> 8) & 0xff; 
     int blue = (pixel) & 0xff; 
     return new int[] { red, green, blue }; 

    } 

    public static boolean isGray(int[] rgbArr) { 
     int rgDiff = rgbArr[0] - rgbArr[1]; 
     int rbDiff = rgbArr[0] - rgbArr[2]; 
     int tolerance = 10; 
     if (rgDiff > tolerance || rgDiff < -tolerance) 
      if (rbDiff > tolerance || rbDiff < -tolerance) { 
       return false; 
      } 
     return true; 
    } 
} 
Cuestiones relacionadas