33

With imagemagick, I'd like to crop an image, in a minimal fashion, so that it fits a given aspect ratio.

Example: given an image of, say, 3038 x 2014 px, I want to crop it to have a 3:2 aspect ratio. The resulting image would then be 3021 x 2014 px, cropped from the, say, center of the original image.

So looking for a command looking something like convert in.jpg -gravity center -crop_to_aspect_ratio 3:2 out.jpg.

Kelley van Evert
  • 1,063
  • 2
  • 9
  • 17
  • 4
    There's a script for that at [Fred's ImageMagick Scripts](http://www.fmwconcepts.com/imagemagick/aspectcrop/index.php). He includes plenty of explanation on how he accomplishes the effect. If you're driving IM within another script language, it might be easier to do the math in there. – T-Dor Jan 22 '14 at 17:01
  • Wonderful! Lots of useful stuff there BTW :) – Kelley van Evert Jan 22 '14 at 23:31

5 Answers5

38

Imagemagick 7.0.7.22 and above

-crop 3:2 works since January 6th, 2018.

JPG

magick convert in.jpg -gravity center -crop 3:2 out.jpg

in.jpg (before) out.jpg (after)

Warning/reminder: if you don't use -gravity center, you will get two output files:

left slice right slice

PNG

As fmw42 points out, PNG files store the virtual canvas size. +repage is recommended.

magick convert in.png -gravity center -crop 3:2 +repage out+repage.png

GIMP, IrfanView, Chrome and Windows Explorer don't show any difference, but Imagemagick knows:

magick identify out*png
out_stndrd.png PNG 252x168 314x168+31+0 8-bit sRGB 78557B 0.000u 0:00.000
out+repage.png PNG 252x168 252x168+0+0 8-bit sRGB 78529B 0.000u 0:00.000

Imagemagick 6.9.9-34 and above

JPG

convert in.jpg -gravity center -crop 3:2 out.jpg

PNG

convert in. -gravity center -crop 3:2 +repage out.png

Imagemagick 6.9.9-33 / 7.0.7.21 and below

Note: you need to add magick before any convert for v7.

1. Specific target resolution

If your goal at the end is to have a certain resolution (for example 1920x1080) then it's easy, using -geometry, the circumflex/hat/roof/house symbol (^) and -crop:

convert in.jpg -geometry 1920x1080^ -gravity center -crop 1920x1080+0+0 out.jpg

To loop over multiple jpg files:

for i in *jpg
  do convert "$i" -geometry 1920x1080^ -gravity center -crop 1920x1080+0+0 out-"$i"
done

2. Aspect ratio crop only

If you want to avoid scaling, you have to calculate the new length of the cropped side outside of Imagemagick. This is more involved:

aw=16 #desired aspect ratio width...
ah=9 #and height
in="in.jpg"
out="out.jpg"

wid=`convert "$in" -format "%[w]" info:`
hei=`convert "$in" -format "%[h]" info:`

tarar=`echo $aw/$ah | bc -l`
imgar=`convert "$in" -format "%[fx:w/h]" info:`

if (( $(bc <<< "$tarar > $imgar") ))
then
  nhei=`echo $wid/$tarar | bc`
  convert "$in" -gravity center -crop ${wid}x${nhei}+0+0 "$out"
elif (( $(bc <<< "$tarar < $imgar") ))
then
  nwid=`echo $hei*$tarar | bc`
  convert "$in" -gravity center -crop ${nwid}x${hei}+0+0 "$out"
else
  cp "$in" "$out"
fi

I'm using 16:9 in the examples, expecting it to be more useful than 3:2 to most readers. Change both occurrences of 1920x1080 in solution 1 or the aw/ah variables in solution 2 to get your desired aspect ratio.

Photo credit: Anders Krusberg / Peabody Awards

qubodup
  • 8,687
  • 5
  • 37
  • 45
19

Recent versions of Imagemagick (since 6.9.9-34) have an aspect crop. So you can do:

Input:

enter image description here

convert barn.jpg -gravity center -crop 3:2 +repage barn_crop_3to2.png

enter image description here

The output is 400x267+0+0. But note that the +repage is needed to remove the virtual canvas of 400x299+0+16, because PNG output supports virtual canvas. JPG output would not need the +repage, since it does not support a virtual canvas.

fmw42
  • 46,825
  • 10
  • 62
  • 80
  • It seems to be added in 7.0.7-22 (2018-01-06) according to the [change log](https://www.imagemagick.org/script/changelog.php) ? – André Laszlo Jun 22 '18 at 17:09
  • 2
    Yes, it was also new to IM 7 at about that version. If using IM 7, then use magick rather than convert. – fmw42 Jun 22 '18 at 18:06
3

With the advent of ImageMagick 7 you can use FX expressions to accomplish a crop to the largest image size possible given an aspect ratio in a single command.

The only trick is you're going to need to enter the desired aspect in four different places on the same command so I find it easiest to make a variable for that bit. The aspect can be a decimal number or a fraction as a string that the fx expression can resolve.

aspect="16/9"

magick input.png -gravity center \
    -extent  "%[fx:w/h>=$aspect?h*$aspect:w]x" \
    -extent "x%[fx:w/h<=$aspect?w/$aspect:h]" \
    output.png

Once the aspect is right, you can follow up the two -extent operations with a -resize to bring the finished image to your output size. The example above keeps it as large as it can be given the input image.

Caleb
  • 5,084
  • 1
  • 46
  • 65
1

You need to work out the reqired dimensions and then do a crop. Here's a function that, given the image's width and height plus the required aspect ratio as aspect_x and aspect_y, will output a crop string that can be used with Imagemagick.

def aspect(width, height, aspect_x, aspect_y)

  old_ratio = width.to_f / height
  new_ratio = aspect_x.to_f / aspect_y

  return if old_ratio == new_ratio

  if new_ratio > old_ratio
    height = (width / new_ratio).to_i # same width, shorter height
  else
    width = (height * new_ratio).to_i # shorter width, same height
  end

  "#{width}x#{height}#" # the hash mark gives centre-gravity

end

I'm using something similar to this in an application that uses the Dragonfly Gem.

starfry
  • 9,273
  • 7
  • 66
  • 96
0

I needed to split very long images vertically with A4 (1x1.414) paper aspect ratio. So I came up with below solution. Assume image filename is ch1.jpg:

convert -crop $(identify -format "%w" ch1.jpg)x$(printf "%.0f" $(echo $(identify -format "%w" ch1.jpg) \* 1.414|bc)) +repage ch1.jpg ch1.jpg
Rovshan
  • 11
  • 1
  • 5