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
.
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
来源:https://stackoverflow.com/questions/21262466/imagemagick-how-to-minimally-crop-an-image-to-a-certain-aspect-ratio