Search code examples
imagemagickcropaspect-ratio

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


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.


Solution

  • 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