Получение наиболее распространенного цвета изображения
Я хотел бы получить наиболее распространенный цвет из изображения. Я использую Java и хочу иметь наиболее преобладающий цвет. Есть ли библиотека Java cbir, чтобы сделать это?
Спасибо
7 ответов
Насколько точно вы хотите, чтобы это было? Вы можете использовать подход Bozhos и зациклить все изображение, но это может быть медленным для больших изображений. Существует 16777216 возможных значений RGB, и их счетчики на карте не очень эффективны.
Альтернативой является повторная выборка изображения с использованием getScaledInstance
чтобы уменьшить его до уменьшенной версии, например изображения 1x1, а затем использовать getRGB
чтобы получить цвет этого пикселя. Вы можете поэкспериментировать с различными алгоритмами передискретизации, такими как SCALE_REPLICATE и SCALE_AREA_AVERAGING, чтобы увидеть, что работает лучше для вас.
Спасибо за ответы. Вот практический пример метода Божо. Он также отфильтровывает белый / серый / черный.
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;
}
}
В зависимости от того, насколько точным должно быть значение цвета, вы можете рассмотреть "цветные корзины", собирающие похожие цвета, чтобы избежать проблем с памятью. Это будет означать разделение цветового пространства на "интервалы" цветов, где все цвета, которые достаточно похожи (т.е. близки друг к другу), считаются одним и тем же цветом. Изменяя размер интервала, вы можете напрямую управлять компромиссом между точностью и потреблением памяти.
Изменить: То, что вы хотите, это в основном гистограмма (посмотрите, что это). Скорее всего, существуют надежные стандартные решения для эффективного расчета одного из них.
Что если вы рассматриваете свое изображение как большой линейный массив пикселей, и после этого все, что вам нужно сделать, это просто отсортировать его? После сортировки вы можете посчитать самую длинную часть тех же значений.
Вы можете зациклить BufferedImage
(две петли - одна от 0 до ширины и одна от 0 до высоты), и получить вызов getRgb(x, y)
, Затем посчитайте каждое другое значение. Вы можете использовать Map
для этого (ключ = цвет, значение = количество вхождений).
Я бы рассчитал оттенок каждого пикселя, а затем мощность каждого оттенка (создает гистограмму). Возможно взвешивание по насыщенности. Затем примените фильтр нижних частот и найдите максимум. Наконец конвертировать из оттенка обратно в RGB.
Это предполагает, что если бы у вас была только красная плоскость изображения, вы бы хотели, чтобы результат был "красным", а не каким-то оттенком розового.
Эндрю Дистер код работает нормально, быстрый ответ в 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;
}
}