The idea for today's example comes from Jonathan. Jonathan was asking: "I’d like to request a tutorial. One thing I’d really like to know how to do is analyze an image for it’s color “paletteâ€, or in other words, to find what the primary colors are that are used in an image."
I don't know if this is exactly what he meant but I hope this example can be used as a kickstart for color analysis. The code in the example reduces the image colors to 10, then discards all but one pixel of every color and then creates the palettes out of those colors. This might not be the most accurate way to do this, but at least it's fast
The code creates three different "palettes" from image; average palette, dark palette and bright palette. I got the idea for three different palettes from MarcosBL at freenode.
Here is the code:
-
<?php
-
-
/* The original image is the average colors */
-
$average = new Imagick( "test.png" );
-
-
/* Reduce the amount of colors to 10 */
-
$average->quantizeImage( 10, Imagick::COLORSPACE_RGB, 0, false, false );
-
-
/* Only save one pixel of each color */
-
$average->uniqueImageColors();
-
-
/* Clone the average and modulate to brighter */
-
$bright = $average->clone();
-
$bright->modulateImage ( 125, 200, 100 );
-
-
/* Clone the average and modulate to darker */
-
$dark = $average->clone();
-
$dark->modulateImage ( 80, 100, 100 );
-
-
/* Helper function to create the mini-images */
-
function createImages( Imagick $composite, Imagick $im )
-
{
-
/* Get ImagickPixelIterator */
-
$it = $im->getPixelIterator();
-
-
/* Reset the iterator to begin */
-
$it->resetIterator();
-
-
/* Loop trough rows */
-
while( $row = $it->getNextIteratorRow() )
-
{
-
/* Loop trough columns */
-
foreach ( $row as $pixel )
-
{
-
/* Create a new image which contains the color */
-
$composite->newImage( 20, 20, $pixel );
-
$composite->borderImage( new ImagickPixel( "black" ), 1, 1 );
-
}
-
}
-
}
-
/* This object holds the color images */
-
$composite = new Imagick();
-
-
/* Create "icons" for each palette */
-
createImages( $composite, $dark );
-
createImages( $composite, $average );
-
createImages( $composite, $bright );
-
-
/* Montage the color images into single image
-
Ten images per row, three rows */
-
$montage = $composite->montageImage( new imagickdraw(), "10x3+0+0",
-
"20x20+4+3>", imagick::MONTAGEMODE_UNFRAME,
-
"0x0+3+3" );
-
-
/* Free some resources */
-
$composite->destroy();
-
-
/* Create an empty canvas */
-
$canvas = new Imagick();
-
$canvas->newImage( $montage->getImageWidth() + 55,
-
$montage->getImageHeight(),
-
new ImagickPixel( "white" ) );
-
-
/* Display the canvas as png */
-
$canvas->setImageFormat( "png" );
-
-
/* Set font size to 12 points */
-
$draw = new ImagickDraw();
-
$draw->setFontSize( 12 );
-
-
/* Create legends for each palette */
-
$canvas->annotateImage( $draw, 5, 20, 0, "Dark: " );
-
$canvas->annotateImage( $draw, 5, 45, 0, "Average: " );
-
$canvas->annotateImage( $draw, 5, 70, 0, "Bright: " );
-
-
/* Composite the montaged images next to texts */
-
$canvas->compositeImage( $montage, Imagick::COMPOSITE_OVER, 55, 0 );
-
-
/* Output the image */
-
echo $canvas;
-
-
?>
Edited the example to work with 2.0.x
The first source image:

The created palette:

The second source image:

And the palette:

And one more:

Palette:

