Finding the dominant color of an image in an Android @drawable

58,095

Solution 1

In Android 5.0 Lollipop, a class was added to help extract useful colors from a Bitmap. The Palette class, found in android.support.v7.graphics, can extract the following colors:

  • Vibrant
  • Vibrant Dark
  • Vibrant Light
  • Muted
  • Muted Dark
  • Muted Light

This Android training page gives all the details you need to use the class (I tried it myself in Android Studio and it was very straightforward): http://developer.android.com/training/material/drawables.html#ColorExtract

To quote:

The Android Support Library r21 and above includes the Palette class, which lets you extract prominent colors from an image. To extract these colors, pass a Bitmap object to the Palette.generate() static method in the background thread where you load your images. If you can't use that thread, call the Palette.generateAsync() method and provide a listener instead.*

You can retrieve the prominent colors from the image using the getter methods in the Palette class, such as Palette.getVibrantColor.

To use the Palette class in your project, add the following Gradle dependency to your app's module:

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

Or if you're using androidx:

implementation 'androidx.palette:palette:1.0.0'

If you need to use generateAsync(), here's how:

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

EDIT: Since the question asks how to extract colors from a drawable resource, you'd first have to convert the drawable to a bitmap to use the technique I've described. Luckily, that is quite simple using BitmapFactory:

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

Solution 2

There is also an another solution, it's more approximative but if you don't want to have long delay for searching color, it can do the job.

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

Solution 3

To find the Dominant / Vibrant / Muted color from an image, use Palette:

import:

implementation 'androidx.palette:palette:1.0.0'

usage:

    val bitmap = BitmapFactory.decodeResource(resources, R.drawable.image)

    Palette.Builder(bitmap).generate { it?.let {  palette ->
        val dominantColor = palette.getDominantColor(ContextCompat.getColor(context!!, R.color.defaultColor))

        // TODO: use dominant color

    } }

Solution 4

This class iterates through a Bitmap and returns the most dominate colour. Feel free to clean up the code where necessary.

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

to get the hex simply call returnColour();

Solution 5

Add to dependencies

implementation 'androidx.palette:palette:1.0.0'

and..

 AppCompatImageView imageView = findViewById(R.id.image_view);

 Bitmap bitmap = ((BitmapDrawable) imageView.getDrawable()).getBitmap();
 Palette.from(bitmap).generate(palette -> {
      int vibrant = palette.getVibrantColor(0x000000); // <=== color you want
      int vibrantLight = palette.getLightVibrantColor(0x000000);
      int vibrantDark = palette.getDarkVibrantColor(0x000000);
      int muted = palette.getMutedColor(0x000000);
      int mutedLight = palette.getLightMutedColor(0x000000);
      int mutedDark = palette.getDarkMutedColor(0x000000);
 });
Share:
58,095
Tyler
Author by

Tyler

Updated on July 09, 2022

Comments

  • Tyler
    Tyler almost 2 years

    You can understand why I'm trying to find the dominant color in an image if you use Windows 7. When your mouse over a program in the taskbar, the background of that particular program changes based on the most dominant color in the icon. I have noticed this technique used in other programs as well, but can't remember them off the top of my head.

    I can see this being helpful in a number of UI techniques that I'm using to develop an application, and I was wondering how finding the most common color would be achieved from an Android drawable resource.

  • Tyler
    Tyler over 12 years
    I was hoping there would be an API function buried deep somewhere. This is good info
  • Thiago M Rocha
    Thiago M Rocha over 9 years
    I suggest to whoever uses this approach to play with the tolerance variable. Depending on the value you set it the algorithm runs faster or slower.
  • radley
    radley over 9 years
    I found a simple hack: copy as a 1x1 bitmap and get the color: aerilys.fr/blog/?p=1341
  • Tony Wickham
    Tony Wickham about 9 years
    If you want to experiment with what colors the Palette class extracts, check out my app on the Play Store: play.google.com/store/apps/…. You can find the source code for it on GitHub: github.com/tony-w/PaletteColorExtraction
  • Baz
    Baz about 9 years
    Excellent answer. This makes most of the available libraries and methods redundant.
  • Hendra Anggrian
    Hendra Anggrian almost 9 years
    question: What is bitmap2 for? you don't seem to use it after copying from the original bitmap.
  • Abdullah Umer
    Abdullah Umer over 8 years
    This is a very helpful answer. I was using the loop method but that was making the UI jerky. Now it's very smooth.
  • David Doyle
    David Doyle over 7 years
    As of support:palette-v7:25.0.0, this no longer needs a custom method - just use palette.getDominantSwatch().
  • Pdroid
    Pdroid over 7 years
    It still take some time to be done and need to be done asynchronously.
  • A.J.
    A.J. almost 7 years
    Palette.generateAsync method has been deprecated. Use Palette.from(bitmap).generate(paletteListener) instead
  • bikram
    bikram about 6 years
    i really appreciate your answer. short and sweet
  • BooleanCheese
    BooleanCheese about 6 years
    Thank you! Android's pallete generator seems to intentionally avoid getting the most used color
  • TheCoderGuy
    TheCoderGuy almost 5 years
    @TonyWickham The generateAsync it is now deprecated. Do you have any idea how to get the color of the favicon from url ?
  • Ken Nichols
    Ken Nichols almost 5 years
    @Spritzig use from: Palette.from(bitmap).generate(new PaletteAsyncListener() { public void onGenerated(Palette p) { // Use generated instance } });
  • Francis
    Francis over 4 years
  • Koen
    Koen over 4 years
    Well to be fair you dont want to scale a bitmap on the UI thread either. But nice approach nonetheless