Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

ImageMagick: get biggest square out of image

Tags:

imagemagick

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?

like image 414
swalkner Avatar asked Oct 03 '17 08:10

swalkner


2 Answers

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.

like image 108
Mark Setchell Avatar answered Nov 20 '22 06:11

Mark Setchell


If you're using ImageMagick v7, Mark Setchell has provided a simple method above (or below). If you're using IMv6, you can crop the largest center square from any image using a command lke this...

convert input.png -set option:distort:viewport "%[fx:min(w,h)]x%[fx:min(w,h)]" \
    -distort affine "%[fx:w>h?(w-h)/2:0],%[fx:w<h?(h-w)/2:0] 0,0" output.png

That sets the output viewport size to the largest square you can crop from the input image. Then it adjusts the position of the input image so it is centered within that square viewport.

This command should work from a command prompt or script on most *nix systems. If you're using Windows, replace that continued line backslash "\" with a caret "^". If you're using a BAT script in Windows you'll also have to make all the single percent signs "%" into doubles "%%".

You can also simply change "convert" to "magick" to run this command using IMv7.

like image 3
GeeMack Avatar answered Nov 20 '22 07:11

GeeMack