| 1 | #!/usr/bin/perl |
| 2 | |
| 3 | # Read an input image, crop its border to a standard width, and |
| 4 | # convert it into a square output image. Parameters are: |
| 5 | # |
| 6 | # - the required total image size |
| 7 | # - the output border thickness |
| 8 | # - the input image file name |
| 9 | # - the output image file name. |
| 10 | |
| 11 | ($osize, $oborder, $infile, $outfile) = @ARGV; |
| 12 | |
| 13 | # Determine the input image's size. |
| 14 | $ident = `identify -format "%w %h" $infile`; |
| 15 | $ident =~ /(\d+) (\d+)/ or die "unable to get size for $infile\n"; |
| 16 | ($w, $h) = ($1, $2); |
| 17 | |
| 18 | # Read the input image data. |
| 19 | $data = []; |
| 20 | open IDATA, "convert -depth 8 $infile rgb:- |"; |
| 21 | push @$data, $rgb while (read IDATA,$rgb,3,0) == 3; |
| 22 | close IDATA; |
| 23 | # Check we have the right amount of data. |
| 24 | $xl = $w * $h; |
| 25 | $al = scalar @$data; |
| 26 | die "wrong amount of image data ($al, expected $xl) from $infile\n" |
| 27 | unless $al == $xl; |
| 28 | |
| 29 | # Find the background colour, by looking around the entire border |
| 30 | # and finding the most popular pixel colour. |
| 31 | for ($i = 0; $i < $w; $i++) { |
| 32 | $pcount{$data->[$i]}++; # top row |
| 33 | $pcount{$data->[($h-1)*$w+$i]}++; # bottom row |
| 34 | } |
| 35 | for ($i = 1; $i < $h-1; $i++) { |
| 36 | $pcount{$data->[$i*$w]}++; # left column |
| 37 | $pcount{$data->[$i*$w+$w-1]}++; # right column |
| 38 | } |
| 39 | @plist = sort { $pcount{$b} <=> $pcount{$a} } keys %pcount; |
| 40 | $back = $plist[0]; |
| 41 | |
| 42 | # Crop rows and columns off the image to find the central rectangle |
| 43 | # of non-background stuff. |
| 44 | $ystart = 0; |
| 45 | $ystart++ while $ystart < $h and scalar(grep { $_ ne $back } map { $data->[$ystart*$w+$_] } 0 .. ($w-1)) == 0; |
| 46 | $yend = $h-1; |
| 47 | $yend-- while $yend >= $ystart and scalar(grep { $_ ne $back } map { $data->[$yend*$w+$_] } 0 .. ($w-1)) == 0; |
| 48 | $xstart = 0; |
| 49 | $xstart++ while $xstart < $w and scalar(grep { $_ ne $back } map { $data->[$_*$w+$xstart] } 0 .. ($h-1)) == 0; |
| 50 | $xend = $w-1; |
| 51 | $xend-- while $xend >= $xstart and scalar(grep { $_ ne $back } map { $data->[$_*$w+$xend] } 0 .. ($h-1)) == 0; |
| 52 | |
| 53 | # Decide how much border we're going to put back on to make the |
| 54 | # image perfectly square. |
| 55 | $hexpand = ($yend-$ystart) - ($xend-$xstart); |
| 56 | if ($hexpand > 0) { |
| 57 | $left = int($hexpand / 2); |
| 58 | $xstart -= $left; |
| 59 | $xend += $hexpand - $left; |
| 60 | } elsif ($hexpand < 0) { |
| 61 | $vexpand = -$hexpand; |
| 62 | $top = int($vexpand / 2); |
| 63 | $ystart -= $top; |
| 64 | $yend += $vexpand - $top; |
| 65 | } |
| 66 | $ow = $xend - $xstart + 1; |
| 67 | $oh = $yend - $ystart + 1; |
| 68 | die "internal computation problem" if $ow != $oh; # should be square |
| 69 | |
| 70 | # And decide how much _more_ border goes on to add the bit around |
| 71 | # the edge. |
| 72 | $realow = int($ow * ($osize / ($osize - 2*$oborder))); |
| 73 | $extra = $realow - $ow; |
| 74 | $left = int($extra / 2); |
| 75 | $xstart -= $left; |
| 76 | $xend += $extra - $left; |
| 77 | $top = int($extra / 2); |
| 78 | $ystart -= $top; |
| 79 | $yend += $extra - $top; |
| 80 | $ow = $xend - $xstart + 1; |
| 81 | $oh = $yend - $ystart + 1; |
| 82 | die "internal computation problem" if $ow != $oh; # should be square |
| 83 | |
| 84 | # Now write out the resulting image, and resize it appropriately. |
| 85 | open IDATA, "| convert -size ${ow}x${oh} -depth 8 -resize ${osize}x${osize}! rgb:- $outfile"; |
| 86 | for ($y = $ystart; $y <= $yend; $y++) { |
| 87 | for ($x = $xstart; $x <= $xend; $x++) { |
| 88 | if ($x >= 0 && $x < $w && $y >= 0 && $y < $h) { |
| 89 | print IDATA $data->[$y*$w+$x]; |
| 90 | } else { |
| 91 | print IDATA $back; |
| 92 | } |
| 93 | } |
| 94 | } |
| 95 | close IDATA; |