I wrote this up a few years ago, figure I might as well share it on here. It will create a PNG which contains all of the possible image combinations for the given set of parameters. So you specify a 2x3 pixel image with 5 possible colors (or whatever) and you'll get a huge image output the other end that contains all the variations.
The fun thing about image generation in this manner is that you can theoretically generate photographs of anything this way, if you had sufficient time and horsepower. Of course, a 10x10 pixel image with 10 colors has one googol possible combinations, so it quickly becomes intractable.
I have a more thorough write up here: http://www.jimandkoka.com/
#!/usr/bin/perl # Copyright and (c) 2002, 2003 Jim Thomason. jim@jimandkoka.com # http://www.jimandkoka.com # distributed under the artistic license or something # # Note that this software requires the GD module, available from CPAN # http://search.cpan.org/ # # Usage - ./imagemaker.pl width height colors ?countonly? > some.png # # width is the image width # height is the image height # colors is the number of possible colors allowed in the image # countonly is an optional param. If passed, the program will just # print out a count of the number of combinations and then quit. # use strict; use warnings; use GD; our $VERSION = 2.00; #snag our command line arguments my $w = shift @ARGV or die 'Cannot run w/o width'; my $h = shift @ARGV or die 'Cannot run w/o height'; my $c = shift @ARGV or die 'Cannot run w/o colors'; my $s = shift @ARGV || 1; my $countonly = shift @ARGV || 0; die "Cannot support more than 7 colors" if $c > 7 && ! $countonly; #find out the number of pixel combinations my $combos = $c ** ($w * $h); #find out the unscaled area of each image set my $unscaled_each_area = $w * $h; #find out the area of each image set, taking the scaling into account my $each_area = $unscaled_each_area * $s * $s; #calculate how many we'd like to display per line. my $perline = int sqrt $combos; #round up if it's not an even number. $perline += 1 unless $perline ** 2 == $combos; # calculate the actual width and height of the image we're going # to generate. Each image will take up $w * $s pixels width + # $s pixels padding to its right but, the last image doesn't # require padding, so we subtract off its padding amount # # ditto for the height my $width = $perline * ($w * $s + $s) - $s; my $height = $perline * ($h * $s + $s) - $s; #display some numbers print STDERR "for a $w x $h image @ $c colors, " . "there are $combos combinations\n"; #and bow out if we're just counting exit if $countonly; #warn them about how big this thing is gonna get print STDERR "your output will be $width x $height\n"; #create our new image my $im = new GD::Image($width,$height); # allocate some colors # # This is silly, we need to allocate red first, so we get that as # the background for our image. Bleh. my $red = $im->colorAllocate(255,0,0); my $green = $im->colorAllocate(0,255,0); my $blue = $im->colorAllocate(0,0,255); my $cyan = $im->colorAllocate(0,255,255); my $magenta = $im->colorAllocate(255,0,255); my $yellow = $im->colorAllocate(0,255,255); my $black = $im->colorAllocate(0,0,0); my $white = $im->colorAllocate(255,255,255); # this is the order we want to use the colors my @colors= ($white, $black, $green, $blue, $cyan, $magenta, $yellow); #our padding starts at 0. my $xpad = 0; my $ypad = 0; # and we're off to the races. # # It's pretty easy. iterate through all the numbers from # 0 to the number of combinations we have. At each number, # convert it from base 10 to the number base of the number # of colors we have. So a B/W image is 2 colors is base 2. # a black, white, and green image is 3 colors, is base 3 # and so on. # # With each base(x) number, each digit corresponds to a color # value in our colors array. So use that color value to fill # in the pixel at the appropriate square. for (my $num = 0; $num < $combos; $num++) { # convert to the appropriate number base my @num = basemaster($num, 10, $c); # now, if we have fewer digits in the new base than we'll have # pixels in the image, zero pad the number until we reach the # appropriate size. if (@num < $unscaled_each_area) { @num = ((0) x ($unscaled_each_area - @num), @num); }; { #keep track of which pixel we're on. my $pix = 0; #we'll start drawing this image at the appropriate xpad # and ypad values my $x = $xpad; my $y = $ypad; #as long as we have pixels left to draw... while ($pix < $unscaled_each_area) { #grab our next color my $col = $colors[$num[$pix] || 0]; #increment our pixel count # (yeah, I could've done the inc up in that last line, # but I didn't want it to get lost in the muck) $pix++; # and draw our pixel, scaled appropriately. # GD is a little silly, the arguments are the upper left # coordinates of the upper left pixel and the upper left # coordinates of the lower right pixel. So, to actually # set the lower right pixel to where we want, we need to back # up by one pixel to get it positioned properly $im->filledRectangle($x, $y, $x + $s - 1, $y + $s - 1, $col); # slide our y coordinate over by the scale value $y += $s; # okay, if $pix % $h == 0, then we're at the end of the row, so # we'll need to drop to the next row by incrementing the xpad, # and go back to the first column by resetting the ypad. unless ($pix % $h) { $xpad += $s; $x = $xpad; $y = $ypad; }; }; }; # okay, we've now finished drawing out the individual image, so # we're going to move onto the next in the sequence. So we're going # to slide our xpadding over by $s pixels to be # ready to start the next one. FYI, this is starting the image to # the right of the one just drawn $xpad += $s; # the one exception is if we've reached the perline limit. In that # case, we're at the end of the row, so we'll reset our xpad back # to zero and increment our ypad instead by the appropriate amount unless (($num + 1) % $perline) { $ypad += $h * $s + $s; $xpad = 0; }; }; #be nice and binmode it. Stupid dos. binmode STDOUT; #print print $im->png; exit; #and we're done! # basemaster takes 3 arguments. # the number you're converting, the base you're converting from, and # the base you're converting to # Always returns an array of numbers in order, higher digits in the # front, lower at the back. # # so basemaster(13, 10, 2) returns (1, 1, 0, 1) sub basemaster { my $num = shift || return 0; my $from = shift; my $to = shift; # we allow it to accept a comma delimited number to allow for higher # bases my @num = reverse $num =~ /,/ ? split(/,/, $num) : split(//, $num); # starting power is 0 my $pow = 0; # and we don't know it in base10 my $base10 = undef; # if we're converting from base ten, then do it. if ($from != 10) { foreach my $digit (@num){ die "Invalid number -- $digit >= $from" if $digit >= $from; $base10 += $digit * $from ** $pow++; }; } # otherwise, we have a base 10 number, so there's no work to do. else { $base10 = $num; }; #reset our power to 1 $pow = 1; # and find the highest power of the number in the base we're # converting to $pow++ while $to ** $pow <= $base10; #we'll return our array here. my @return = (); #convert to the new base while ($pow >= 0){ push @return, int ($base10 / $to ** $pow); $base10 %= $to ** $pow--; }; #trash leading zeroes shift @return while $return[0] == 0; #and we are done. return @return; };
|
|---|
| Replies are listed 'Best First'. | |
|---|---|
|
Re: Image combinations
by wazoox (Prior) on Jul 11, 2006 at 13:11 UTC |