2010-12-13 4 views
9

Я хотел бы получить наиболее распространенный цвет изображения. Я использую Java, и я хочу иметь самый преобладающий цвет. Есть ли библиотека cbir java для этого?Получение наиболее распространенного цвета изображения

Благодаря

+0

Что именно вы имеете в виду под "доминирующим"? – Thomas

+0

Самый распространенный цвет на картинке? –

ответ

2

Вы можете мертвую BufferedImage (две петли - один от 0 до ширины, и один от 0 до высоты), и получить вызов getRgb(x, y). Затем подсчитайте каждое другое значение. Вы можете использовать для этого Map (key = color, value = количество вхождений).

+0

Действительно? Там может быть до 16 581 375 цветов. – dogbane

+0

Значит, вы беспокоитесь о памяти? – Bozho

+2

Да. Мог бы просто быть моим компьютером, но я получил OOM после примерно 4 миллионов цветов на моей карте. – dogbane

9

Насколько вы хотите, чтобы это было так? Вы можете использовать подход Bozhos и цикл по всему изображению, но это может быть медленным для больших изображений. Есть 16777216 возможных значений RGB и сохранение счетчиков для них на карте не очень эффективно.

Альтернативой является повторная выборка изображения с использованием getScaledInstance, чтобы уменьшить масштаб до меньшей версии, например. изображение 1x1, а затем используйте getRGB, чтобы получить цвет этого пикселя. Вы можете поэкспериментировать с различными алгоритмами передискретизации, такими как SCALE_REPLICATE и SCALE_AREA_AVERAGING, чтобы увидеть, что лучше всего подходит для вас.

+0

Обратите внимание, что этот подход дал бы другой результат, чем подход Божоса. В последнем случае цвет, который появляется наиболее часто на изображении, определяется, когда ваш подход пытается найти что-то вроде «среднего цвета» - нечеткое понятие, но ясно, что возвращенный цвет может даже не появляться нигде в оригинале образ.Я не говорю, что один подход лучше, чем другой, я думаю, что оригинальный плакат должен уточнить, что он/она ищет. – Thomas

+0

Да, я так понимаю, поэтому я спросил, сколько требуется точности. Если вы используете «ReplicateScaleFilter», вы получите цвет, который появляется в исходном изображении, потому что он «пропускает строки и столбцы пикселей для уменьшения масштаба». Он не делает «смешивания», как «AreaAveragingScaleFilter». – dogbane

+1

Откуда у вас номер 16581375? Если мы говорим о 8 бит на канал, есть 2^24 = 16777216 возможных значений RGB. – Jesper

3

Что делать, если вы рассматриваете свое изображение как большой линейный массив пикселей, и после этого все, что вам нужно сделать, это просто его сортировать? Когда вы его сортируете, вы можете считать самую длинную часть одних и тех же значений.

3

В зависимости от того, насколько точным вам нужно значение цвета, вы можете рассмотреть «цветовые ведра», собирающие похожие цвета, чтобы избежать проблем с памятью. Это означало бы разделение цветового пространства на «интервалы» цветов, где все цвета, которые похожи (например, близко друг к другу), считаются одним и тем же цветом. Изменяя размер интервала, вы имеете возможность напрямую манипулировать компромиссом между точностью и потреблением памяти.


Редактировать: То, что вы хотите, в основном представляет собой гистограмму (идите посмотреть вверх). Есть, скорее всего, хорошо зарекомендовавшие себя стандартные решения для эффективного расчета одного из них.

+0

Да, просто подсчитайте каждый цвет (довольно легко использовать значение цвета как индекс для массива целых чисел), которое вы можете быстро увеличить. Массив будет меньше, чем анализируемое изображение, и приращение целого числа довольно дешево на большинстве языков программирования (всего?). – Eno

1

Я бы вычислил оттенок каждого пикселя, а затем мощность каждого оттенка (создает гистограмму). Возможно, взвешивание насыщения. Затем примените фильтр нижних частот и найдите максимум. Наконец, конвертируйте из оттенка обратно в RGB.

Это предполагает, что если бы у вас была только красная плоскость изображения, вы бы хотели, чтобы результат был «красным», а не оттенком розового.

4

Спасибо за ответы. Вот практический пример метода Божо. Он также отфильтровывает белый/серый/черный.

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

выглядит как дорогой способ сделать, повторяя каждый пиксель. Он легко умрет за 5-мегапиксельное фото. – Taranfx

+0

Если изображение массивное, сначала измените его размер. –

0

Эндрю Dyster код работает отлично, Быстрый отклик в андроиде

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