Efficiently generating thumbnails with ImageMagick and convert

ensnare picture ensnare · Aug 31, 2012 · Viewed 8.6k times · Source

I'm looking to efficiently generate various sized thumbnails with ImageMagick's convert utility in Python. Some of my image files are quite large (~15MB JPGs).

One way I could do it would be to take the full-sized image, and to generate the various thumbnails from the full-sized image, as follows:

convert sample_image.jpg -resize 1024x768  sample_image-1024x768.jpg
convert sample_image.jpg -resize 800x600   sample_image-800x600.jpg
convert sample_image.jpg -resize 400x300   sample_image-400x300.jpg
convert sample_image.jpg -resize 200x150   sample_image-200x150.jpg

But another way would be to resize the images from each other:

convert sample_image.jpg           -resize 1024x768  sample_image-1024x768.jpg
convert sample_image-1024x768.jpg  -resize 800x600   sample_image-800x600.jpg
convert sample_image-800x600.jpg   -resize 400x300   sample_image-400x300.jpg
convert sample_image-400x300.jpg   -resize 200x150   sample_image-200x150.jpg

Is there any downside to doing this, or perhaps a better way? It seems like this would be a lot more efficient.

As a corollary, are there any flags or "tricks" convert uses to speed up the process?

Answer

Kurt Pfeifle picture Kurt Pfeifle · Oct 2, 2012

ImageMagick has a few tricks up its sleeves which help you to optimize for speed when you want to process large images and when you want to create different output from the same original:

  1. Make use of ImageMagick's mpr:{name} feature, which makes it temporarily save the input image into a named memory program register, from which you can later (while processing) read the data much faster than you could do from harddisk.

  2. Do all resize operations in one single process writing out the different output sizes you require.

And the even better news is you can combine both these into one single command.

So you do not need to run multiple processes with all their context-switching overhead -- do it all in one go.

The following example also crops two separate areas from the original image and creates re-sized thumbnails from them, just to show how many different operations IM can do in one commandline. It also, of course outputs the sizes you requested. (You'll need, of course, a really large-dimensioned input image for the cropping parameters to work).

convert                           \
  huge-original.jpg               \
 -quality 80                      \
 -colorspace rgb                  \
 +profile '*'                     \
 -filter Lanczos                  \
 -write mpr:copy-of-huge-original \
 +delete                          \
  mpr:copy-of-huge-original -crop '3000x2000+0+480'   -resize '200x125!>' -write thumb1-extract.jpg +delete \
  mpr:copy-of-huge-original -crop '2000x1500+280+220' -resize '75x75!>'   -write thumb2-extract.jpg +delete \
  mpr:copy-of-huge-original -resize '1024x768'  -write sample-1024x768.jpg +delete \
  mpr:copy-of-huge-original -resize '800x600'   -write sample-800x600.jpg  +delete \
  mpr:copy-of-huge-original -resize '400x300'   -write sample-400x300.jpg  +delete \
  mpr:copy-of-huge-original -resize '200x150'   -write sample-200x150.jpg  +delete \
  mpr:copy-of-huge-original -resize '163x163!>' -write sample-163x163.jpg

Update

I only now saw the question asked by @JonathanOng: How to stream the output to <stdout>?

Assuming, you want the format going to stdout also be JPEG, you can try this:

convert                           \
  huge-original.jpg               \
 -quality 80                      \
 -colorspace rgb                  \
 +profile '*'                     \
 -filter Lanczos                  \
 +write mpr:copy-of-huge-original \
  mpr:copy-of-huge-original -crop '3000x2000+0+480'   -resize '200x125!>' +write thumb1-extract.jpg \
  mpr:copy-of-huge-original -crop '2000x1500+280+220' -resize '75x75!>'   +write thumb2-extract.jpg \
  mpr:copy-of-huge-original -resize '1024x768'  +write jpeg:- \
  mpr:copy-of-huge-original -resize '800x600'   +write jpeg:- \
  mpr:copy-of-huge-original -resize '400x300'   +write jpeg:- \
  mpr:copy-of-huge-original -resize '200x150'   +write jpeg:- \
  mpr:copy-of-huge-original -resize '163x163!>' +write jpeg:-

This way each variant will go to stdout. How you deal with this stream of consecutive images then, is up to you...

Note, instead of writing -write filename +delete you can use +write filename. It amounts to the same effect.