Pingback: Propiedad Privada » Blog Archive » CSS Color Generator (from pics)
Trackback: PHPDeveloper.org
Pingback: developercast.com » Mikko Koppanen’s Blog: Color analysis
#1 by Jonathan on November 5, 2007 - 11:05 pm
Quote
Mikko,
Thanks for the writeup! I got it working with only two changes. Since I’m using a slightly older Imagick build, newImage and borderImage require ImagickPixel objects for the color instead of strings. So,
Line 42 was changed to $composite->newImage( 20, 20, $pixel );
Line 43 was changed to $composite->borderImage( new ImagickPixel(“black”), 1, 1 );
Lines 35-40 were no longer needed.
My goal for this technique it to retrieve an array of the top 10 colors since I’m probably going to output them as HTML. I’ll post back later today after I experiment with your code a bit.
Thanks again!
#2 by Mikko Koppanen on November 5, 2007 - 11:10 pm
Quote
Jonathan,
sorry about that one. I always forget things (I am working on Imagick 2.1.0 while I’m writing the examples).
#3 by Mikko Koppanen on November 5, 2007 - 11:11 pm
Quote
P.S. I hope this helps you to start!
#4 by Mikko Koppanen on November 5, 2007 - 11:19 pm
Quote
I edited the example to work with 2.0.x. Thanks for the heads up. The lines were there since I first tested to count the colors.
You need to do more than that, I noticed that you will get a lot of colors like rgb(0,0,0) rgb(0,1,0). A lot of very dark and very white colors.
So I suggest that you use ImagickPixel->isSimilar() to filter the colors (otherwise you will have palette where you have only maximas; white, black and so on).
#5 by Jonathan on November 5, 2007 - 11:23 pm
Quote
I got my color array for the web, which is what I was going for. For anyone else who is curious how to do this, using Mikko’s example, here’s the code (starting after line 20). Note: I’m only building the “average” colors here.
[code]
function rgb2hex($color)
{
return sprintf('%02X%02X%02X',$color["r"], $color["g"], $color["b"]);
}
/* Helper function to create the mini-images */
function buildColors(&$colorArray, Imagick $im )
{
/* Get ImagickPixelIterator */
$it = $im->getPixelIterator();
/* Reset the iterator to begin */
$it->resetIterator();
/* Loop trough rows */
while( $row = $it->getNextIteratorRow() )
{
/* Loop trough columns */
foreach ( $row as $pixel )
{
array_push($colorArray, rgb2hex($pixel->getColor()));
}
}
}
/* Storage for the colors */
$colorArray = array();
/* Build color array from the image */
buildColors( $colorArray, $average );
print_r($colorArray); // show the colors
[/code]
#6 by Jonathan on November 5, 2007 - 11:31 pm
Quote
Mikko, I’m not sure what you mean to use isSimilar() for? I’m pretty much using your example to get my color palette, I’m just planning to storage it in an array for HTML outputting instead of building a new image. Would isSimilar make the palette more accurate?
#7 by Mikko Koppanen on November 5, 2007 - 11:41 pm
Quote
Ah,
I misunderstood you. Forget about that
Pingback: links for 2007-11-06 « Stand on the shoulders of giants
Pingback: Analisi dei colori di un’immagine con PHP / Melodycode.com - Life is a flash
#8 by Jonathan on November 8, 2007 - 4:51 am
Quote
Mikko,
I finally got a chance to put this example to action, but I am getting wierd numbers from ImagickPixel.getColor(). For example, if I output the color around line 35, print_r($pixel->getColor()); the RGB numbers are always less than 100. If I output the colors via HTML, they are very dark. Why are do the RGB numbers from getColor() seem incorrect?
Thanks,
Jonathan
#9 by Jonathan on November 8, 2007 - 4:57 am
Quote
I was browsing through CVS and saw that you fixed something with ImagickPixel->getColor() about a week ago. I’m guessing this is my problem..? If so, any chance we can get a new Win32 build?
#10 by Mikko Koppanen on November 8, 2007 - 9:05 pm
Quote
I did fix issues with getColor. Previously it parsed the color from a string (which was not very good).
I will update the Windows builds as soon as I get Visual Studio Standard/Pro license somewhere. I like to compile ImageMagick by hand and the configuration requires MFC (not present in the Express editions).
In the meanwhile try ImagickPixel::getColorValue() ?
#11 by Jonathan on November 8, 2007 - 10:14 pm
Quote
Ok, I got getColorValue to work. Something like this…
function rgb2hex($r, $g, $b)
{
$r *= 255;
$g *= 255;
$b *= 255;
$r = (int)($r > 0.0 ? $r + 0.5 : $r – 0.5);
$g = (int)($g > 0.0 ? $g + 0.5 : $g – 0.5);
$b = (int)($b > 0.0 ? $b + 0.5 : $b – 0.5);
return sprintf(“%02X%02X%02X”, $r, $g, $b);
}
$hexColor = rgb2hex($pixel->getColorValue(Imagick::COLOR_RED), $pixel->getColorValue(Imagick::COLOR_GREEN), $pixel->getColorValue(Imagick::COLOR_BLUE));
Not the most efficient way to do things, but good enough for now I suppose
#12 by Mikko Koppanen on November 8, 2007 - 10:20 pm
Quote
Yeah, I know. That should’ve been fixed ages ago. So little time and so much to do..
Pingback: links for 2007-11-11 « Mandarine
Pingback: Weekend Links - MooTools Rubberband, Flickable, MooMonth, PHP Image Colors, Prototype 1.6 & Scriptaculous 1.8
#13 by Jonathan on November 14, 2007 - 2:15 am
Quote
Mikko,
I have a followup question regarding this post. It looks like when the colors are reduced in the image, they are ordered from lightest to darkest. I am wondering if there’s a way to have the colors ordered based on which colors are most prominent in the picture? Or maybe instead it would be cool if there’s a way to find what percentage of the picture consisted of each color, so they can be ordered? What do you think?
Jonathan
#14 by Mikko Koppanen on November 14, 2007 - 2:37 am
Quote
Jonathan,
Skip uniqueImageColors and add each color string to an array. You should easily get a count out of that.
#15 by Jonathan on November 14, 2007 - 3:26 am
Quote
Thanks the response Mikko. At first I tried iterating through all the pixels in the image, but it took forever to execute. Anthony on the ImageMagick forum pointed me in the right direction though, and I ended up using getImageHistogram instead of uniqueImageColors, which still gives me the unique image colors along with counts for each color. The execution time is only a little slower.
Thanks,
Jonathan
#16 by Michael Bushey on November 17, 2007 - 6:18 pm
Quote
This is a really cool script, thank you. Trying to run it I get:
Fatal error: Call to undefined method Imagick::uniqueImageColors() in /home/michael/palette.php on line 10
# dpkg -l | grep php5-im
ii php5-imagick 2.0.1-1 ImageMagick module for php5
Thanks!
Michael
#17 by Mikko Koppanen on November 18, 2007 - 11:46 am
Quote
Michael,
Imagick::uniqueImageColors is present if you have ImageMagick 6.2.9 or newer.
#18 by Steven on November 20, 2007 - 7:59 pm
Quote
Lovely! The list of applications for this is huge!
Neat stuff.
But first I need Imagick…
#19 by Cristiano on February 1, 2008 - 1:26 am
Quote
Hi
I’m getting this error
Fatal error: Class ‘Imagick’ not found
and I have the php_imagick.dll installed with my php.
Did you come around an error like this?
I have PHP, APACHE, MYSQL installed on a windows xp…
Thanks
#20 by Anthony Thyssen on February 4, 2008 - 3:07 am
Quote
Rather this using the pixel iterator to extract each color from the unique colors image, it should be possible to use Crop to split that image into 10 1×1 pixel images. These can then be resized to form a lager image. Should be a lot better then iterating though the image yourself!!!
I am not certain on how this is done in PHP Imagick, but I have done this with the command line API.
Anthony — IM Examples Webmaster
#21 by Mikko Koppanen on February 4, 2008 - 3:41 am
Quote
Anthony,
that would probably be more performance friendly. All thou I thought that this example would be a good place to illustrate PixelIterator usage. I haven’t figured out that many examples that would use PixelIterator.
#22 by John on May 19, 2008 - 10:59 am
Quote
Hi Makko,
I installed imagick, I copied your code on my page php, but it doesn’t work. When at the end of the code I do echo $canvas, I don’t see image.
Can you help me?
#23 by John on May 22, 2008 - 11:44 pm
Quote
Hi Mikko,
I installed imagick, I copied your code on my page php, but it doesn’t work. When at the end of the code I do echo $canvas, I don’t see image.
Can you help me?
#24 by John on May 22, 2008 - 11:44 pm
Quote
Hi Mikko,
I installed imagick, I copied your code on analysis colors on my page php, but it doesn’t work. When at the end of the code I do echo $canvas, I don’t see image.
Can you help me?
#25 by fedmich on May 27, 2008 - 10:18 am
Quote
Wow, your posts are really interesting
a lot of wise and userful use of imagemagik
Thanks and pls post more of these
#26 by Spexfox on June 1, 2008 - 4:39 am
Quote
holy crap, thank you! This script is just what I needed to get started on a project! Awesome! =)
#27 by virus-2k on June 19, 2008 - 3:44 pm
Quote
hm how do I write the Image-Color-Hex-Code under each color? :S
[php]
while( $row = $it->getNextIteratorRow() )
{
/* Loop trough columns */
foreach ( $row as $pixel ) {
$currentColor = $pixel->getColor();
$pColor = sprintf( ‘%02X%02X%02X’, $currentColor['r'], $currentColor['g'], $currentColor['b'] );
} }
[/php]
This is the possibility to get the color of the pixel column.
But how to write under it? :S
#28 by Mikko Koppanen on June 28, 2008 - 7:08 pm
Quote
virus-2k,
you can use imagick::annotateImage to write text.
Pingback: scottluxford.com » Blog Archive » First Proof
#29 by andi on July 21, 2008 - 12:31 pm
Quote
hello mikko,
do you know how can i restrict the colors I get from quantizeImage() to web safe colors only..?
Pingback: Resources on the web « Mcloide’s Weblog
Pingback: Resources on the web « Mcloide’s ideas tornado
#30 by Richard on September 20, 2008 - 8:18 am
Quote
This is nearly exactly what I was looking for. (And already coded before I thought to look more thoroughly [DOH!])
I’m working on an open source replacement for Vector Magic ( http://www.vectormagic.com ) and color analysis is a critical piece of the puzzle.
Vector Magic’s color analysis seems like it works like this:
1. All “Similar” colors which occur more than X times in the image are averaged together (With preference to colors used more often in the image)
2. The top X colors which are used in more than x% of the image are selected by default as they’re likely the most important colors in the image.
Implementing that has been a bit of a stumbling block, though.
Any help would be greatly appreciated.
Pingback: Bookmarks for November 11th at dpwolf/blog
#31 by dan on December 29, 2008 - 4:06 pm
Quote
hi. can you help me, please? how can it be realized with the help of imagemagick program from console (using linux)?
the idea is to find out what are the most colors used at the picture?
thanx.
#32 by Tim Hunter on January 12, 2009 - 1:16 am
Quote
Nice tutorial, Mikko! After being asked how to do this by an RMagick user I found your page via the ImageMagick Forums. I’ve converted your example to Ruby and RMagick for this user and for any others who ask later on.
Again, very nice example! Thanks for the effort.
#33 by Pink on January 22, 2009 - 4:49 pm
Quote
what a nice story..
#34 by Fletch on February 7, 2009 - 9:26 am
Quote
Thanks for all your tutorials. I am just getting started with Image Magick and you have helped me out a lot. I am using imagick version 2.2.2 and for me to get this example to work I had to set a font type. I put this between lines 68 and 69
$draw->setFont(‘arial.ttf’);
Again thanks for all these super examples.
#35 by Patrick Fisher on February 8, 2009 - 1:01 pm
Quote
A nifty side effect going on: an optical illusion. In the spaces between the color samples, it looks like there are small grey squares.
Pingback: Whoila Blog » Blog Archive » More color palette and PHP
#36 by Alexwebmaster on March 3, 2009 - 12:44 pm
Quote
Hello webmaster
I would like to share with you a link to your site
write me here preonrelt@mail.ru
#37 by Ryan on March 4, 2009 - 8:48 am
Quote
Greetings,
Is this still being updated? I’m receiving an error when I attempt to run this script.
Parse error: syntax error, unexpected T_STRING, expecting ‘)’ in /nfs/c01/h06/mnt/36280/domains/staging.mybrokenbox.com/html/image.php on line 22
I’m trying to run it straight from the example, is this a working example?
#38 by Alessandro on April 8, 2009 - 4:25 pm
Quote
Good morning, i would like to use it to make a search engine by color selection but i don’t know how to do, if interested in this work please contact me and will discuss about the budget.
thanks