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?
I think you mean this. If you start with a landscape image bean.jpg:
magick bean.jpg -gravity center -extent "%[fx:h<w?h:w]x%[fx:h<w?h:w]" result.jpg
If you start with a portrait image, scooby.jpg:
magick scooby.jpg -gravity center -extent "%[fx:h<w?h:w]x%[fx:h<w?h:w]" result2.jpg
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.
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.
I find this easier to remember:
convert in.png -gravity Center -extent 1:1 out.png
Related
I am trying to use ImageMagick to place one image (Poseidon_map01.jpg in my code below) in the top-left corner of another (zzOcean Backdrop.png) then crop the resulting image down to 1280x720.
This is the command I currently have:
".\ImageMagick-7.0.8-12-portable-Q16-x64\magick.exe" ".\Raw\zzOcean Backdrop.png" ".\Raw\Poseidon_map01.jpg" -gravity northwest -composite -crop 1280x720>! +repage ".\1280x720\Poseidon_map01.jpg"
The problem is that this command is cutting the composited image into 1280x720 pieces then saving all of those pieces with the names Poseidon_map01-1.jpg to Poseidon_map01-48.jpg. Poseidon_map01-1.jpg is the top-left corner of the composited image and this is the piece that I want to keep. I want to discard the rest of the composite. Does anyone know what I have to change in my command to make this happen? Thanks.
In Imagemagick crop, if you do not include the +X+Y offsets, it will crop as many pieces as it can given the size you specify. That is called tiled cropping. So use -crop WxH+X+Y>!
See https://imagemagick.org/Usage/crop/#crop
".\ImageMagick-7.0.8-12-portable-Q16-x64\magick.exe" ".\Raw\zzOcean Backdrop.png" ".\Raw\Poseidon_map01.jpg" -gravity northwest -composite -crop 1280x720+0+0>! +repage ".\1280x720\Poseidon_map01.jpg"
I am not a windows user and syntax may vary some. You may need to escape the > or ! with ^ on windows or put double quotes around the whole crop set of arguments. Keep that in mind, if this does not work. See https://imagemagick.org/Usage/windows/
I have two pngs. One is of unknown size (but always square), the 2nd is 1024x1024 and mostly transparent. I want to put the 2nd on top of the first, but first scale it down to the size of the first.
E.g. image1.png is 100x100, overlay.png is 1024x1024. The resulting image size is 100x100 with the overlay scaled down to 100x100 and put on top of the source file.
So far I got this:
magick convert ~/Downloads/Test\ icon.png res/drawable/icon.png -gravity center -composite ~/result.png
But the resulting image is 1024x1024 and the original is tiny somewhere in the center.
This will read in both images, resize the second to fit within the dimensions of the first, then composite the second centered over the first.
magick img1.png img2.png \
-resize %[fx:u.w]x%[fx:u.h] -gravity center -composite output.png
If used in Windows, that continued line backslash "\" should be changed to a caret "^". If used in a Windows BAT script, the single percent signs "%" need to be doubles "%%".
EDITED TO ADD: The way that works is this... Two images are read into the command. The FX expressions "u.w" and "u.h" stand for the width and height of the first image. So to "-resize" first image to its own dimensions doesn't change it, of course. And the second gets resized to fit within the dimensions of the first.
I have images of unknown size and ratios. I wish to take a centred 16:9 crop.
If all the images were known 4:3, then it's easy: 9/16 รท 3/4 = 0.75 so I simply set the height to 75% of the original like this:
convert photo.jpg -gravity center -crop '100%x75%' +repage photo16x9.jpg
However, if the photo is already 16:9 (or even wider), I don't want to crop it, and if it is 'taller' than 16:9 I want to crop it only by the amount necessary to achieve a 16:9 crop.
This would be also easier if I wanted to scale to a known width or height (example question for that). But I'm looking to leave as much of the original image data in place as poss.
Therefore I'm looking for a way to crop the height to a factor of the image's width, with a cut-off.
I hoped this might work
convert photo.jpg -gravity center \
-crop '100%x[fx:9/16*w]' +repage photo16x9.jpg
but alas it seems the [fx:...] is not allowed in a -crop argument.
Hacking a bit I found somewhere that I could not for the life of me understand(!) also failed, but I'll list it here to show research effort!
convert photo.jpg -set option:distort:viewport \
'[fx: w]x[fx: w*9/16 ]+0+0' -filter point \
-distort SRT 0 +repage photo16x9.jpg
(I realise that neither attempts above cover the case when the image is already wider than 16:9, but if the [fx:...] thing worked I could achieve that by using the ternary operator, so I kept the examples simple.)
Maybe you can just calculate the aspect ratio and use that to make a decision. Create two test images, one 15x9 and one 17x9:
convert -size 15x9 xc:black 15x9.png
convert -size 17x9 xc:black 17x9.png
Now ask ImageMagick to tell you if the aspect ratio is wider than 16:9 or less than or equal to 16:9:
convert 15x9.png -format "%[fx:(w/h)>16/9]" info:
0
convert 17x9.png -format "%[fx:(w/h)>16/9]" info:
1
I have images of variable height and width, and I want to crop them all to be 200 pixels high, without any resizing. i.e. their width will remain the same, and height will be 200px. I want to use "north" gravity for this.
Any CLI tool that allows batch processing would be OK - imagemagick/mogrify, gm, etc. I've tried to figure this out but can't find any option to just leave width unspecified.
mogrify *.png -gravity North -extent x200 -path ./complete
The mogrify utility would suite your batch task with -gravity, -extent, and -path options:
You could leave the width unspecified by, well, leaving the width unspecified, and use extent to do the cropping:
convert in.png -extent x200 out.png
or, say,
mogrify -extent x200 *.png
For the wizard example image, that would turn this into this. You can see more resizing examples on the examples site, as well as some different ways of doing cropping.
Can ImageMagick's convert append white or black bars to maintain aspect ratio after specifying just the aspect ratio?
More concretely
Suppose I have a 2000x1000 widescope image and I would like to compute a new image that has an aspect ratio of 4:3 to fit, say a TV. I can do
convert input.png -background black -extent 2000x1500 -gravity center output.jpg
But here I have manually chosen 2000x1500 to produce an extra 250x2 pixels of blakc. Can I ask convert to:
change the aspect ratio to 4:3
not lose any pixels; not interpolate any pixels
center the image
?
If it's also possible to chose the background color as the dominant color in the image (as in iTunes 11), do mention how.
Convert does not have the built-in capability to pad an image out to a given aspect ratio, so you will need to script this. Here is how this might be done in bash:
#!/bin/bash -e
im="$1"
targetaspect="$2"
read W H <<< $(identify -ping -format "%w %h" "$im")
curaspect=$(bc <<< "scale=10; $W / $H")
echo "current-aspect: $curaspect; target-aspect: $targetaspect"
comparison=$(bc <<< "$curaspect > $targetaspect")
if [[ "$comparison" = "1" ]]; then
targeth=$(bc <<< "scale=10; $W / $targetaspect")
targetextent="${W}x${targeth%.*}"
else
targetw=$(bc <<< "scale=10; $H * $targetaspect")
targetextent="${targetw%.*}x$H"
fi
echo convert "$im" -background black \
-gravity center -extent "$targetextent" \
output.jpg
Call this script with the input image and the target aspect ratio given as a floating point number (for example, 4/3 = 1.333):
$ do-aspect input.png 1.333
Notes:
bc is used for floating point math, because bash itself has only integer arithmetic.
Note that -gravity center is on the final command line before -extent. This is because gravity is a setting while extent is an operator. Settings should always precede the operators that they affect, or else convert will do unexpected things when your commands start to get more complicated.
When you're happy with the results of the program, you can either copy and execute its output, or just remove the echo from before the final convert.
To your question about finding the dominant color of an image, there are different ways of doing that, but one common method is to resize the image to 1x1 and output the color of the resultant pixel - it will be the average color of the whole image:
convert input.png -resize 1x1 -format '%[pixel:p[0,0]]' info:
You can do other processing on the image before resizing to get a different heuristic.