resize_images.pl 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185
  1. #!/usr/bin/perl
  2. # Author: Trizen
  3. # Date: 30 October 2023
  4. # Edit: 10 August 2024
  5. # https://github.com/trizen
  6. # Resize images to a given width or height, keeping aspect ratio.
  7. use 5.036;
  8. use Imager qw();
  9. use File::Find qw(find);
  10. use List::Util qw(min max);
  11. use Getopt::Long qw(GetOptions);
  12. my $width = 'auto';
  13. my $height = 'auto';
  14. my $min = 'auto';
  15. my $max = 'auto';
  16. my $qtype = 'mixing';
  17. my $outdir = undef;
  18. my $img_formats = '';
  19. my $preserve_attr = 0;
  20. my @img_formats = qw(
  21. jpeg
  22. jpg
  23. png
  24. );
  25. sub usage ($code) {
  26. local $" = ",";
  27. print <<"EOT";
  28. usage: $0 [options] [dirs | files]
  29. options:
  30. -w --width=i : resize images to this width
  31. -h --height=i : resize images to this height
  32. --min=i : resize images to have the smallest side equal to this
  33. --max=i : resize images to have the largest side equal to this
  34. -q --quality=s : quality of scaling: 'normal', 'preview' or 'mixing' (default: $qtype)
  35. -f --formats=s,s : specify more image formats (default: @img_formats)
  36. -p --preserve! : preserve original file timestamps and permissions
  37. -o --outdir=s : create resized images into this directory
  38. examples:
  39. $0 --min=1080 *.jpg # smallest side = 1080 pixels
  40. $0 --height=1080 *.jpg # height = 1080 pixels
  41. EOT
  42. exit($code);
  43. }
  44. GetOptions(
  45. 'w|width=i' => \$width,
  46. 'h|height=i' => \$height,
  47. 'minimum=i' => \$min,
  48. 'maximum=i' => \$max,
  49. 'q|quality=s' => \$qtype,
  50. 'f|formats=s' => \$img_formats,
  51. 'p|preserve!' => \$preserve_attr,
  52. 'o|outdir=s' => \$outdir,
  53. 'help' => sub { usage(0) },
  54. )
  55. or die("Error in command line arguments");
  56. push @img_formats, map { quotemeta } split(/\s*,\s*/, $img_formats);
  57. my $img_formats_re = do {
  58. local $" = '|';
  59. qr/\.(@img_formats)\z/i;
  60. };
  61. if (defined($outdir)) {
  62. if (not -d $outdir) {
  63. require File::Path;
  64. File::Path::make_path($outdir)
  65. or die "Can't create output directory <<$outdir>>: $!";
  66. }
  67. require File::Basename;
  68. require File::Spec::Functions;
  69. }
  70. sub resize_image ($image) {
  71. my $img = Imager->new(file => $image) or do {
  72. warn "Failed to load <<$image>>: ", Imager->errstr();
  73. return;
  74. };
  75. my ($curr_width, $curr_height) = ($img->getwidth, $img->getheight);
  76. if ($min ne 'auto' and $min > 0) {
  77. if (min($curr_width, $curr_height) <= $min) {
  78. say "Image too small to resize";
  79. return;
  80. }
  81. if ($curr_width < $curr_height) {
  82. $img = $img->scale(xpixels => $min, qtype => $qtype);
  83. }
  84. else {
  85. $img = $img->scale(ypixels => $min, qtype => $qtype);
  86. }
  87. }
  88. elsif ($max ne 'auto' and $max > 0) {
  89. if (max($curr_width, $curr_height) <= $max) {
  90. say "Image too small to resize";
  91. return;
  92. }
  93. if ($curr_height > $curr_width) {
  94. $img = $img->scale(ypixels => $max, qtype => $qtype);
  95. }
  96. else {
  97. $img = $img->scale(xpixels => $max, qtype => $qtype);
  98. }
  99. }
  100. elsif ($height ne 'auto' and $height > 0) {
  101. if ($curr_height <= $height) {
  102. say "Image too small to resize";
  103. return;
  104. }
  105. $img = $img->scale(ypixels => $height, qtype => $qtype);
  106. }
  107. elsif ($width ne 'auto' and $width > 0) {
  108. if ($curr_width <= $width) {
  109. say "Image too small to resize";
  110. return;
  111. }
  112. $img = $img->scale(xpixels => $width, qtype => $qtype);
  113. }
  114. else {
  115. die "No --width or --height specified...";
  116. }
  117. my ($dev, $ino, $mode, $nlink, $uid, $gid, $rdev, $size, $atime, $mtime, $ctime, $blksize, $blocks) = stat($image);
  118. # Create resized image into $outdir directory
  119. if (defined($outdir)) {
  120. $image = File::Spec::Functions::catfile($outdir, File::Basename::basename($image));
  121. }
  122. $img->write(file => $image) or do {
  123. warn "Failed to rewrite image: ", $img->errstr;
  124. return;
  125. };
  126. # Set the original ownership of the image
  127. chown($uid, $gid, $image);
  128. if ($preserve_attr) {
  129. # Set the original modification time
  130. utime($atime, $mtime, $image)
  131. or warn "Can't change timestamp: $!\n";
  132. # Set original permissions
  133. chmod($mode & 07777, $image)
  134. or warn "Can't change permissions: $!\n";
  135. }
  136. return 1;
  137. }
  138. @ARGV || usage(1);
  139. find {
  140. no_chdir => 1,
  141. wanted => sub {
  142. (/$img_formats_re/o && -f) || return;
  143. say "Resizing: $_";
  144. resize_image($_);
  145. }
  146. } => @ARGV;