0

I am trying to create a collage/montage of 15000 images using imagemagick montage command.The tool works perfectly for a small subset of the images but when trying to create a montage using 15K images the program crashes because it can't just load 15K images into main memory. I think opening the files in streams and then stacking in batches of 100 would work but if someone has a nice solution,it would be helpful.

Spacehead
  • 21
  • 5
  • 1
    This site is not a code-writing service, or a site where you can get general coding advice. You need to read the [tour], the [ask] page, and the [help/on-topic] to learn what is expected of you. You almost always need a [mre] of your own code, and provide enough info to prove that you've exhausted all possible research avenues, and explain clearly what issue or error you are having, so that users can post answers, with code, that solves that specific issue. This is a very broad question. If there's a specific thing you are having trouble with in the implementation, ask about that specifically. – Random Davis Jan 27 '22 at 22:29
  • What OS do you use, please? – Mark Setchell Jan 27 '22 at 22:39
  • I am using Linux,Ubuntu and Elementary OS – Spacehead Jan 27 '22 at 22:52
  • And what shape do you expect the 15,000 images to be montaged into? 100 images wide by 150 images tall? – Mark Setchell Jan 27 '22 at 22:55
  • Yes exactly or I can do 150*100 also – Spacehead Jan 27 '22 at 22:58
  • You will need to modify your policy.xml file to increase RAM or make use slower disk space if you do you have enough RAM to hold your input image and the final montage. See https://imagemagick.org/script/resources.php – fmw42 Jan 27 '22 at 23:10
  • Yes i already tried modifying the disk value in policy.xml but still no success – Spacehead Jan 27 '22 at 23:14

2 Answers2

2

libvips can make huge image montages without needing a lot of memory. For example:

$ vips arrayjoin "a.png b.png c.png d.png" output.png --across 2

This will read the four PNG images, lay them out in rows in a 2 x 2 grid, and write to out.png. You can use $() to find lots of images and sort by filename, perhaps:

$ vips arrayjoin "$(ls *.jpeg | sort -t_ -k2g -k1g)" x.tif --across 20

That will load all JPEG images with filenames of the form x_y.jpeg, sort them numerically by row and then by column, and assemble them into a 20 x N grid.

15,000 images is too much for the command-line on linux (you'll get an Argument list too long error from bash), but you can do it from python with pyvips. Perhaps:

#!/usr/bin/python3

import sys
import glob
import pyvips

images = [pyvips.Image.new_from_file(filename, access="sequential")
          for filename in glob.glob(sys.argv[1] + "/*.jpg")]

joined = pyvips.Image.arrayjoin(images, across=100)

joined.write_to_file(sys.argv[2])

Then:

$ vips crop ~/pics/k2.jpg x.jpg 0 0 1000 1000
$ for i in {1..15000}; do cp x.jpg $i.jpg; done
$ VIPS_CONCURRENCY=1 /usr/bin/time -f %M:%e \
    ./arrayjoin.py . x.tif[compression=jpeg,tile]
4689264:78.38
$ vipsheader x.tif
x.tif: 100000x150000 uchar, 3 bands, srgb, tiffload

So that's assembling 15,000 1,000 x 1,000 pixel JPG images to make a 100,000 x 150,000 pixel JPEG-compressed TIFF. It takes 80s and needs a peak of 4.7gb of memory.

jcupitt
  • 10,213
  • 2
  • 23
  • 39
1

It's a bit late here to write and test anything, but I'd probably do something like this:

  • generate a text file with all the filenames to montage
  • use split command to break that into groups of 100
  • iterate over all the groups making a row of images from each
  • stack the rows

First part is like:

find . -name "*.jpg" -print > filelist.txt

Next part is like:

split -l 100 filelist.txt ROWS

Next part is like:

n=0
for f in ROWS* ; do
    magick @"$f" +append row${n}.jpg
    ((n=n+1))
done

Last part is like:

magick row*.jpg -append result.jpg
Mark Setchell
  • 191,897
  • 31
  • 273
  • 432