Home > Software design >  Can ImageMagick generate multiple outputs from one input?
Can ImageMagick generate multiple outputs from one input?

Time:09-14

I'm running this from the command line:

magick.exe input.png -shave '1x0' output_%d.png
magick.exe input.png -shave '2x0' output_%d.png
magick.exe input.png -shave '3x0' output_%d.png
magick.exe input.png -shave '4x0' output_%d.png
magick.exe input.png -shave '5x0' output_%d.png
magick.exe input.png -shave '0x1' output_%d.png
magick.exe input.png -shave '0x2' output_%d.png
magick.exe input.png -shave '0x3' output_%d.png
magick.exe input.png -shave '0x4' output_%d.png
magick.exe input.png -shave '0x5' output_%d.png

The first command creates output_0.png but the following commands overwrite the same file. Is there a single command that could generate output_0.png to output_9.png instead?

The ImageMagick documentation says:

Filename References

Optionally, use an embedded formatting character to write a sequential image list. Suppose our output filename is image-%d.jpg and our image list includes 3 images. You can expect these images files to be written:

image-0.jpg 
image-1.jpg
image-2.jpg

That's the closest evidence I found it's possible to do what I'm looking for in ImageMagick. It's not clear to me if I need to leverage shell scripting to do this or if ImageMagick provides a command line feature.

CodePudding user response:

With ImageMagick you can run multiple operations on separate instances of the input image in a single command by cloning the input and isolating the operations inside parentheses like this...

magick input.png \
   \( -clone 0 -shave '1x0' \) \
   \( -clone 0 -shave '2x0' \) \
   \( -clone 0 -shave '3x0' \) \
   \( -clone 0 -shave '4x0' \) \
   \( -clone 0 -shave '5x0' \) \
   \( -clone 0 -shave '0x1' \) \
   \( -clone 0 -shave '0x2' \) \
   \( -clone 0 -shave '0x3' \) \
   \( -clone 0 -shave '0x4' \) \
   \( -clone 0 -shave '0x5' \) \
   -delete 0 output_d.png

That will create 10 output images, each with a sequential filename with the number padded to two places, like "output_00.png ... output_10.png".

To convert this to Windows CMD syntax you can remove all the backslashes that escape the parentheses, and replace the continued-line backslashes "\" with carets "^".

EDITED TO ADD: There are many ways to accomplish this task with ImageMagick. Here is another example command that would produce the same results, but it uses FX expressions so it will only work in IMv7. (The above command should work with IMv6 by changing "magick" to "convert".)

magick input.png -duplicate 9 -shave "%[fx:t<5?t 1:0]x%[fx:t>4?t-4:0]" output_d.png

This reads the input and duplicates it 9 times, then uses FX expressions as arguments to the -shave operation so it can step through all 10 images, shaving each according to the formula in the FX expression.

CodePudding user response:

As @GeeMack points out, there are many ways of doing this with ImageMagick and I was inspired to try a couple of other methods by his marvellous FX expression - just for fun!

Here's what I was originally proposing:

magick input.png -write MPR:orig \
  -shave 1x -write out_1.png \
  -shave 1x -write out_2.png \
  -shave 1x -write out_3.png \
  -shave 1x -write out_4.png \
  -shave 1x -write out_5.png \
  -delete 0--1               \
  MPR:orig                   \
  -shave x1 -write out_6.png \
  -shave x1 -write out_7.png \
  -shave x1 -write out_8.png \
  -shave x1 -write out_9.png \
  -shave x1 out_10.png 

It loads the input image and saves a copy in an MPR or "Magick Persistent Register" called orig. It then shaves a pixel off left and right sides and writes out_1.png, then shaves a further pixel off left and right sides and saves in out_2.png and continues till left and right are shaved by 5 pixels. It then reloads the original from the MPR and shaves off the top and bottom five times.

With a 1080p image, i.e. 1920x1080, this takes 3.4s and uses 96MB of RAM. By comparison, GeeMack's takes 3.5s on my machine and uses 291MB of RAM.

I then parallelised the creation of the left-right shaving with the top-bottom shaving as follows:

magick input.png \
  -shave 1x -write out_1.png \
  -shave 1x -write out_2.png \
  -shave 1x -write out_3.png \
  -shave 1x -write out_4.png \
  -shave 1x out_5.png &

magick input.png \
  -shave x1 -write out_6.png \
  -shave x1 -write out_7.png \
  -shave x1 -write out_8.png \
  -shave x1 -write out_9.png \
  -shave x1 out_10.png &

wait

That takes just 1.97s and peaks at 68MB of RAM.

I think GeeMack's solution is probably simplest and most concise, for most cases, but there may be some mileage in some of the ideas here for some situations, i.e. probably larger images.


In case anyone is interested how to measure the peak RAM usage, I used this:

/usr/bin/time -l ./go
    1.97 real         6.01 user         2.48 sys
        68354048  maximum resident set size
               0  average shared memory size
               0  average unshared data size
               0  average unshared stack size
           10343  page reclaims
               0  page faults
               0  swaps
               ...
               ...
  • Related