java 获取图像最常见的颜色
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/4427200/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Getting the most common color of an image
提问by Sergio
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?
我想从图像中获取最常见的颜色。我使用 Java 并且我想要最显着的颜色。是否有任何 cbir java 库可以做到这一点?
Thanks
谢谢
回答by dogbane
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.
你希望这有多准确?您可以使用 Bozhos 的方法并循环遍历整个图像,但这对于大图像可能会很慢。有 16777216 个可能的 RGB 值,在 Map 中为它们保留计数器不是很有效。
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_REPLICATEand SCALE_AREA_AVERAGINGto see what works best for you.
另一种方法是重新采样图像,使用getScaledInstance
将其缩小到较小的版本,例如 1x1 图像,然后用于getRGB
获取该像素的颜色。您可以尝试不同的重采样算法,例如SCALE_REPLICATE和SCALE_AREA_AVERAGING,看看哪种算法最适合您。
回答by Andrew Dyster
Thanks for the answers. Here is a practical example of Bozho's method. It also filters out white/grays/black.
感谢您的回答。这是 Bozho 方法的一个实际示例。它还过滤掉白色/灰色/黑色。
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;
}
}
回答by Illarion Kovalchuk
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.
如果您将图像视为一个大的线性像素阵列,然后您要做的就是对它进行排序呢?排序后,您可以计算相同值的最长部分。
回答by David
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.
编辑:你想要的基本上是一个直方图(去查一下)。很可能有完善的标准解决方案可以有效地计算其中之一。
回答by Bozho
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).
您可以循环BufferedImage
(两个循环 - 一个从 0 到宽度,一个从 0 到高度),然后调用getRgb(x, y)
. 然后计算每个不同的值。您可以Map
为此使用 a (键 = 颜色,值 = 出现次数)。
回答by Ben Voigt
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.
我会计算每个像素的色调,然后计算每个色调的基数(创建一个直方图)。也许按饱和度加权。然后,应用低通滤波器,并找到最大值。最后从色调转换回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.
这假设如果您只有图像的红色平面,您希望结果是“红色”,而不是某种粉红色阴影。
回答by Sandeep P
Andrew Dyster code is working fine, Quick response in android
Andrew Dyster 代码运行良好,在 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;
}
}