afc306fc |
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 = []; |
fbd2dc51 |
20 | open IDATA, "convert -depth 8 $infile rgb:- |"; |
afc306fc |
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; |
fbd2dc51 |
26 | die "wrong amount of image data ($al, expected $xl) from $infile\n" |
afc306fc |
27 | unless $al == $xl; |
28 | |
e0433c68 |
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]; |
afc306fc |
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; |