Поиск доминирующего цвета изображения в Android @drawable

Вы можете понять, почему я пытаюсь найти доминирующий цвет изображения, если вы используете Windows 7. Когда вы наводите указатель мыши на программу на панели задач, фон этой конкретной программы изменяется на основе наиболее доминирующего цвета в значке. Я заметил эту технику, используемую и в других программах, но не могу запомнить их с головы.

Я вижу, что это полезно в ряде методов пользовательского интерфейса, которые я использую для разработки приложения, и мне было интересно, как найти наиболее распространенный цвет из ресурса, пригодного для Android.

В Android 5.0 Lollipop был добавлен класс, чтобы помочь извлечь полезные цвета из растрового изображения. Класс Palette , найденный в файле android.support.v7.graphics, может извлекать следующие цвета:

  • вибрирующий
  • Яркий темный
  • Яркий свет
  • приглушенный
  • Приглушенный темный
  • Приглушенный свет

На этой обучающей странице Android вы найдете все детали, которые вам нужны для использования этого класса (я сам это пробовал в Android Studio, и это было очень просто): http://developer.android.com/training/material/drawables.html#ColorExtract

Цитировать:

Библиотека поддержки Android r21 и выше включает в себя класс Palette , который позволяет извлекать яркие цвета из изображения. Чтобы извлечь эти цвета, передайте объект Bitmap в статический метод Palette.generate () в фоновом потоке, где вы загружаете изображения. Если вы не можете использовать этот поток, вызовите метод Palette.generateAsync () и вместо этого создайте прослушиватель. *

Вы можете получить яркие цвета изображения, используя методы геттера в классе Palette, такие как Palette.getVibrantColor.

Чтобы использовать класс Palette в вашем проекте, добавьте следующую зависимость Gradle в модуль вашего приложения:

dependencies { ... compile 'com.android.support:palette-v7:21.0.+' } 

