ImageMagick: how to minimally crop an image to a certain aspect ratio?
Asked Answered
K

5

34

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.

Krissie answered 21/1, 2014 at 15:34 Comment(2)
There's a script for that at Fred's ImageMagick Scripts. 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.Decameter
Wonderful! Lots of useful stuff there BTW :)Krissie
H
42

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

Hydrotropism answered 4/5, 2016 at 21:9 Comment(0)
E
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.

Eserine answered 29/3, 2018 at 16:56 Comment(2)
It seems to be added in 7.0.7-22 (2018-01-06) according to the change log ?Buckshot
Yes, it was also new to IM 7 at about that version. If using IM 7, then use magick rather than convert.Eserine
Z
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.

Zoon answered 12/1, 2017 at 16:50 Comment(0)
H
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.

Helpmate answered 11/2, 2016 at 19:11 Comment(0)
J
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
Johnathon answered 29/3, 2018 at 13:46 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.