img-autocrop-avg.pl 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237
  1. #!/usr/bin/perl
  2. # Author: Daniel "Trizen" Șuteu
  3. # License: GPLv3
  4. # Date: 14 June 2015
  5. # Edit: 19 March 2017
  6. # https://github.com/trizen
  7. # A generic image auto-cropper which adapt itself to any background color.
  8. use 5.010;
  9. use strict;
  10. use warnings;
  11. use GD qw();
  12. use Getopt::Long qw(GetOptions);
  13. use File::Basename qw(basename);
  14. use File::Spec::Functions qw(catfile);
  15. # Set true color
  16. GD::Image->trueColor(1);
  17. # Autoflush mode
  18. local $| = 1;
  19. my $tolerance = 5;
  20. my $invisible = 0;
  21. my $jpeg_quality = 95;
  22. my $png_compression = 7;
  23. my $directory = 'Cropped images';
  24. sub help {
  25. my ($code) = @_;
  26. print <<"EOT";
  27. usage: $0 [options] [images]
  28. options:
  29. -t --tolerance=i : tolerance value for the background color
  30. default: $tolerance
  31. -i --invisible! : make the background transparent after cropping
  32. default: ${$invisible ? \'true' : \'false'}
  33. -p --png-compress=i : the compression level for PNG images
  34. default: $png_compression
  35. -j --jpeg-quality=i : the quality value for JPEG images
  36. default: $jpeg_quality
  37. -d --directory=s : directory where to create the cropped images
  38. default: "$directory"
  39. example:
  40. perl $0 -t 10 *.png
  41. EOT
  42. exit($code // 0);
  43. }
  44. GetOptions(
  45. 'd|directory=s' => \$directory,
  46. 'i|invisible!' => \$invisible,
  47. 't|tolerance=i' => \$tolerance,
  48. 'p|png-compression=i' => \$png_compression,
  49. 'j|jpeg-quality=i' => \$jpeg_quality,
  50. 'h|help' => sub { help(0) },
  51. )
  52. or die("$0: error in command line arguments!\n");
  53. {
  54. my %cache;
  55. sub is_background {
  56. my ($img, $index, $bg_rgb) = @_;
  57. my $rgb = ($cache{$index} //= [$img->rgb($index)]);
  58. abs($rgb->[0] - $bg_rgb->[0]) <= $tolerance
  59. and abs($rgb->[1] - $bg_rgb->[1]) <= $tolerance
  60. and abs($rgb->[2] - $bg_rgb->[2]) <= $tolerance;
  61. }
  62. }
  63. sub make_invisible_bg {
  64. my ($img, $transparent, $bg_rgb, $width, $height) = @_;
  65. foreach my $x (0 .. $width) {
  66. foreach my $y (0 .. $height) {
  67. if (is_background($img, $img->getPixel($x, $y), $bg_rgb)) {
  68. $img->setPixel($x, $y, $transparent);
  69. }
  70. }
  71. }
  72. }
  73. sub autocrop {
  74. my @images = @_;
  75. foreach my $file (@images) {
  76. my $img = GD::Image->new($file);
  77. if (not defined $img) {
  78. warn "[!] Can't process image `$file': $!\n";
  79. next;
  80. }
  81. my ($width, $height) = $img->getBounds();
  82. $width -= 1;
  83. $height -= 1;
  84. my $C = (2 * $width + 1 + 2 * $height + 1);
  85. my @bg_rgb = (0, 0, 0);
  86. foreach my $x (0 .. $width) {
  87. for my $arr ([map { $_ / $C } $img->rgb($img->getPixel($x, 0))],
  88. [map { $_ / $C } $img->rgb($img->getPixel($x, $height))]) {
  89. $bg_rgb[0] += $arr->[0];
  90. $bg_rgb[1] += $arr->[1];
  91. $bg_rgb[2] += $arr->[2];
  92. }
  93. }
  94. foreach my $y (0 .. $height) {
  95. for my $arr ([map { $_ / $C } $img->rgb($img->getPixel(0, $y))],
  96. [map { $_ / $C } $img->rgb($img->getPixel($width, $y))]) {
  97. $bg_rgb[0] += $arr->[0];
  98. $bg_rgb[1] += $arr->[1];
  99. $bg_rgb[2] += $arr->[2];
  100. }
  101. }
  102. print "Cropping: $file";
  103. my $top;
  104. my $bottom;
  105. TB: foreach my $y (1 .. $height) {
  106. foreach my $x (1 .. $width) {
  107. if (not defined $top) {
  108. if (not is_background($img, $img->getPixel($x, $y), \@bg_rgb)) {
  109. $top = $y - 1;
  110. }
  111. }
  112. if (not defined $bottom) {
  113. if (not is_background($img, $img->getPixel($x, $height - $y), \@bg_rgb)) {
  114. $bottom = $height - $y + 1;
  115. }
  116. }
  117. if (defined $top and defined $bottom) {
  118. last TB;
  119. }
  120. }
  121. }
  122. if (not defined $top or not defined $bottom) {
  123. say " - fail!";
  124. next;
  125. }
  126. my $left;
  127. my $right;
  128. LR: foreach my $x (1 .. $width) {
  129. foreach my $y (1 .. $height) {
  130. if (not defined $left) {
  131. if (not is_background($img, $img->getPixel($x, $y), \@bg_rgb)) {
  132. $left = $x - 1;
  133. }
  134. }
  135. if (not defined $right) {
  136. if (not is_background($img, $img->getPixel($width - $x, $y), \@bg_rgb)) {
  137. $right = $width - $x + 1;
  138. }
  139. }
  140. if (defined $left and defined $right) {
  141. last LR;
  142. }
  143. }
  144. }
  145. if (not defined $left or not defined $right) {
  146. say " - fail!";
  147. next;
  148. }
  149. my $cropped = GD::Image->new($right - $left + 1, $bottom - $top + 1);
  150. my $index;
  151. if ($invisible) {
  152. $index = $cropped->colorAllocateAlpha(int(rand(256)), int(rand(256)), int(rand(256)), 0);
  153. $cropped->filledRectangle(0, 0, $cropped->width, $cropped->height, $index);
  154. $cropped->transparent($index);
  155. }
  156. $cropped->copyResized(
  157. $img,
  158. 0, # destX
  159. 0, # destY
  160. $left, # srcX
  161. $top, # srcY
  162. $right, # destW
  163. $bottom, # destH
  164. $right, # srcW
  165. $bottom, # srcH
  166. );
  167. my $name = catfile($directory, basename($file));
  168. if ($invisible) {
  169. make_invisible_bg($cropped, $index, \@bg_rgb, $cropped->width - 1, $cropped->height - 1);
  170. $name =~ s/\.\w+\z/.png/;
  171. }
  172. open my $fh, '>:raw', $name or die "Can't create file `$name': $!";
  173. print $fh (
  174. $name =~ /\.png\z/i ? $cropped->png($png_compression)
  175. : $name =~ /\.gif\z/i ? $cropped->gif
  176. : $cropped->jpeg($jpeg_quality)
  177. );
  178. close $fh;
  179. say " - ok!";
  180. }
  181. }
  182. @ARGV || help(1);
  183. if (not -d $directory) {
  184. mkdir($directory) || die "Can't mkdir `$directory': $!";
  185. }
  186. autocrop(@ARGV);