问题
I'd like to get the most common color from an image. I use Java and I want to have the most predominant color. Are there any cbir java library to make this?
Thanks
回答1:
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.
回答2:
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;
}
}
回答3:
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.
回答4:
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.
回答5:
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).
回答6:
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.
回答7:
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;
}
}
来源:https://stackoverflow.com/questions/4427200/getting-the-most-common-color-of-an-image