BitmapData Average Colours

If you want a very simple way of extracting a colour palette from an image, one technique would be to average the colour values within specific areas. Averaging colour values is almost identical to averaging numbers, except with the added initial step of finding the red, green and blue components of the colour. To do this we can use bitwise operators, in this case bitwise shift, to perform fast operations on each bit inside the unsigned integer returned by getPixel or getPixel32. If you want to know more about bitwise operators, Moock has written a detailed and, as ever, very clear article on where, when and why to use bitewise operations. You can read it here.

So once you’re familiar with how to shift the bits of an integer, you can easily get the RGB values from a 24 bit hexadecimal by moving the bits to the right by a certain amount using the bitwise right shift operator (>>).

For example, getting the red, green and blue values from a 24 bit integer would look like:

var colour:uint = 0x33CC99;

var R:Number = colour >> 16 & 0xFF;
var G:Number = colour >> 8  & 0xFF;
var B:Number = colour & 0xFF;

And finding the alpha, red, green and blue values of a 32 bit integer:

var colour:uint = 0xFF33CC99;

var A:Number = colour >> 24 & 0xFF;
var R:Number = colour >> 16 & 0xFF;
var G:Number = colour >> 8 & 0xFF;
var B:Number = colour & 0xFF;

So we know that by shifting the bits to the right, we can effectively break an unsigned integer representing a colour into its specific components, but what about changing these components back into an RGB or ARGB value? Well, it’s simply a matter of shifting the bits in the other direction, using the bitwise left shift operator, which looks like this << (two less than operators next to each other).

So using this bitwise shift left operator, you can take your RGB or ARGB values and cram the little buggers back into a usable format, with a little help from bitwise OR:

// RGB
var colourRBG:uint = (R << 16 | G << 8 | B);

// ARGB
var colourARBG:uint = (A << 24 | R << 16 | G << 8 | B);

Right, so we know how to take an unsigned integer, for example one that’s been returned from getPixel or getPixel32 and break it down into its RGB or ARGB components – and then for the sake of universal harmony, put it back together again and release it back into its 24 or 32bit world with all its binary friends.

So now, finding the average colour within a set of colours is simply a question of averaging the red, green and blue values of all the colours, and then turning these averages into a new colour.

The method bellow does just this, by looping through the pixels in a BitmapData object, adding up all of the red, green and blue values, dividing them by the total number of pixels and then creating a new colour from the results.

public static function averageColour( source:BitmapData ):uint
{
	var red:Number = 0;
	var green:Number = 0;
	var blue:Number = 0;

	var count:Number = 0;
	var pixel:Number;

	for (var x:int = 0; x < source.width; x++)
	{
		for (var y:int = 0; y < source.height; y++)
		{
			pixel = source.getPixel(x, y);

			red += pixel >> 16 & 0xFF;
			green += pixel >> 8 & 0xFF;
			blue += pixel & 0xFF;

			count++
		}
	}

	red /= count;
	green /= count;
	blue /= count;

	return red << 16 | green << 8 | blue;
}

So taking this a step further; say we want an array of 64 colours from an image, we can break the input image down into 64 chunks and find the averages of each. The easiest way of doing this is to build a grid, copy the pixels in each cell and use the averageColour function to return the average colour value of that cell.

public static function averageColours( source:BitmapData, colours:int ):Array
{
	var averages:Array = new Array();
	var columns:int = Math.round( Math.sqrt( colours ) );

	var row:int = 0;
	var col:int = 0;

	var x:int = 0;
	var y:int = 0;

	var w:int = Math.round( source.width / columns );
	var h:int = Math.round( source.height / columns );

	for (var i:int = 0; i < colours; i++)
	{
		var rect:Rectangle = new Rectangle( x, y, w, h );

		var box:BitmapData = new BitmapData( w, h, false );
		box.copyPixels( source, rect, new Point() );

		averages.push( averageColour( box ) );
		box.dispose();

		col = i % columns;

		x = w * col;
		y = h * row;

		if ( col == columns - 1 ) row++;
	}

	return averages;
}

In terms of building an accurate colour palette from an image, this isn’t a great method, mainly because averaging the colours doesn’t give you a fair representation of the range of colours in the image. The palette returned can often be somewhat muddy; however it is still a technique that I’ve found a use for on a surprisingly regular basis and so thought it worth sharing. Becoming familiar with bitwise operators is also a useful exercise and so, if they aren’t already part of your life, I suggest you begin to woo them with your nerdy charm.

Posted on 10 Oct 2008
17 Comments
8 Trackbacks

Meta

BitmapData Average Colours was posted on October 10th 2008 in the category Code / Actionscript 3.0, Flash, Open Source and tagged; , , , , , , .

You can Leave a comment.

Twitter <follow>

March 18th 2010 - 5:29pm

We've missed you Morris: RT @memotv: This guy is nuts. New trailer for Chris Morris' jihadist comedy Four Lions http://is.gd/aNzki

Discussion

25 Responses to BitmapData Average Colours

1 2 3

Leave a Reply

