
- Drop me a line! Contact me here.
Image Mosaics with GD
Image Mosaics with GD
Image mosaics are pretty damn cool. They're those pictures that were fantastically popular in advertising during the late 1990s, invented by a clever chap at MIT. A picture made up of hundreds, even thousands, of tiny little pictures. Actually making them seems like a complicated affair, but with some PHP, the GD library, and MySQL its not as bad as it might appear. In fact, the principle is extremely simple.
On a computer images are made up of pixels. Each one is a solid block of colour. Computer based images are all made up of this individual building blocks.
If you take a small image, and add up the colour values for all the individual pixels, then divide the total by the number of pixels in the image you will end up with the average colour value for the overall image. So, if you have an image of, for example, a lawn, the average image colour will be a shade of green. This is the key to making an image mosaic. We take an image, and for each of the pixels in it we draw a small image that has approximately the same average colour. When viewing the larger image our eyes see the average colour and build the original image back up.
The image mosaic process is made up of 2 distinct stages. First of all you need to collect the images you will be using as "tiles", the small images. These all need to have their average colour values calculated and stored. Then, once you have enough images to make a good looking mosaic, you need to go through another image taking one pixel at a time and finding a small image of approximately the same overall colour. These small images are copied into a larger grid that will eventually become the final mosaic image.
As we step through the tile images we need to calculate the average colour of each one. In this example we'll use a MySQL database as there are RAM limitations in some PHP installations. If you're running the code on your own server it might make sense to copy the colour data into memory when it comes to generating the mosaic.
Each of the colour channels, red, green, and blue is extracted from each pixel, and the values are added together in an array. Also, a count of the total number of pixels is kept.
On a computer images are made up of pixels. Each one is a solid block of colour. Computer based images are all made up of this individual building blocks.
If you take a small image, and add up the colour values for all the individual pixels, then divide the total by the number of pixels in the image you will end up with the average colour value for the overall image. So, if you have an image of, for example, a lawn, the average image colour will be a shade of green. This is the key to making an image mosaic. We take an image, and for each of the pixels in it we draw a small image that has approximately the same average colour. When viewing the larger image our eyes see the average colour and build the original image back up.
The image mosaic process is made up of 2 distinct stages. First of all you need to collect the images you will be using as "tiles", the small images. These all need to have their average colour values calculated and stored. Then, once you have enough images to make a good looking mosaic, you need to go through another image taking one pixel at a time and finding a small image of approximately the same overall colour. These small images are copied into a larger grid that will eventually become the final mosaic image.
<?php
$tiledir = "tiles";
$dir = openDir($tiledir);
while ($file = readDir($dir)) {
if ($file != "." and $file != "..") {
//Process tile image and //save information to database
}
}
?>
As we step through the tile images we need to calculate the average colour of each one. In this example we'll use a MySQL database as there are RAM limitations in some PHP installations. If you're running the code on your own server it might make sense to copy the colour data into memory when it comes to generating the mosaic.
<?php
$image = imagecreatefromjpeg($tiledir."/".$file);
for ($x=0;$x<imagesx($image);$x++) {
for ($y=0;$y<imagesy($image);$y++) {
$rgb = imagecolorat($image,$x,$y);
$r = ($rgb >> 16) & 0xFF;
$g = ($rgb >> 8) & 0xFF;
$b = $rgb & 0xFF;
$i[$file]['r'] += $r; $i[$file]['g'] += $g; $i[$file]['b'] += $b; $i[$file]['total']++;
}
}
?>
Each of the colour channels, red, green, and blue is extracted from each pixel, and the values are added together in an array. Also, a count of the total number of pixels is kept.
<?php
$sql = "insert into sourceimage (filename, r, g, b) ";
$sql .= "values ";
$sql .= "("; $sql .= "'".$file."',";
$sql .= "'".round($i[$file]['r']/$i[$file]['total'])."',";
$sql .= "'".round($i[$file]['g']/$i[$file]['total'])."',";
$sql .= "'".round($i[$file]['b']/$i[$file]['total'])."',";
$sql .= ");";
?>
We have kept the colour channels seperate in the database. This helps speed things up a little later on. In general it is always preferable to work on colour channels seperately, it makes things go a little faster and produces nicer results.
Now we have a bank of tiles to use we can set about making our first mosaic. We need to select a relatively small image file. If we're using 32*32 pixel tiles, then a 64*64 source file will generate a mosaic image that is 2048*2048 pixels square. Thats a large image file, and requires a lot of memory to generate.
Once we have the source image open we need to open a blank canvas to copy our tiles to.
$s is the size of our tile images.
The pixel colour processing stage is very similar to the code that was used to calculate the pixel colour averages in the first stage of the mosaic process. We select a pixel, then calculate the value of each colour channel. Once we have these we can then go to the database and select the closest matching tile image.
Calculating the nearest colour is a matter of subtracting the channel value in the database from our extracted channel value, and adding up the absolute values for each of the three channels.
Taking the absolute value of the difference between two numbers gives you the difference between the two values. If there is a large difference you will have a large integer, if there is a small difference you will have a small integer. Adding together the differences between each channel gives you the overall difference in colour between the tile image and the source pixel. By finding the tile with the smallest difference you can find which image will most closely represent the pixel colour of the source.
Each source pixel colour is copied into $rgbCache to save having to calculate the same pixel colour twice.
Now we have a bank of tiles to use we can set about making our first mosaic. We need to select a relatively small image file. If we're using 32*32 pixel tiles, then a 64*64 source file will generate a mosaic image that is 2048*2048 pixels square. Thats a large image file, and requires a lot of memory to generate.
<?php
$mosaic = "mosaic.jpg";
$image = imagecreatefromjpeg($mosaic);
?>
Once we have the source image open we need to open a blank canvas to copy our tiles to.
<?php
$output = imagecreatetruecolor((imagesx($image)*$s), (imagesy($image)*$s));
?>
$s is the size of our tile images.
<?php
for ($y=0;$y<imagesy($image);$y++) {
for ($x=0;$x<imagesx($image);$x++) {
//Process pixel colour
}
}
?>
The pixel colour processing stage is very similar to the code that was used to calculate the pixel colour averages in the first stage of the mosaic process. We select a pixel, then calculate the value of each colour channel. Once we have these we can then go to the database and select the closest matching tile image.
Calculating the nearest colour is a matter of subtracting the channel value in the database from our extracted channel value, and adding up the absolute values for each of the three channels.
Taking the absolute value of the difference between two numbers gives you the difference between the two values. If there is a large difference you will have a large integer, if there is a small difference you will have a small integer. Adding together the differences between each channel gives you the overall difference in colour between the tile image and the source pixel. By finding the tile with the smallest difference you can find which image will most closely represent the pixel colour of the source.
<?php
$rgb = imagecolorat($image,$x,$y);
$r = ($rgb >> 16) & 0xFF;
$g = ($rgb >> 8) & 0xFF;
$b = $rgb & 0xFF;
if ($rgbCache[$rgb] == "") {
$sql = "select * from sourceimage order by abs(".$r."-rVal) + abs(".$g."-gVal) + abs(".$b."-bVal) limit 1";
$result = mysql_query($sql,$databaseLink);
$record = mysql_fetch_object($result);
$rgbCache[$rgb] = $record->filename;
}
?>
Each source pixel colour is copied into $rgbCache to save having to calculate the same pixel colour twice.
<?php
$tile = imagecreatefromjpeg("tiles/".$rgbCache[$rgb]);
imagecopy($output,$tile,$x*$s,$y*$s,0,0,$s,$s);
?>
- © Ooer.com Chris Neale 2007
- PHPGD.com
- Powered by PHP
- Database by MySQL
- DB Queries: 3
- DB Time: 0.0633 seconds
