42

I want to be able to take an image and find out what is the average colour. meaning if the image is half black half white, I would get something in between... some shade of gray. It could be the most frequent single colour or median. Any average will do.

How can I do this in android.

124697
  • 21,137
  • 63
  • 182
  • 307
  • 7
    Sounds pretty straightforward... what have you tried? –  Sep 13 '12 at 14:22
  • 1
    loop on all the colors - and store the average... not too bad – Randy Sep 13 '12 at 14:22
  • Unless you're into taking the NDK or RenderScript route, out of which latter made this task rather easy and fast, ``Bitmap.getPixel`` and ``Bitmap.getPixels`` are your friends. – harism Sep 13 '12 at 14:22
  • @bdares I don't know where to start. do i need some image processing library or can I do it in android platform straight away? – 124697 Sep 13 '12 at 14:25
  • @randy : only problem with it is to use a smart average method, as on big image sum(pixels value) may exceed MAX_INT – njzk2 Sep 13 '12 at 14:44
  • Read this: http://stackoverflow.com/questions/5823854/how-can-i-generate-a-palette-of-prominent-colors-from-an-image – Rafael Sanches Sep 29 '13 at 11:49

6 Answers6

52
Bitmap bitmap = someFunctionReturningABitmap();
long redBucket = 0;
long greenBucket = 0;
long blueBucket = 0;
long pixelCount = 0;

for (int y = 0; y < bitmap.getHeight(); y++)
{
    for (int x = 0; x < bitmap.getWidth(); x++)
    {
        Color c = bitmap.getPixel(x, y);

        pixelCount++;
        redBucket += Color.red(c);
        greenBucket += Color.green(c);
        blueBucket += Color.blue(c);
        // does alpha matter?
    }
}

Color averageColor = Color.rgb(redBucket / pixelCount,
                                greenBucket / pixelCount,
                                blueBucket / pixelCount);
Dan O
  • 5,875
  • 2
  • 34
  • 47
  • 5
    Getting each pixel individually with `getPixel` inside your loop is wasteful and slow. You should get all of the pixels with a single call to `getPixels` before the for loops. – slayton Sep 13 '12 at 14:54
  • 1
    Slow? probably, yes. Wasteful? Maybe, depends on the size of the bitmap and how much memory is available (I believe `getPixels()` creates a copy of the bitmap data). – Dan O Sep 13 '12 at 14:59
  • 2
    `Color c = bitmap.getPixel()` should be `int c = bitmap.getPixel()` because `getPixel()` returns an int, not a Color – Stardust Jun 21 '17 at 17:55
  • @Stardust is correct. This function will not return a Color object, but an int. Otherwise this works pretty well. To improve the speed on larger images try scaling the bitmap down. CIF (352x240) works pretty well for me. – Zack Matthews Jul 05 '17 at 14:08
  • Color.rgb(redBucket / pixelCount, greenBucket / pixelCount, blueBucket / pixelCount); call requires api 26 – Sujith Manjavana Nov 21 '19 at 18:32
13

I think you will have to do that yourself.

Just create an int array with all the colors :

    Bitmap bmp = BitmapFactory.decodeResource(getResources(), R.drawable.icon);  
    bmp = bmp.copy(Bitmap.Config.ARGB_8888, true);    
    int intArray[] = new int[bmp.getWidth()*bmp.getHeight()];  
    bmp.getPixels(intArray, 0, bmp.getWidth(), 0, 0, bmp.getWidth(), bmp.getHeight());  

Then you can get the color with intArray[0], the value could be 0xFFFF0000 for red (last 6 numbers are the RGB color value).

EDIT : Easy solution :

Get you full-size image in a bitmap.

Create a scaled bitmap of 1*1px.

Get this bitmap color.
Stephane Mathis
  • 6,372
  • 6
  • 41
  • 69
11

Building off Dan O's solution, here's a method that automatically takes into account the alpha channel and makes the optimization/ tradeoff of getPixels vs getPixel.

The cost is memory but the benefit is performance, invocation of a virtual method in a loop that could possibly be run several million times [i.e. an 8MP image has 3,456x2,304 = 7,962,624 pixels]). I've even taken things one step further by removing the looped android.graphics.Color method calls.

public static int getDominantColor(Bitmap bitmap) {
   if (null == bitmap) return Color.TRANSPARENT;

   int redBucket = 0;
   int greenBucket = 0;
   int blueBucket = 0;
   int alphaBucket = 0;

   boolean hasAlpha = bitmap.hasAlpha();
   int pixelCount = bitmap.getWidth() * bitmap.getHeight();
   int[] pixels = new int[pixelCount];
   bitmap.getPixels(pixels, 0, bitmap.getWidth(), 0, 0, bitmap.getWidth(), bitmap.getHeight());

   for (int y = 0, h = bitmap.getHeight(); y < h; y++)
   {
       for (int x = 0, w = bitmap.getWidth(); x < w; x++)
       {
           int color = pixels[x + y * w]; // x + y * width
           redBucket += (color >> 16) & 0xFF; // Color.red
           greenBucket += (color >> 8) & 0xFF; // Color.greed
           blueBucket += (color & 0xFF); // Color.blue
           if (hasAlpha) alphaBucket += (color >>> 24); // Color.alpha
       }
   }

   return Color.argb(
           (hasAlpha) ? (alphaBucket / pixelCount) : 255,
           redBucket / pixelCount,
           greenBucket / pixelCount,
           blueBucket / pixelCount);
}
Tom
  • 6,757
  • 7
  • 45
  • 74
  • 2
    funny that you calculate the number of pixels in an 8 _megapixel_ image wich has as the name states 8 million pixels. But thanks for the solution. – Gumbo Aug 15 '14 at 18:07
  • 4
    8 megapixel is not precise, 7,962,624 pixels is precise. – mvmn Oct 05 '16 at 11:28
10

You can use Palete class from AndroidX, or from the the v7-support library.

It provides additional methods to extract colours from a Bitmap, such as getting:

  • Most Dominant color
  • Vibrant colors
  • Muted color
  • much more
Entreco
  • 12,402
  • 8
  • 73
  • 90
aleb
  • 2,545
  • 1
  • 25
  • 45
  • Hmmm, very recent yes ;) Just a friendly reminder this class is also available in androidx. https://developer.android.com/reference/androidx/palette/graphics/Palette – Entreco Jan 07 '19 at 20:38
2

Use the Bitmap.getPixels() method to get the color values. Then to calculate the average you have to decide what you mean by that. In a grayscale image it is simple, but with colors there are no such thing as an average. You can separate into components (for example RGBA), and take the average of each component. An alternative is to search for the most commonly used color, and there are several other options I'm sure. Play with it :)

Tobias Ritzau
  • 3,319
  • 2
  • 16
  • 29
2

There is also a library that can do this for you.

http://www.michaelevans.org/blog/2013/12/12/colorart-a-library-to-do-itunes-11-style-color-matching-for-android/

NathofGod
  • 479
  • 5
  • 12