#! /usr/bin/perl # mosaic.pl # Copyright (C) 1995-2005 Everett Bell # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA #use strict; # Create a package and turn strict back on!!! use File::Find; use Image::Magick; die( "Usage: mosaic ...\n(Ex: mosaic hotpornpic.jpg 20 20)\n" ) if $#ARGV != 2; my $mainimage = Image::Magick->new; $mainimage->Read( $ARGV[0] ); # get image's size my $mainw = $mainimage->Get('width'); my $mainh = $mainimage->Get('height'); print "Image size: $mainw x $mainh\n"; my $tilewidth = $mainw / $ARGV[1]; my $tileheight = $mainh / $ARGV[2]; my $rows = $mainh / $tileheight; my $cols = $mainw / $tilewidth; print "Tiles will be $tilewidth x $tileheight\n"; my $maingrid; my ( $x, $y ); #set up maingrid print "Analyzing main image ...\n"; for ( $y = 0 ; $y < $rows ; $y++ ) { for ( $x = 0 ; $x < $cols ; $x++ ) { my $section = $mainimage->Clone(); $section->Crop( width => $tilewidth, height => $tileheight, x => $x * $tilewidth, y => $y * $tileheight ); $section->Resize( width => 1, height => 1 ); my $pixel = $section->Get("pixel[0,0]"); for ( $c = 0 ; $c < 4 ; $c++ ) { $maingrid[$x][$y][$c] = ( ( split /,/, $pixel )[$c] ) / 256; #print $x.",".$y.",".$c." = ".$maingrid[$x][$y][$c]."\n"; } # end c } # end x } # end y my $numtiles = 0; my $tiles; my $tilecolors; print "Analyzing tiles ...\n"; printf "name dimensions filesize format\n"; printf "----------------------------------------- ------------ ----------- ------\n"; find( \&readtile, '.' ); print "Done.\n"; print $numtiles. " tiles found.\n"; # start comparing tiles to main image's sections # make array of how many times an image has been used my $used; for ( my $i = 0 ; $i < $numtiles ; $i++ ) { $used[$i] = 0; } # end loop through array if ( ( $tileheight * $tilewidth ) < $numtiles ) { print "Not enough tiles ... some images will be repeated.\n"; } my $outimage = Image::Magick->new; print "Matching tiles to main image sections ...\n"; my $pass = 1; #pass number print "Pass $pass ...\n"; # randomly place tiles srand; # my @new = (); my @new = 0 .. ($rows*$cols); # just a demo my @old = 0 .. ($rows*$cols); # just a demo # for( @old ){ # my $r = rand @new+1; # push(@new,$new[$r]); # $new[$r] = $_; # } my $currtileno = 0; # for( $currtileno = 0; $currtileno <= ($rows * ($cols+1)); $currtileno++ ) { # $y = $currtileno / $cols; # $x = $currtileno - ($y * $cols); for ( $y = 0 ; $y < $rows ; $y++ ) { for ( $x = 0 ; $x < $cols ; $x++ ) { my $currdiff = 256 * 4; # ANYTHING is closer than this! my $match = -1; for ( my $tileno = 0 ; $tileno < $numtiles ; $tileno++ ) { # print "\nTileno: $tileno "; # print '.'; my $rdiff = abs( $tilecolors[$tileno][0] - $maingrid[$x][$y][0] ); my $gdiff = abs( $tilecolors[$tileno][1] - $maingrid[$x][$y][1] ); my $bdiff = abs( $tilecolors[$tileno][2] - $maingrid[$x][$y][2] ); my $diff = $rdiff + $gdiff + $bdiff; # print " $diff-$currdiff"; if ( $diff < $currdiff ) { #check for this tile's usage if ( $used[$tileno] < $pass ) { #reset old match if one was found if ( $match != -1 ) { $used[$match]--; } # print " match found. $diff < $currdiff\n"; $match = $tileno; $currdiff = $diff; $used[$tileno]++; my $numtilesused = 0; #check to see if all tile's have been used for ( my $index = 0 ; $index < $numtiles ; $index++ ) { if ( $used[$index] == $pass ) { $numtilesused++; } } if ( $numtilesused == $numtiles ) { $pass++; print "Pass $pass ...\n"; } } # if tile not used in this pass yet } # end if better match } # end compare each tile #print $x.",".$y." matches tile ".$match."\n"; # printf "%4d %3d,%3d,%3d %3d,%3d,%3d ", $currdiff, # $tilecolors[$tileno][0], # $tilecolors[$tileno][1], # $tilecolors[$tileno][2], # $maingrid[$x][$y][0], # $maingrid[$x][$y][1], # $maingrid[$x][$y][2]; # $outimage[$i++] = $tiles[$match]->Clone(); # $old[($y*$cols)+$x] = $match; # for now just fill in the array, gotta stack later $myclone = $tiles[$match]->Clone(); push( @$outimage, $myclone ); #$outimage = $tiles[$match]->Append(); } # end x # print "\n"; } # loop through all tiles # stack 'em up! print "Compiling mosaic ...\n"; #for( @old ) { # print " $_\n"; # $myclone = $tiles[$_]->Clone(); # push( @$outimage, $myclone ); # } # end for each tile my $montage = $outimage->Montage( geometry => "$tilewidth x $tileheight", gravity => 'Center', bordercolor => 'green', borderwidth => 0, tile => "$cols x $rows", compose => 'over', background => '#ffffff', font => 'Generic.ttf', pointsize => 18, fill => '#600', stroke => 'none' ); print "Saving \'mosaic.png\' ...\n"; $montage->Write('mosaic.png'); print "Displaying \'mosaic.png\' ...\n"; $montage->Write('win:'); sub readtile { my ( $width, $height, $size, $format ); return if $File::Find::dir eq $File::Find::name; return if $_ eq 'mosaic.png'; return if $_ eq $ARGV[0]; my ( $image, $x ); $image = Image::Magick->new; #Determine if this is a picture ... ( $width, $height, $size, $format ) = $image->Ping($_); return if $format eq ''; return if $format eq 'TXT'; printf "%40s %5d x %5d %11d %6s\n", $_, $width, $height, $size, $format; # print "$_: $width x $height, size=$size, format=$format\n"; # print '.'; $x = $image->Read("$_"); # warn "$x" if "$x"; $image->Resize( width => $tilewidth, height => $tileheight ); # $tiles[$numtiles] = Image::Magick->new(size=>'40x40'); $tiles[$numtiles] = $image->Clone(); #$fn = $_.".tile.png"; #$tiles[$numtiles]->Write( $fn ); $image->Resize( width => 1, height => 1 ); $pixel = $image->Get("pixel[0,0]"); $tilecolors[$numtiles][0] = ( ( split /,/, $pixel )[0] ) / 256; $tilecolors[$numtiles][1] = ( ( split /,/, $pixel )[1] ) / 256; $tilecolors[$numtiles][2] = ( ( split /,/, $pixel )[2] ) / 256; # print $tilecolors[$numtiles][0] . "," . $tilecolors[$numtiles][1] . "," . $tilecolors[$numtiles][2] . "\n"; # warn "$x" if "$x"; # $x = $image->Write("$_" . '.ev'); # warn "$x" if "$x"; $numtiles++; }