I have a set of images, and I can use the Imagemagick montage command on them to produce a montage image file with transparent background (let's call this fgimg). Now I have another existing image (let's call this bgimg) that I'd like to use (after some special processing with the convert command) as the background for fgimg, which can be achieved within the same convert command. At this point it seems trivial to avoid writing the temporary fgimg to disk, simply by piping the standard output of montage to the standard input of convert.

My problem is that the special processing I'm applying to bgimg will require some knowledge of the image properties of fgimg (e.g., resizing bgimg to have the same size as fgimg), which I don't know in advance. How can this information be retrieved and used in the convert command?

Note: I'm using Imagemagick version 6.9.7-4 on Linux.


I'll include some commands below to further illustrate the problem in detail.

The following command produces the montage image fgimg from a set of input images. The output is in the 'special' miff format (which seems best for temporary output to be worked on later), and has transparent background so that the actual background can be applied later. Most of the other options here are not important, but the point is that the output size (dimensions) cannot be determined in advance.

montage input_*.jpg -tile 5x -border 2 -geometry '200x200>+20+20' \
  -gravity center -set label '%f\n%G' -background none -fill white \
  -title 'Sample Title' miff:fgimg

Next, I have another input image bgimg.jpg. I want to perform some processing on it before using it as background to fgimg. The processing can be quite complex in general, but in this example, I want to:

  1. resize bgimg.jpg to fit inside the dimensions of fgimg without any cropping;
  2. apply a fade-to-black effect around the edges;
  3. make it the same size as fgimg, with a black background;
  4. combine this with fgimg to produce the final output.

Notice that I need the size of fgimg in two places. I can first extract this into a shell variable:

size=$(identify -format '%G' miff:fgimg)

Then I can do all the steps above in one convert command (note that $size is used twice):

convert "bgimg.jpg[$size]" -gravity center \
  \( +clone -fill white -colorize 100% -bordercolor black \
  -shave 20 -border 20 -blur 0x20 \) -compose multiply -composite \
  -background black -compose copy -extent $size \
  miff:fgimg -compose over -composite final_out.jpg

Now here is the problem: I want to avoid writing the temporary file fgimg to disk.

I could replace miff:fgimg with miff:- in both the montage and convert commands and then just pipe one to the other: montage ... | convert .... But how do I deal with the $size?

I tried to use file descriptors (miff:fd:3) but this does not seem to work, which is confirmed by the comments to this question.

Is there a way to do this (in Imagemagick v6) without creating a temporary file?

1

There are 1 answers

4
GeeMack On BEST ANSWER

This example command uses ImageMagick v6 on a bash shell. Instead of "montage" it starts by using "convert" to create a "logo:", one of IM's built-in sample images, then pipes it out as a MIFF and into the "convert" command that follows. You can pipe the output of "montage" just as easily. And it uses another IM built-in image "rose:" as your "bgimg.jpg"...

convert logo: miff:- | convert - rose: \
   +distort SRT "%[fx:t?min(u.w/v.w,u.h/v.h):1] 0" \
   -shave 1 +repage -gravity center -bordercolor black \
   \( -clone 1 -fill white -colorize 100 -shave 6 -border 6 \
      -blur 0x6 -clone 1 -compose multiply -composite \) -delete 1 \
   \( -clone 0 -alpha off -fill black -colorize 100 \
      -clone 1 -compose over -composite \) -delete 1 \
   +swap -composite final_out.jpg

That reads the piped image "-" and the background image "rose:".

Then it uses "+distort" with an FX expression to scale "rose:" to the maximum dimensions that still fit within the original piped input image. That operation adds a pixel all around so we use "-shave 1" to get rid of that.

Next inside parentheses it clones that re-scaled background image, makes an edge blur mask, and composites them to make the fade-to-black edge on the background image. Right after the parentheses it deletes the non-edged background image.

In the next parentheses it clones the input image, makes it black, clones the modified background image, and composites it centered over the black one. Again the non-extended background image is discarded after the parentheses with "-delete 1".

Finally the modified background and the input image are put in the proper order with "+swap" and composited for the final output. Run this command without the last "-composite" to see the two images that result from the prior parts of the command.

Both the main input image and background image can be any size, any dimensions, and any aspect ratio. This works for me on v6.8.9 on a bash shell. It should work on any ImageMagick newer. It should work on Windows by removing all the backslashes that escape parentheses and changing the continued-line backslashes "\" to carets "^".

EDITED TO ADD:

You can use that FX expression to find the scaling amount, save it as a variable, then isolate the background image inside parentheses and use that variable to do the scaling and shaving there. That way it only affects the background image. There may be a rounding error with that, but the main image, which determines the exact final output dimensions, will be unaffected. Note the difference in the first few lines of this command...

convert logo: miff:- | convert - rose: \
   -set option:v1 "%[fx:min(u.w/v.w,u.h/v.h)]" \
   \( -clone 1 +distort SRT "%[v1] 0" -shave 1 \) -delete 1 \
   +repage -gravity center -bordercolor black \
   \( -clone 1 -fill white -colorize 100 -shave 6 -border 6 \
      -blur 0x6 -clone 1 -compose multiply -composite \) -delete 1 \
   \( -clone 0 -alpha off -fill black -colorize 100 \
      -clone 1 -compose over -composite \) -delete 1 \
   +swap final_out.jpg