[LIST=1][*]<?php[*]/**[*]* Purpose: A photographic mosaic generator[*]* Description: Gathers information about images and composes a photographic mosaic[*]*[*]* @version Date: 1. may 2009[*]* @author �an Kafol[*]* @access public[*]*/[*] [*]debug('--- STARTING MOSAIC ---');[*] [*]/*[*] * Set system boundaries and error reporting[*] */[*] [*][URL="http://www.php.net/error_reporting"]error_reporting[/URL](E_ALL);[*][URL="http://www.php.net/set_time_limit"]set_time_limit[/URL](0);[*][URL="http://www.php.net/ini_set"]ini_set[/URL]('memory_limit','3G');[*] [*]/*[*] * Some parameters...[*] */[*] [*]//how big is the 'pixel' or piece of the composed mosaic image[*]$piece_w = 256;[*]$piece_h = 256;[*] [*]//smaller scale of the template in pixels (in large scale this is $mosaic_w*$piece_w)[*]$mosaic_w = 30;[*]$mosaic_h = 45;[*] [*]//algorithm for the average pixel scan (1=arithmetic_mean,2=harmonic_mean,3=median,4=modal_score,5=resample(),6=resize)[*]$average_type = 5;[*]//override this setting if it's passed as an argument[*]if([URL="http://www.php.net/count"]count[/URL]($argv)>1) $average_type = [URL="http://www.php.net/intval"]intval[/URL]($argv[1]);[*] [*]//jpeg compression quality [0..100], where 100 is best quality[*]$quality = 90;[*] [*]//Where are your images stored (will be scaned recursively for .jpg files)[*]$imgdir = "xx";[*] [*]//Template image (will be resized to $mosaic_w x $mosaic_h)[*]$source_img = "20090409-_MG_6779.jpg";[*] [*]//Filename of the generated mosaic[*]$output_image = "mosaic_$average_type.jpg";[*] [*]//Index cache filename[*]$cache_idx = "cachergb-$average_type.txt";[*] [*]//Cache in case something crashes[*]$generated_idx = "composit-$source_img-$mosaic_w-$mosaic_h-$piece_w-$piece_h-$average_type.txt";[*] [*]//Cache smaller pieces (useful if your images are as-is, and once it has cache, this really speeds things up)[*]$piece_cache = "pieces";[*] [*]/*[*] * Start processing![*] *[*] * a brief explanation on how this works:[*] * - Get all available images (their relative filenames) in the array[*] * - Loop through the array of images, extract their 'average pixel' - a one-color representation of the whole image in a 4D array[*] *              where the first three dimensions are the [R][G][B] colorspace and the fourth is the array of image filenames that represent[*] *              the current RGB color. Cache the array to disk, as the scan is intensive and the array is easily reused for a new photographic mosaic[*] * - When we have our available pixel colors and their image representation in the array, we can begin building our mosaic.[*] *              We take our template image and shrink it way down, because every pixel in that template will be an image. Loop through pixels, find the[*] *              best approximation to the pixel color we have in our RGB array (there is little probability, that we have the exact pixel), and paste[*] *              the image to the coordinate in the large-scale mosaic relative to the pixel in the template[*] */[*] [*]debug("average type set to $average_type !");[*]debug('Getting available images...');[*]//recursively scan all images in the directory and save them in the array[*]$all_images = get_images($imgdir);[*]if(!$all_images) {[*]        debug("invalid directory");[*]        [URL="http://www.php.net/die"]die[/URL];[*]}[*] [*]//Read indexes from cache - check if images exists[*]$_rgb_indexes = uncache_var($cache_idx);[*]foreach($_rgb_indexes as $r=>$gb_idx) {[*]        foreach($gb_idx as $g=>$b_idx) {[*]                foreach($b_idx as $b=>$images) {[*]                        foreach($images as $image) {[*]                                if([URL="http://www.php.net/file_exists"]file_exists[/URL]($image)) {[*]                                        $rgb_indexes[$r][$g][$b][] = $image;[*]                                        $cached[$image] = true;[*]                                } else {[*]                                        debug("!!! Indexed file [$image] does not exist anymore!");[*]                                }[*]                        }[*]                }[*]        }[*]}[*] [*]//Index all images, and skip the ones that are cached, add the ones that are not cached (new)[*]debug('Indexing '.[URL="http://www.php.net/count"]count[/URL]($all_images).' images for average pixel...');[*]$bfin = $proctime = 1;[*]foreach($all_images as $i=>$image) {[*]        if(![URL="http://www.php.net/isset"]isset[/URL]($cached[$image]) || !$cached[$image]) {[*]                $proctime = [URL="http://www.php.net/microtime"]microtime[/URL](true);[*]               [*]                $piece = generate_piece($image,$piece_w,$piece_h,$piece_cache,$quality);[*]               [*]                if(!$piece) {[*]                        debug("something went wrong!");[*]                        [URL="http://www.php.net/die"]die[/URL];[*]                }[*]               [*]                [URL="http://www.php.net/list"]list[/URL]($r,$g,$b) = get_average_pixel($piece,$average_type);[*]                [URL="http://www.php.net/imagedestroy"]imagedestroy[/URL]($piece);[*]               [*]                $rgb_indexes[$r][$g][$b][] = $image;[*]                $cached[$image] = true;[*]                $n_pix = [URL="http://www.php.net/count"]count[/URL]($rgb_indexes[$r][$g][$b]);[*]               [*]                cache_var($rgb_indexes,$cache_idx);[*]               [*]                $percent = $i/[URL="http://www.php.net/count"]count[/URL]($all_images);[*]                $proctime = [URL="http://www.php.net/microtime"]microtime[/URL](true) - $proctime;[*]                $fin = percent($percent);[*]                $eta = duration(($proctime*(1-$percent)) / ($percent-$bfin));[*]                debug("INDEX $image [$fin%] [ETA: $eta] [avgpx($r,$g,$b)] [c: $n_pix]");[*]                $bfin = $percent;[*]                $bproctime = $proctime;[*]        }[*]}[*]cache_var($rgb_indexes,$cache_idx);[*] [*] [*]//Prepare the template and allocate the memory for the mosaic[*]debug('Preparing for composit...');[*]if(![URL="http://www.php.net/file_exists"]file_exists[/URL]($source_img)) {[*]        debug('template does not exist!');[*]        [URL="http://www.php.net/die"]die[/URL];[*]}[*]$original = [URL="http://www.php.net/imagecreatefromjpeg"]imagecreatefromjpeg[/URL]($source_img);[*]$template = [URL="http://www.php.net/imagecreatetruecolor"]imagecreatetruecolor[/URL]($mosaic_w,$mosaic_h);[*][URL="http://www.php.net/imagecopyresampled"]imagecopyresampled[/URL]($template,$original,0,0,0,0,$mosaic_w,$mosaic_h,[URL="http://www.php.net/imagesx"]imagesx[/URL]($original),[URL="http://www.php.net/imagesy"]imagesy[/URL]($original));[*][URL="http://www.php.net/imagedestroy"]imagedestroy[/URL]($original);[*] [*]debug('Checking for cached composit...');[*]$gen_pieces = uncache_var($generated_idx);[*]if([URL="http://www.php.net/count"]count[/URL]($gen_pieces)>0) {[*]        debug('Opening cached composit...');[*]        $mosaic = [URL="http://www.php.net/imagecreatefromjpeg"]imagecreatefromjpeg[/URL]($output_image);[*]        if(!$mosaic) {[*]                debug("corrupt index and/or dumped file! cleaning up...");[*]                if([URL="http://www.php.net/file_exists"]file_exists[/URL]($generated_idx)) [URL="http://www.php.net/unlink"]unlink[/URL]($generated_idx);[*]                debug("restart the composit! aborting...");[*]                [URL="http://www.php.net/die"]die[/URL];[*]        }[*]} else {[*]        debug('Allocating memory for the new image...');[*]        $mosaic = [URL="http://www.php.net/imagecreatetruecolor"]imagecreatetruecolor[/URL]($mosaic_w*$piece_w,$mosaic_h*$piece_h);[*]}[*] [*]//Start putting photos on the image as they were pixels[*]debug('Generating composition ...');[*]$loops = 0;[*]for($i=0;$i<$mosaic_w;$i++) {[*]        for($j=0;$j<$mosaic_h;$j++) {[*]                //if this is a fix, skip pieces that are already generated[*]                if([URL="http://www.php.net/isset"]isset[/URL]($gen_pieces[$i][$j]) && $gen_pieces[$i][$j]) {[*]                        debug("skipping pixel [$i][$j] ...");[*]                        continue;[*]                }[*]               [*]                $proctime = [URL="http://www.php.net/microtime"]microtime[/URL](true);[*]               [*]                [URL="http://www.php.net/list"]list[/URL]($r,$g,$b) = rgb([URL="http://www.php.net/imagecolorat"]imagecolorat[/URL]($template,$i,$j));[*]               [*]                //Find the best matching color[*]                foreach($rgb_indexes as $x1=>$xi) {[*]                        foreach($xi as $y1=>$yi) {[*]                                foreach($yi as $z1=>$zi) {[*]                                        $distance = distance($x1,$y1,$z1,$r,$g,$b);[*]                                        if(![URL="http://www.php.net/isset"]isset[/URL]($min_distance) || $distance<$min_distance) {[*]                                                $min_distance = $distance;[*]                                                $br = $x1;[*]                                                $bg = $y1;[*]                                                $bb = $z1;[*]                                        }[*]                                }[*]                        }[*]                }[*]                //This estimate is preety bad[*]                // $kbr = $kbg = $kbb = 999;[*]                // foreach($rgb_indexes as $red_index => $rest_indexes) if(abs($r-$red_index) < abs($r-$kbr)) $kbr = $red_index;[*]                // foreach($rgb_indexes[$kbr] as $green_index => $rest_indexes) if(abs($g-$green_index) < abs($g-$kbg)) $kbg = $green_index;[*]                // foreach($rgb_indexes[$kbr][$kbg] as $kblue_index => $images) if(abs($b-$kblue_index) < abs($b-$kbb)) $kbb = $kblue_index;[*]                // $prev_dist = distance($kbr,$kbg,$kbg,$r,$g,$b);[*]               [*]               [*]                //At these RGB coordinates, we have one or more images. Choose a random one.[*]                $best_match_pixels = $rgb_indexes[$br][$bg][$bb];[*]                $n_matching = [URL="http://www.php.net/count"]count[/URL]($best_match_pixels)-1;[*]                $r_match = [URL="http://www.php.net/rand"]rand[/URL](0,$n_matching);[*]                $best_match = $best_match_pixels[$r_match];[*]                $distincts[$best_match] = true;[*]               [*]                //Put the piece on the composit[*]                $piece = generate_piece($best_match,$piece_w,$piece_h,$piece_cache,$quality);[*]                $success = [URL="http://www.php.net/imagecopy"]imagecopy[/URL]($mosaic,$piece,$i*$piece_w,$j*$piece_h,0,0,$piece_w,$piece_h);[*]                [URL="http://www.php.net/imagedestroy"]imagedestroy[/URL]($piece);[*]               [*]                //Print some statistical data on the screen[*]                $percent = ++$loops/($mosaic_w*$mosaic_h);[*]                $proctime = [URL="http://www.php.net/microtime"]microtime[/URL](true) - $proctime;[*]                $eta = duration(($proctime*(1-$percent)) / ($percent-$bfin));[*]                $fin = percent($percent);[*]                $est = percent((distance(0,0,0,255,255,255)-$min_distance)/distance(0,0,0,255,255,255));[*]                $ests[] = $est; $r_match++; $n_matching++;[*]                debug("COMP [$fin%] [ETA: $eta] $est% match [$r_match/$n_matching] for ($r,$g,$b) to ($br,$bg,$bb) => $best_match");[*]               [*]                $bfin = $percent;[*]                $bproctime = $proctime;[*]                [URL="http://www.php.net/unset"]unset[/URL]($min_distance);[*]               [*]                //Check if everything is successfull[*]                if($success) {[*]                        $gen_pieces[$i][$j] = true;[*]                } else {[*]                        debug("error in generating, chechk it out! dumping current composit...");[*]                        cache_var($gen_pieces,$generated_idx);[*]                        [URL="http://www.php.net/imagejpeg"]imagejpeg[/URL]($mosaic,$output_image,$quality);[*]                        debug("abort!");[*]                        [URL="http://www.php.net/die"]die[/URL];[*]                }[*]        }[*]}[*] [*]//Mosaic is generated, output it to disk and exit[*]$distinct = [URL="http://www.php.net/count"]count[/URL]($distincts);[*][URL="http://www.php.net/imagedestroy"]imagedestroy[/URL]($template);[*]$est = arithmetic_mean($ests);[*]debug("Saving generated image to $output_image (composed from $loops images, $distinct distinct photos) $est% match overall...");[*][URL="http://www.php.net/imagejpeg"]imagejpeg[/URL]($mosaic,$output_image,$quality);[*]debug('Saved! Unallocating memory...');[*][URL="http://www.php.net/imagedestroy"]imagedestroy[/URL]($mosaic);[*]if([URL="http://www.php.net/file_exists"]file_exists[/URL]($generated_idx)) [URL="http://www.php.net/unlink"]unlink[/URL]($generated_idx);[*]debug('--- ALL DONE ---');[*] [*]/*[*] * Functions[*] */[*] [*]//Calculates Euclidian distance between two points in space (3-norm)[*]function distance($x1,$y1,$z1,$x2,$y2,$z2) {[*]        return [URL="http://www.php.net/pow"]pow[/URL]([URL="http://www.php.net/pow"]pow[/URL]([URL="http://www.php.net/abs"]abs[/URL]($x1-$x2),3)+[URL="http://www.php.net/pow"]pow[/URL]([URL="http://www.php.net/abs"]abs[/URL]($y1-$y2),3)+[URL="http://www.php.net/pow"]pow[/URL]([URL="http://www.php.net/abs"]abs[/URL]($z1-$z2),3),1/3);[*]}[*] [*]//Calculates Euclidian distance between two points in p-norm[*]function euclidian_distance($p1,$p2) {[*]        $distances = 0;[*]        $norm = ([URL="http://www.php.net/count"]count[/URL]($p1)+[URL="http://www.php.net/count"]count[/URL]($p2))/2;[*]        for($i=0;$i<$norm;$i++) $distances += [URL="http://www.php.net/pow"]pow[/URL]([URL="http://www.php.net/abs"]abs[/URL]($p1[$i]-$p2[$i]),$norm);[*]        return [URL="http://www.php.net/pow"]pow[/URL]($distances,1/$norm);[*]}[*] [*]//Reads the variable from disk[*]function uncache_var($file) {[*]        if(![URL="http://www.php.net/file_exists"]file_exists[/URL]($file)) return [URL="http://www.php.net/array"]array[/URL]();[*]        $handle = [URL="http://www.php.net/fopen"]fopen[/URL]($file,'r');[*]        $contents = [URL="http://www.php.net/fread"]fread[/URL]($handle, [URL="http://www.php.net/filesize"]filesize[/URL]($file));[*]        [URL="http://www.php.net/fclose"]fclose[/URL]($handle);[*]        return [URL="http://www.php.net/unserialize"]unserialize[/URL]($contents);[*]}[*] [*]//Saves the variable to disk[*]function cache_var($var,$file) {[*]        $fh = [URL="http://www.php.net/fopen"]fopen[/URL]($file,'w+');[*]        [URL="http://www.php.net/fwrite"]fwrite[/URL]($fh,[URL="http://www.php.net/serialize"]serialize[/URL]($var));[*]        [URL="http://www.php.net/fclose"]fclose[/URL]($fh);[*]}[*] [*]//Generates the 'block' or 'pixel' of the mosaic[*]function generate_piece($image,$piece_w,$piece_h,$cache=false,$quality=100) {[*]        $sha = [URL="http://www.php.net/sha1"]sha1[/URL]($image);[*]        $cfn = "$cache/$sha.jpg";[*]       [*]        //Check if cache of the piece exists[*]        if($cache && [URL="http://www.php.net/file_exists"]file_exists[/URL]($cfn)) {[*]                return [URL="http://www.php.net/imagecreatefromjpeg"]imagecreatefromjpeg[/URL]($cfn);[*]        }[*]       [*]        $original = [URL="http://www.php.net/imagecreatefromjpeg"]imagecreatefromjpeg[/URL]($image);[*]        $original_min = [URL="http://www.php.net/min"]min[/URL]([URL="http://www.php.net/imagesx"]imagesx[/URL]($original),[URL="http://www.php.net/imagesy"]imagesy[/URL]($original));[*]       [*]        //Take the center of crop[*]        $src_x = ([URL="http://www.php.net/imagesx"]imagesx[/URL]($original)-$original_min)/2;[*]        $src_y = ([URL="http://www.php.net/imagesy"]imagesy[/URL]($original)-$original_min)/2;[*]       [*]        //Shrink it down[*]        $piece = [URL="http://www.php.net/imagecreatetruecolor"]imagecreatetruecolor[/URL]($piece_w,$piece_h);[*]        $success = [URL="http://www.php.net/imagecopyresampled"]imagecopyresampled[/URL]($piece,$original,0,0,$src_x,$src_y,$piece_w,$piece_h,$original_min,$original_min);[*]        [URL="http://www.php.net/imagedestroy"]imagedestroy[/URL]($original);[*]       [*]        //Cache it[*]        if($cache && $success) {[*]                if(![URL="http://www.php.net/is_dir"]is_dir[/URL]($cache)) [URL="http://www.php.net/mkdir"]mkdir[/URL]($cache);[*]                [URL="http://www.php.net/imagejpeg"]imagejpeg[/URL]($piece,$cfn,$quality);[*]        }[*]       [*]        return $success?$piece:false;[*]}[*] [*]//Returns a one-color representation of an image[*]function get_average_pixel($im,$average_type) {[*]        $width = [URL="http://www.php.net/imagesx"]imagesx[/URL]($im);[*]        $height = [URL="http://www.php.net/imagesy"]imagesy[/URL]($im);[*]        switch($average_type) {[*]                case 1:[*]                case 2:[*]                case 3:[*]                case 4:[*]                        $r = $g = $b = [URL="http://www.php.net/array"]array[/URL]();[*]                        for($i=0;$i<$width;$i++) {[*]                                for($j=0;$j<$height;$j++) {[*]                                        $rgb = rgb([URL="http://www.php.net/imagecolorat"]imagecolorat[/URL]($im, $i, $j));[*]                                        $r[] = $rgb[0];[*]                                        $g[] = $rgb[1];[*]                                        $b[] = $rgb[2];[*]                                }[*]                        }[*]                        return [URL="http://www.php.net/array"]array[/URL](px_avg($r,$average_type),px_avg($g,$average_type),px_avg($b,$average_type));[*]                case 5:[*]                        $onepx = [URL="http://www.php.net/imagecreatetruecolor"]imagecreatetruecolor[/URL](1,1);[*]                        [URL="http://www.php.net/imagecopyresampled"]imagecopyresampled[/URL]($onepx,$im,0,0,0,0,1,1,$width,$height);[*]                        $rgb = [URL="http://www.php.net/imagecolorat"]imagecolorat[/URL]($onepx,0,0);[*]                        [URL="http://www.php.net/imagedestroy"]imagedestroy[/URL]($onepx);[*]                        return rgb($rgb);[*]                case 6:[*]                        $onepx = [URL="http://www.php.net/imagecreatetruecolor"]imagecreatetruecolor[/URL](1,1);[*]                        [URL="http://www.php.net/imagecopyresized"]imagecopyresized[/URL]($onepx,$im,0,0,0,0,1,1,$width,$height);[*]                        $rgb = [URL="http://www.php.net/imagecolorat"]imagecolorat[/URL]($onepx,0,0);[*]                        [URL="http://www.php.net/imagedestroy"]imagedestroy[/URL]($onepx);[*]                        return rgb($rgb);[*]        }[*]}[*] [*]//Calculates the average of an array[*]function px_avg($a,$average_type) {[*]        switch($average_type) {[*]                case 1: return [URL="http://www.php.net/round"]round[/URL](arithmetic_mean($a));[*]                case 2: return [URL="http://www.php.net/round"]round[/URL](harmonic_mean($a));[*]                case 3: return [URL="http://www.php.net/round"]round[/URL](median($a));[*]                case 4: return [URL="http://www.php.net/round"]round[/URL](modal_score($a));[*]        }[*]}[*] [*]// List all 'jpg' files in a directory, recursively[*]function get_images($folder) {[*]        $files = [URL="http://www.php.net/array"]array[/URL]();[*]        if(![URL="http://www.php.net/is_dir"]is_dir[/URL]($folder)) return false;[*]        if ($handle = [URL="http://www.php.net/opendir"]opendir[/URL]($folder)) {[*]                while (false !== ($file = [URL="http://www.php.net/readdir"]readdir[/URL]($handle))) {[*]                        if ($file != '.' && $file != '..') {[*]                                if([URL="http://www.php.net/strtolower"]strtolower[/URL](file_extension($file)) == 'jpg') {[*]                                        $files[] = "$folder/$file";[*]                                }[*]                                if([URL="http://www.php.net/is_dir"]is_dir[/URL]("$folder/$file")) {[*]                                        $recursive = get_images("$folder/$file");[*]                                        foreach($recursive as $rfile) $files[] = $rfile;[*]                                }[*]                        }[*]                }[*]                [URL="http://www.php.net/closedir"]closedir[/URL]($handle);[*]        }[*]        return $files;[*]}[*] [*]// Get file extension[*]function file_extension($filename) {[*]    return [URL="http://www.php.net/end"]end[/URL]([URL="http://www.php.net/explode"]explode[/URL]('.', $filename));[*]}[*] [*]//Average functions[*]function arithmetic_mean($a) {[*]        return [URL="http://www.php.net/array_sum"]array_sum[/URL]($a)/[URL="http://www.php.net/count"]count[/URL]($a);[*]}[*]function geometric_mean($a) {[*]        $mul = 0.0;[*]        foreach($a as $i=>$n) $mul = (float) (($i == 0) ? $n : $mul*$n);[*]        return [URL="http://www.php.net/pow"]pow[/URL]($mul,1/[URL="http://www.php.net/count"]count[/URL]($a));[*]}[*]function harmonic_mean($a) {[*]        $sum = 0;[*]        foreach($a as $n) $sum += ($n == 0) ? 0 : 1/$n;[*]        return (1/$sum)*[URL="http://www.php.net/count"]count[/URL]($a);[*]}[*]function median($a) {[*]        [URL="http://www.php.net/sort"]sort[/URL]($a,SORT_NUMERIC);[*]        return ([URL="http://www.php.net/count"]count[/URL]($a)%2 == 0) ?[*]                $a[[URL="http://www.php.net/floor"]floor[/URL]([URL="http://www.php.net/count"]count[/URL]($a)/2)] :[*]                ($a[[URL="http://www.php.net/count"]count[/URL]($a)/2] + $a[[URL="http://www.php.net/count"]count[/URL]($a)/2 - 1]) / 2;[*]}[*]function modal_score($a) {[*]        $quant = [URL="http://www.php.net/array"]array[/URL]();[*]        foreach($a as $n) {[*]                if([URL="http://www.php.net/isset"]isset[/URL]($quant[$n])) {[*]                        $quant[$n]++;[*]                } else {[*]                        $quant[$n] = 1;[*]                }[*]        }[*]        $max = 0;[*]        $mode = 0;[*]        foreach($quant as $key=>$n) {[*]                if($n>$max) {[*]                        $max = $n;[*]                        $mode = $key;[*]                }[*]        }[*]        return $mode;[*]}[*] [*]//Returns array(R,G,B) of an (int) color (bit shift)[*]function rgb($rgb) {[*]        $r = ($rgb >> 16) & 0xFF;[*]        $g = ($rgb >> 8) & 0xFF;[*]        $b = $rgb & 0xFF;[*]        return [URL="http://www.php.net/array"]array[/URL]($r,$g,$b);[*]}[*] [*]//Custom println() ;-)[*]function debug($msg) {[*]        print("[".udate("d.m.Y H:i:s.u")."] $msgn");[*]}[*] [*]//Custom date function ;) - for milliseconds[*]function udate($format, $utimestamp = null) {[*]        if ([URL="http://www.php.net/is_null"]is_null[/URL]($utimestamp)) $utimestamp = [URL="http://www.php.net/microtime"]microtime[/URL](true);[*]        $timestamp = [URL="http://www.php.net/floor"]floor[/URL]($utimestamp);[*]        $milliseconds = [URL="http://www.php.net/round"]round[/URL](($utimestamp - $timestamp) * 1000000);[*]        return [URL="http://www.php.net/date"]date[/URL]([URL="http://www.php.net/preg_replace"]preg_replace[/URL]('`(?<!\\)u`', $milliseconds, $format), $timestamp);[*]}[*] [*]//Returns time span duration[*]function duration($seconds) {[*]        $days = [URL="http://www.php.net/floor"]floor[/URL]($seconds/60/60/24);[*]        $hours = $seconds/60/60%24;[*]        $mins = $seconds/60%60;[*]        $secs = $seconds%60;[*] [*]        $duration='';[*]        if($days>0) $duration .= "{$days}d";[*]        if($hours>0) $duration .= "{$hours}h";[*]        if($mins>0) $duration .= "{$mins}m";[*]        if($secs>0) $duration .= "{$secs}s";[*] [*]        $duration = [URL="http://www.php.net/trim"]trim[/URL]($duration);[*]        if($duration==null) $duration = 'n/a';[*] [*]        return $duration;[*]}[*] [*]//Returns percentage rounded to n decimal points[*]function percent($percent,$decimal=2) {[*]        return [URL="http://www.php.net/round"]round[/URL]($percent * 100 * [URL="http://www.php.net/pow"]pow[/URL](10,$decimal)) / [URL="http://www.php.net/pow"]pow[/URL](10,$decimal);[*]}[*]?>[/LIST]
Bunu buldum, bunu da dener misiniz?