Search code examples
imagemagick

ImageMagick: get biggest square out of image


I do have thousands of images in different sizes; now I would like to get the biggest square out of it that's possible without transparent/black background. Of course, the ratio should be kept, if it's e.g. a landscape image all height should be in the destination image but the left and right should be cropped; for portrait images, the other way round.

How's that possible?


Solution

  • I think you mean this. If you start with a landscape image bean.jpg:

    enter image description here

    magick bean.jpg -gravity center -extent "%[fx:h<w?h:w]x%[fx:h<w?h:w]" result.jpg
    

    enter image description here

    If you start with a portrait image, scooby.jpg:

    enter image description here

    magick scooby.jpg -gravity center -extent "%[fx:h<w?h:w]x%[fx:h<w?h:w]" result2.jpg
    

    enter image description here


    The part inside the double-quotes is the interesting part. It is basically setting the extent of the image, like:

    -extent 100x100
    

    where 100 is the width and the height. Rather than that though, I am using a calculated expression which tests whether the height (h) is less than the width (w) using a ternary operator. That results in taking whatever is smaller out of the current height and width as the new height and width, So there are really two calculated expressions in there, with an x between them, similar to 100x100.


    Note that this method requires ImageMagick v7 or better - i.e. it uses the magick command rather than v6's convert command. If you have v6, you need to use more steps. First, get the width and the height of the image, then choose the smaller of the two and then issue a convert command with the gravity and extent both set. In bash:

    # Get width and height
    read w h < <(identify -format "%w %h" scooby.jpg)
    
    # Check them
    echo $w,$h
    272,391
    
    # Set `n` to lesser of width and height
    n=$w
    [ $h -lt $n ] && n=$h
    
    # Now do actual crop
    convert scooby.jpg -gravity center -extent "${n}x${n}" result.jpg
    

    If you have thousands to do, I would suggest using GNU Parallel if you are on macOS or Linux. If you are on Windows, sorry, you'll need a loop and be unable to easily use all your CPU cores.

    I have not tested the following, so only try it out on a small, COPIED, sample of a few files:

    # Create output directory
    mkdir output
    
    # Crop all JPEG files, in parallel, storing result in output directory
    parallel --dry-run magick {} -gravity center -extent "%[fx:h<w?h:w]x%[fx:h<w?h:w]" output/{} ::: *.jpg
    

    If the commands look good, remove the --dry-run part to do it for real.