Pingbacks / Trackbacks

  1. 1 year ago AS3 Colour class for extracting the color palette from a BitmapData image or photo, ColourUtils.as

    [...] Comments BitmapData Average Colours 17% similar Finding the average colours in an image [...]

  2. 11 months ago Color Palettes of Game Advertisements in German Video Game Entertainment Magazins | MINDsPAGE

    [...] Soulwire – Finding the average colours in an image [...]

  3. 5 months ago Who wins the award for most popular pixel? | BLOG.RUEDAMINUTE>COM

    [...] Soulwire, a snippet that gets the average color in an [...]

  4. 5 months ago Case Study : ActionScript 3 Performance Optimization at Mike Chambers

    [...] an image, created by averaging the colors within that image. Upon searching on google, I found a very good solution over at soulwire.co.uk, which I will use as the base for creating the palette. I want to point out that the original code [...]

  5. 4 months ago Median Cut « GrgrDvrt

    [...] couleurs d’une belle image, tu peux tout de suite aller chez soulwire voir ces articles: couleurs moyennes et extraire une palette de couleurs, c’est plus simple et ça marche mieux. Par contre tu vas [...]

  6. 3 months ago Collage Creator | Flash Monkey

    [...] I actually did a quick Google search for this one before I started writing it myself and found Justin Windle’s post which goes into detail about how this works. Once I had the color values I just compare them to [...]

  7. 3 days ago Making the most of your toy robot (Part 3 of 4) – Object Detection | Electric Pineapple

    [...] spot is a colored ring that can be averaged out and determined to be in a specific range. Using the technique outlined here by Justin Windle we have averaged out the ring color and can ask if the RGB values are in the range [...]

  1. ynk 1 year ago

    hey there !

    very interesting stuff !

    i’ve made a color conversion class a while ago… if u wanna try u’ll find it here :
    http://code.google.com/p/martian-arts/source/browse/trunk/martian/soup/utils/Color.as

    Reply to this comment

  2. Adrian 1 year ago

    Great post, I’ve been working with color in AS3 and this tutorial helps me a lot with various tips. Thank you.
    Just a question: How can you export the average colors as a hexagesimal list and append it to a text file when you click an “export button”?

    Reply to this comment

  3. Soulwire 1 year ago

    Hi YNK

    Yeah, I’ve been using a colourConversion class myself for some time, its very useful. I’ve also been using classes for HSB, ARGB and RGB, though I see you have a method for converting to CMYK which is great! Thanks for sharing :)

    I’ve just been looking around your other classes, there is some fantastic stuff there mate. Keep up the good work, I’m a fan!

    Hi Adrian

    Yeah, its very simple, just trace each colour in the array to a string, passing 16 as the parameter and it will return the hex, so a loop like this should do it:

    var export:String = new String();
    
    for (var i:int = 0; i < colours.length; i++)
    {
    	export += '#' + colours[i].toString(16);
    	if ( i < colours.length - 1 ) export += ',';
    }
    
    trace( export );
    // Save to a text file
    

    The saving could be done with a server side script or through AIR. The button in my demo uses the setClipboard method, so if this is just for your personal use you can call this method then paste it into the txt file yourself.

    System.setClipboard( export  );

    If you were using AIR, there is all sorts of potential for looking at writing the colours into a Photoshop palette or something similar.

    Hope that helps

    Reply to this comment

  4. Vadim 1 year ago

    I dont think this is good way to get average colors as you want. Two loops work much longer than just one bitmapData resize operation. Resize it to (for example) 3×4 pixels without smoothing – and you will det you 12 pixels with 12 different colors.
    Anyway, both methods are rubbish, but simple.

    Reply to this comment

  5. Vadim 1 year ago

    I mean rubbish if you want GIF-alike palette

    Reply to this comment

  6. Soulwire 1 year ago

    Hi Vadim,

    Yep, you can certainly do it that way, you’re right. It does actually yield very different results though (I just drew the source to a new BitmapData using a scaled Matrix), but I guess this would be to do with how flash player scales images, and I can’t claim to know how that works internally (bicubic interpolation I would assume?)

    To get back to your general point though, I would aggree, a ton of getPixels isn’t ideal. The main purpose of this post was to talk about bitwise operators and getting the RGB values from a uint etc. In the post about getting the best color palette from an image I used paletteMap, maybe this is best for the GIF-alike look you mentioned? There’s also Ralph Hauwert’s image dithering technique.

    Reply to this comment

  7. Vadim 1 year ago

    Yes, that is exactly what I’m talking about, thanks for link!
    You know, I used some work with pixels in my drawing game, Sketch2, that was interesting

    Reply to this comment

  8. Bas Horsting 1 year ago

    An idea: If you are lucky enough to work with OS X Leopard, you can clip this little application and put it on your Dashboard for future use, as I did. Great little widget this way! Thanks for sharing.

    Reply to this comment

  9. SUTUTUYET 1 year ago

    i am working on the Coloring Project and that is exactly what I’m looking for, but my question now is that how can I trace the color code(or add the function to the box) when i click the color box
    thanks

    Reply to this comment

  10. Gaggo 11 months ago

    Hi,

    just wanted to tell you that I really love your work, this blog, the code snippets and everything else around here! Keep up the good work!

    best
    Rasso

    Reply to this comment

  11. Ramón Illobre 10 months ago

    You dont need to upload anything. The FileReference class (FP10) has a load method that loads a local file into flash. When the file is load an Event.COMPLETE is dispatched and the file.data is filled with the image ByteArray.

    http://help.adobe.com/en_US/AS3LCR/Flash_10.0/flash/net/FileReference.html#load()

    Reply to this comment

  12. Soulwire 10 months ago

    Cheers Ramon!

    Yep, I’m aware of the FP10 capabilities. Unfortunately these demos were published for FP9 and using a different approach. I was hoping to grab an evening this week to change my .htaccess file etc, and get this working, though your suggestion for using FP10 is a good one and I think I’ll do just that!

    The only problem is; if I go FP10 for these demos then I’ll feel like I should update the source too to use typed arrays (vectors) and maybe even Pixel bender to make these algorithms more efficient! ;)

    Thanks for the comment – I’ll fix the fileSystem stuff asap and then can think about getting these algorithms running at top speed!

    Reply to this comment