I have a bunch of photos. They're all the same height, and I'm trying to put them together into a composite image using ImageMagick's montage program via Bash. The problem is that by default, the tile size (one image occupies one tile) is equal to the image with the largest dimensions. Narrow images, therefore, are surrounded by a lot of white space. I want to remove this white space. How can I do it?
Try something like this:
montage file1.jpg file2.jpg -geometry +0+0 -background none output.jpg
This will make the border between images as small as possible and whatever is there will be transparent.
To see a demo of the difference using builtin images, try these and compare:
$ montage rose: -resize 100x60 rose: -geometry +0+0 -background none montage.jpg $ display montage.jpg & $ montage rose: -resize 100x60 rose: montage.jpg $ display montage.jpg &
See Montage Usage.
If you post an example of what you're getting and manually edit together an example of what you'd like as a result, we might be able to get a little closer to that.
Here are examples that I like better than the ones I originally posted above:
montage \( rose: -resize 100x46\! \) rose: -background gray montage.jpg
montage \( rose: -resize 100x46\! \) rose: -geometry +0+0 -background none montage.jpg
I'd agree with the accepted answer of
-geometry +0+0 to remove extra tile space, and I'd add
-mode Concatenate (under certain conditions).
Also, once you have differing sizes with
montage, it gets a bit difficult to discern between what is "tile background" (tile space) vs "frame" and "border" - I lose track myself often, so here is a small test case with (clickable) images:
#$ montage --version # done on: #Version: ImageMagick 6.6.2-6 2012-08-17 Q16 http://www.imagemagick.org # pipe to `display` (for preview): # montage img1.png img3.png img2.png img4.png bmp:- | display # generate images first convert -size 200x100 xc:red img1.png convert -size 300x200 xc:blue img2.png convert -size 400x300 xc:green img3.png convert -size 500x400 xc:orange img4.png # #01: direct montage (-tile 2x2 automatic for four images) # note: mont01.png is 256x252 pixels! montage img1.png img3.png img2.png img4.png \ mont01.png
# "The 'tile' size is then set to the largest dimentions # of all the resized images, and the size actually specified." # "by removing the 'size' component, non of the images will # be resized, and the 'tile' size will be set to the largest # dimensions of all the images given" # #02: specify -geometry offset (tile spacing) # note: mont02.png is 1008x808 pixels now! montage img1.png img3.png img2.png img4.png \ -geometry +2+2 \ mont02.png
# #03: add border to command #02: # border sticks around images themselves montage img1.png img3.png img2.png img4.png \ -geometry +2+2 -border 5 \ mont03.png
# #04: add frame to command #02: # frame is around the tile (and is "3D") - and # background (which isn't image) is colored default gray: montage img1.png img3.png img2.png img4.png \ -geometry +2+2 -frame 5 \ mont04.png
# #05: add background color spec to command #04: # that is background behind the tiles - not of the tiles montage img1.png img3.png img2.png img4.png \ -geometry +2+2 -frame 5 -background "brown" \ mont05.png
# #06: add mattecolor to command #05: # "-mattecolor The color used as the frame color." # but just changes color of the "3D" frame borders montage img1.png img3.png img2.png img4.png \ -geometry +2+2 -frame 5 -mattecolor "white" -background "brown" \ mont06.png
# #07: add bordercolor to command #05: # "-bordercolor The fill color inside the frame for images, or any border padding." # this does change the color of time background montage img1.png img3.png img2.png img4.png \ -geometry +2+2 -frame 5 -bordercolor "purple" -background "brown" \ mont07.png
# #08: both frame and border : # no difference from command #07 - # once the tiles are resized, the entire remaining # background is used as a "border", and specifying # "-border 5" size for it has no effect montage img1.png img3.png img2.png img4.png \ -geometry +2+2 -frame 5 -border 5 -bordercolor "purple" \ mont08.png
# #09: add mode Concatenate (with -tile) to #08 # No difference from #08 montage img1.png img3.png img2.png img4.png \ -mode Concatenate -tile 2x2 -geometry +2+2 -frame 5 -border 5 -bordercolor "purple" \ mont09.png
# #10 remove -frame, from #09 # now there is no tile background, and # images are not centered in tiles (they # have gravity NorthWest instead) montage img1.png img3.png img2.png img4.png \ -mode Concatenate -tile 2x2 -geometry +2+2 -border 5 -bordercolor "purple" \ mont10.png
# #11 Mode Concatenate with only -tile # images are without padding (as much as possible) montage img1.png img3.png img2.png img4.png \ -mode Concatenate -tile 2x2 -border 5 -bordercolor "purple" \ mont11.png
# #12 Try geometry +0+0 instead of concatenate # almost the same as #11, except more correct overall borders montage img1.png img3.png img2.png img4.png \ -tile 2x2 -geometry +0+0 -border 5 -bordercolor "purple" \ mont12.png
Well, hope this can be found useful,
EDIT: I put together a small Python/Tkinter/PIL GUI for ImageMagick, tkGui_ImageMagick.py - and finally I could find the proper command line for something that I wanted: to make a montage of four images, where the tiles' height & width are matched to whatever is the largest width of that column (or height of row).
In this example, img1 (200x100) and img2 (300x200) go in first column, the larger width is 300 - which should set the tile width of img1. Also, img1 needs to have it's height correlated to the larger height of img3 (300 px), with which it forms a row. This can be specified via the
extent operator (see also ImageMagick • View topic - Resize and pad instead of stretch). And that command line requires subprocess calls to separate
montages for each column - and from there, separate
converts for each image:
montage \ <(montage \ <(convert \ img1.png -gravity center -extent 300x300 \ bmp:-) \ <(convert \ img2.png -gravity North -extent x400 \ bmp:-) \ -tile 1x -geometry +0+0 \ bmp:-) \ <(montage \ <(convert \ img3.png -gravity center -extent 500x \ bmp:-) \ img4.png \ -tile 1x -geometry +0+0 \ bmp:-) \ -geometry +0+0 -border 2 \ mont13.png # or as one liner: montage <(montage <(convert img1.png -gravity center -extent 300x300 bmp:-) <(convert img2.png -gravity North -extent x400 bmp:-) -tile 1x -geometry +0+0 bmp:-) <(montage <(convert img3.png -gravity center -extent 500x bmp:-) img4.png -tile 1x -geometry +0+0 bmp:-) -geometry +0+0 -border 2 mont13.png
Note here, if we had used
-extents directly in the montage line, like this:
montage \ img1.png -extent 300x200 -gravity center \ img2.png -extent 0x400 -gravity North \ -tile 1x -geometry +0+0 \ bmp:-
... we will notice that the first specification for height (200) will be ignored, and 400 as larger will be applied to both tiles anyways!
Thus we must control the padding of each individual image (by calling
extents for each) - and then avoid
extents in the
montage line; and as such, we must apriori know the (largest) width of each column (and height of each row). Note also:
- Since img1 is smaller than the implied width/height of its neighbors, we must explicitly set both width and height in its extents
- The other dimension can have only the relevant dimension specified - and img4, as largest, doesn't have to be padded (and ran through
convert) at all
-gravityhas to come after (fully specified: w & h)
montage, but feels more accurate with the following :
convert 1.jpg 2.jpg 3.jpg -geometry x500 +append -gravity South a.png
x500 being the desired final height