Getting the most common color of an image

百般思念 提交于 2019-11-28 05:08:20

How accurate do you want this to be? You can use Bozhos's approach and loop over the entire image but this could be slow for large images. There are 16777216 possible RGB values and keeping counters for them in a Map is not very efficient.

An alternative is to resample the image using getScaledInstance to scale it down to a smaller version e.g. a 1x1 image and then use getRGB to get the colour of that pixel. You can experiment with different resampling algorithms such as SCALE_REPLICATE and SCALE_AREA_AVERAGING to see what works best for you.

Thanks for the answers. Here is a practical example of Bozho's method. It also filters out white/grays/black.

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

What if you consider your image as a big linear array of pixels, and after that all what you have to do is just sort it? When you have it sorted, you can count the longest part of same values.

Depending on how exact you need the color value to be, you might want to consider "color buckets" collecting similar colors to avoid memory issues. This would mean to partition the color space into "intervals" of colors, where all colors which are similar (i.e. close together) enough are counted as the same color. By changing the interval size you have a means of directly manipulating the trade-off between accuracy and memory consumption.


Edit: What you want is basically a histogram (go look that up). There are most probably well established standard solutions for efficiently calculating one of those.

You can loop the BufferedImage (two loops - one from 0 to width, and one from 0 to height), and get the call getRgb(x, y). Then count each different value. You can use a Map for that (key = color, value = number of occurences).

I would calculate the hue of each pixel and then the cardinality of each hue (creates a histogram). Perhaps weighting by saturation. Then, apply a low-pass filter, and find the maximum. Finally convert from hue back to RGB.

This assumes that if you had just the red plane of an image, you'd want the result to be "red", not some shade of pink.

Andrew Dyster code is working fine, Quick response in 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;
    }
}
标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!