* Если вам нужно использовать generateAsync (), вот как это сделать:

 Palette.generateAsync(bitmap, new Palette.PaletteAsyncListener() { public void onGenerated(Palette palette) { // Do something with colors... } }); 

EDIT: поскольку вопрос задает вопрос о том, как извлекать цвета из ресурса, пригодного для рисования, сначала нужно преобразовать рисованное в растровое изображение, чтобы использовать описанную вами технику. К счастью, это довольно просто, используя BitmapFactory:

 Bitmap icon = BitmapFactory.decodeResource(context.getResources(), R.drawable.icon_resource);` 

Существует еще одно решение, оно более приближенное, но если вы не хотите иметь длительную задержку для поиска цвета, он может выполнить эту работу.

 public static int getDominantColor(Bitmap bitmap) { Bitmap newBitmap = Bitmap.createScaledBitmap(bitmap, 1, 1, true); final int color = newBitmap.getPixel(0, 0); newBitmap.recycle(); return color; } 

Этот класс выполняет итерацию через Bitmap и возвращает самый доминирующий цвет. Не стесняйтесь очищать код там, где это необходимо.

 public class ImageColour { String colour; public ImageColour(Bitmap image) throws Exception { 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.getPixel(i, j); int[] rgbArr = getRGBArr(rgb); if (!isGray(rgbArr)) { Integer counter = (Integer) m.get(rgb); if (counter == null) counter = 0; counter++; m.put(rgb, counter); } } } String colourHex = getMostCommonColour(m); } 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 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; } public String returnColour() { if (colour.length() == 6) { return colour.replaceAll("\\s", ""); } else { return "ffffff"; } } 

Для получения шестнадцатеричного returnColour(); просто вызовите returnColour();

Я написал свои собственные методы, чтобы получить доминирующий цвет:

Метод 1 (Моя техника)

  1. Уменьшить до цветового пространства ARGB_4444
  2. Вычислить максимальное количество отдельных элементов RGB и получить 3 отличительных максимальных значения
  3. Объединение максимальных значений с доминирующим цветом RGB

     public int getDominantColor1(Bitmap bitmap) { if (bitmap == null) throw new NullPointerException(); int width = bitmap.getWidth(); int height = bitmap.getHeight(); int size = width * height; int pixels[] = new int[size]; Bitmap bitmap2 = bitmap.copy(Bitmap.Config.ARGB_4444, false); bitmap2.getPixels(pixels, 0, width, 0, 0, width, height); final List<HashMap<Integer, Integer>> colorMap = new ArrayList<HashMap<Integer, Integer>>(); colorMap.add(new HashMap<Integer, Integer>()); colorMap.add(new HashMap<Integer, Integer>()); colorMap.add(new HashMap<Integer, Integer>()); int color = 0; int r = 0; int g = 0; int b = 0; Integer rC, gC, bC; for (int i = 0; i < pixels.length; i++) { color = pixels[i]; r = Color.red(color); g = Color.green(color); b = Color.blue(color); rC = colorMap.get(0).get(r); if (rC == null) rC = 0; colorMap.get(0).put(r, ++rC); gC = colorMap.get(1).get(g); if (gC == null) gC = 0; colorMap.get(1).put(g, ++gC); bC = colorMap.get(2).get(b); if (bC == null) bC = 0; colorMap.get(2).put(b, ++bC); } int[] rgb = new int[3]; for (int i = 0; i < 3; i++) { int max = 0; int val = 0; for (Map.Entry<Integer, Integer> entry : colorMap.get(i).entrySet()) { if (entry.getValue() > max) { max = entry.getValue(); val = entry.getKey(); } } rgb[i] = val; } int dominantColor = Color.rgb(rgb[0], rgb[1], rgb[2]); return dominantColor; } 

Метод 2 (старая техника)

  1. Уменьшить до цветового пространства ARGB_4444
  2. Вычислите появление каждого цвета и найдите максимальный цвет как доминирующий цвет

     public int getDominantColor2(Bitmap bitmap) { if (bitmap == null) throw new NullPointerException(); int width = bitmap.getWidth(); int height = bitmap.getHeight(); int size = width * height; int pixels[] = new int[size]; Bitmap bitmap2 = bitmap.copy(Bitmap.Config.ARGB_4444, false); bitmap2.getPixels(pixels, 0, width, 0, 0, width, height); HashMap<Integer, Integer> colorMap = new HashMap<Integer, Integer>(); int color = 0; Integer count = 0; for (int i = 0; i < pixels.length; i++) { color = pixels[i]; count = colorMap.get(color); if (count == null) count = 0; colorMap.put(color, ++count); } int dominantColor = 0; int max = 0; for (Map.Entry<Integer, Integer> entry : colorMap.entrySet()) { if (entry.getValue() > max) { max = entry.getValue(); dominantColor = entry.getKey(); } } return dominantColor; } 

Прокрутите все данные цвета пикселя и сравните значения цвета, игнорируйте все, что является оттенком серого или прозрачного. Я считаю, что именно это делает Microsoft в Windows 7 на основе недавнего сообщения в блоге.

редактировать
Сообщение в блоге: http://blogs.msdn.com/b/oldnewthing/archive/2011/12/06/10244432.aspx

Эта ссылка, показывающая, как Chrome выбирает доминирующий цвет, также может быть полезна. http://www.quora.com/Google-Chrome/How-does-Chrome-pick-the-color-for-the-stripes-on-the-Most-visited-page-thumbnails

Я пытался добиться подобной вещи. Однако не удалось найти готовые API для Android.

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

Полный источник доступен по адресу http://makingmoneywithandroid.com/forum/showthread.php?tid=433

Надеюсь, это поможет вам!

Примечание. В этом коде используется BitmapDrawable, а не только обычный Drawable. См. Как конвертировать Drawable в Bitmap? Для получения дополнительной информации об этом различии.

Ни один из других ответов не помог мне, и я не исключил причину проблемы.

Вот что я в итоге использовал:

 public static int getDominantColor(Bitmap bitmap) { if (bitmap == null) { return Color.TRANSPARENT; } int width = bitmap.getWidth(); int height = bitmap.getHeight(); int size = width * height; int pixels[] = new int[size]; //Bitmap bitmap2 = bitmap.copy(Bitmap.Config.ARGB_4444, false); bitmap.getPixels(pixels, 0, width, 0, 0, width, height); int color; int r = 0; int g = 0; int b = 0; int a; int count = 0; for (int i = 0; i < pixels.length; i++) { color = pixels[i]; a = Color.alpha(color); if (a > 0) { r += Color.red(color); g += Color.green(color); b += Color.blue(color); count++; } } r /= count; g /= count; b /= count; r = (r << 16) & 0x00FF0000; g = (g << 8) & 0x0000FF00; b = b & 0x000000FF; color = 0xFF000000 | r | g | b; return color; }