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

女生的网名这么多〃 提交于 2019-12-03 01:28:37

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 occurences of 1920x1080 in solution 1 or the aw/ah variables in solution 2 to get your desired aspect ratio.

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

Input:

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

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.

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.

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.

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
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!