image-magic: automatically adding the filenames of the stacking images - imagemagick

My bash script uses convert and montage of the Image magic to stack 2xN multi-image chart.
Convert:
convert \( "${output}/type1*.png" -append \) \( "${output}/type2*.png" -append \) +append -background white -alpha deactivate ${output}/summary.png
Montage:
montage \( "${output}/type1*.png" \) \( "${output}/type2*.png" \) -geometry 800x600+1+1 -tile x2 -frame 4 -background white -mattecolor lightgoldenrod2 -mode Frame -bordercolor white ${output}/summary.png
Would it be possible via some option to automatically add the name of the stacked images directly on each initial png file?

Of course. I made 4 randomly coloured input images, each 100x100 like this:
magick -size 2x2 xc: +noise random -crop 1x1 -scale 100x100 +repage type-%02d.png
They are named like this:
-rw-r--r--# 1 mark staff 595 11 Oct 10:35 type-00.png
-rw-r--r--# 1 mark staff 594 11 Oct 10:35 type-01.png
-rw-r--r--# 1 mark staff 595 11 Oct 10:35 type-02.png
-rw-r--r--# 1 mark staff 595 11 Oct 10:35 type-03.png
The basic technique you need is to:
iterate over the input images
add a label to each one
aggregate and pipe all the resulting individual images into montage
#!/bin/bash
for f in type-*png; do
magick "$f" -gravity South -fill black -background Plum -font "/System/Library/Fonts/Supplemental/Comic Sans MS Bold.ttf" -splice 0x18 -annotate +0+2 "$f" miff:-
done | magick montage -geometry +1+1 -tile x2 miff:- montage.png
Here's another example, but with an under-colour underneath the text and the text directly on the image and a transparent background to the montaged output:
#!/bin/bash
for f in type-*png; do
magick "$f" -fill white -undercolor '#00000080' -gravity South -annotate +0+5 "$f" miff:-
done | magick montage -background none -geometry +1+1 -tile x2 miff:- montage.png
Obviously you can diddle around with the colours, fonts, positioning as you wish, but the core technique remains the same.
Note: MIFF: is just "Magick Image File Format", a format specific to ImageMagick that is guaranteed to maintain and pass on all aspects of its input - from bit-depth, through comments, through quality, through transparency down to EXIF data and beyond.
Note: The annotation methods I have used are derived from the excellent work by Anthony Thyssen linked here and there are many, many other examples - well worth a read.

Here is a simple way to do that in Imagemagick.
Lets create images as in Mark Setchell's answer:
convert -size 2x2 xc: +noise random -crop 1x1 -scale 100x100 +repage type-%02d.png
Now we use the -label option in montage:
(use %t if you do not want the suffix and %f if you do want the suffix on the file name)
montage -label "%t" *.png -tile 2x2 -geometry +5+5 result.png
Or
montage -label "%t" *.png -tile 2x2 -geometry +5+5 result2.png

Related

Imagemagick - move/offset images within a tiled montage?

Consider this example (Ubuntu 18.04, ImageMagick 6.9.7-4 Q16 x86_64 20170114):
convert -size 300x100 xc:red red.png
convert -size 100x100 xc:blue blue.png
montage red.png blue.png -frame 5 -geometry '+0+0' -tile 1x redblue.png
This gives the following image:
What I'd like to do, is move the blue square on arbitrary x position "within its tile"; say align left edge of blue square to where 25% of the red rectangle width would be, or at 50% - or even align right edge of blue square with right edge of red rectangle.
I have seen that -tile-offset exists (https://imagemagick.org/script/command-line-options.php#tile-offset), and I've tried it with this example, but it doesn't look like it does anything.
How can I move an image, part of a ImageMagick montage, within its tile?
EDIT: it looks like -tile-offset can only be specified for explicit tile images (not as in -tile 1x, but as in -tile red.png), and:
Tile smaller image over a background with offsets? - ImageMagick
-tile-offset must come before the tiling. It represents a single global offset, not the spacing for the tiling.
Here's an example:
convert -size 300x100 radial-gradient:\#400-\#FAA red.png
convert -size 500x500 xc: -tile-offset +100+40 +size -tile red.png -border 5 -geometry +5+5 -draw "color 0,0 reset" out.png
then out.png is this (click for full image):
... so to clarify - I'd like to know is its possible to move an image within a tile as obtained in montage tile 1x
As suggested in the comment:
convert -background none red.png \( -size 25x xc:none blue.png +append \) -append result.png
Or with 2 different offsets:
convert -background none red.png \
\( -size 25x xc:none blue.png +append \) \
\( -size 50x xc:none blue.png +append \) \
-append result.png
Not sure what your end-goal is, but you can also do this:
convert -gravity east -background none red.png blue.png red.png blue.png -append result.png
Or this:
convert -gravity center -background none red.png blue.png red.png blue.png -append result.png
In ImageMagick 6, another way is to extend the transparent background, then composite the blue image in the center of the bottom half of the extended image.
convert -size 300x100 xc:red -background none -extent 300x200 -size 100x100 xc:blue -geometry +100+100 -composite result.png

How to split an image with a grid and preserve transparency bounding box

I have some png images that I want to split it into parts, like by grid or size.
But each part should have the same bounding box (transparency) as original image.
Example:
Splitting image into 2 parts.
Original: 200 × 89
Output:
part_1.png, 200 × 89
part_2.png, 200 × 89
Can ImageMagick do this? Or any other app or method.
My actual goal is to split into 100+ slices images.
EDIT:
Another goal to have an indents for each slice. Say indent = 10px.
Example:
Input: 200 x 100
Output:
part_1.png, 200 x 100
part_2.png, 200 x 100
And just as example, to visually compare input and output: combined output images in Photoshop as layer added one onto another
200 x 100 :
Also this is showing input image added onto combined(so it's better to see what was cropped and how):
In ImageMagick, you can split an image into many parts with the -crop command. For your example above with two parts, you can do that with the following commands. ImageMagick will append -0, -1 ... to the output file names.
ImageMagick 6:
dim=`convert image.png -format "%wx%h" info:`
convert \( -size $dim xc:none \) null: \( image.png -crop 50x100% \) -layers composite result.png
ImageMagick 7:
magick \( image.png -set option:dim "%wx%h" -crop 50x100% \) null: \( -size "%[dim]" xc:none \) -reverse -layers composite result.png
The results are:
See
http://www.imagemagick.org/Usage/crop/#crop
http://www.imagemagick.org/Usage/crop/#crop_percent
http://www.imagemagick.org/Usage/crop/#crop_tile
http://www.imagemagick.org/Usage/crop/#crop_quad
http://www.imagemagick.org/Usage/crop/#crop_equal
http://www.imagemagick.org/script/command-line-options.php#layers
Note that -crop keeps the virtual canvas information if you do not add +repage afterwards. So to put the individual images back into their original placement, you have to composite them onto a transparent background the size of the input. That is done in one command using -layers composite using the null: separator.
Here is another way to add transparent areas between parts of a crop in ImageMagick. Crop the image into pieces, chop off the parts you want to remove, then pipe to montage to add the spacing back.
Input:
Here I make this into a 4x4 grid of images with 10 pixel spacing:
convert lena.png -crop 25%x25% +repage -gravity east -chop 10x0 -gravity south -chop 0x10 +repage miff:- | montage - -background none -tile 4x4 -geometry +5+5 result.png
To answer your new question, you can do that with a script loop. On a Unix-like platform, assuming your images do not have spaces, you can do the following:
cd path/to/current_folder
list=`ls *.png`
for img in $list; do
name=`convert $img -format "%t" info:`
dim=`convert $img -format "%wx%h" info:`
convert \( -size $dim xc:none \) null: \( $img -crop 50x100% \) -layers composite -scene 1 path/to/new_folder/${name}_%d.png
done
If you want leading 0s in the output, say 3, use path/to/new_folder/${name}_%03d.png.
Note that to start with 1 rather than 0, I have added -scene 1.
Sorry, I do not know how to script for Windows.
Please always provide your ImageMagick version and platform.
In ImageMagick, the best way to put transparent areas into your image is with a binary mask that is put into the alpha channel of your image.
convert input.png \( -size 200x89 xc:white -size 10x89 xc:black -gravity center -composite \) -alpha off -compose copy_opacity -composite result.png
You can add as many blank areas as you want by adding more white areas to the mask or by tiling out one region of black and one region of white to create the mask with regular spacing of black and white.
Edited to add this ImageMagick 6 example which splits the input image into 4 pieces, 25% of the original width and 100% of its height, then creates a transparent canvas for each piece the same dimensions of the input image, and locates the pieces at their original offsets on those canvases.
convert input.png -set option:distort:viewport %[w]x%[h] -crop 25x100% \
-virtual-pixel none -distort affine "0,0 %[fx:s.page.x],%[fx:s.page.y]" out%03d.png
The output file names will be numbered starting from zero like "out000.png", etc.
Original message...
Here's a simple command using ImageMagick 7 that can crop an image into any number of pieces, and output all the pieces at their original offsets on transparent backgrounds of the original input dimensions...
magick input.png -crop 100x1# -background none \
-extent "%[fx:s.page.width]x%[fx:s.page.height]-%[fx:s.page.x]-%[fx:s.page.y]" out%03d.png
That "-crop 100x1#" tells it to split the image into a grid 100 pieces wide by 1 piece high. You could just as well specify the crop sizes as percents or numbers of pixels.
Edited again to add:
This following command will split the input image into the individual pieces specified with the "-crop" operator, then shave 5 pixels from every side of each piece, then apply a 5 pixel transparent border to every side of each piece. It will still remember the original locations of the pieces within the input canvas, so the "-distort affine ..." can extend the canvases and place the pieces where they were in the input image.
convert input.png -set option:distort:viewport %[w]x%[h] \
-bordercolor none -background none -virtual-pixel none \
-crop 25x100% -shave 5x5 -border 5x5 \
-distort affine "0,0 %[fx:s.page.x],%[fx:s.page.y]" out%03d.png
To use this command with IM7 you need to change "convert" to "magick".
Given the changes of requirements provided by Kamikaze, here is one way to achieve the split with indent in ImageMagick, assuming I understand correctly.
dim=`convert image.png -format "%wx%h" info:`
convert \( -size $dim xc:none \) null: \( image.png -crop 50x100% -shave 5x5 \) -geometry +5+5 -layers composite result.png
To check, I flatten over a blue background:
convert result-0.png result-1.png -background blue -flatten result.png

Montage 3 images in a 2x2 grid, first in top center (like "triforce")?

Not sure how to put this question otherwise - but let's assume I have three square images. I'd like to arrange them in a sort of a square 2x2 grid, such that image 2 is bottom left, image 3 is bottom right - and image 1 is top center (so image 1 is not in the cells of the grid on top; neither left cell, nor right cell, but in center of the row delimited by them).
Closest I could get was with this test, done on Ubuntu 14.04, montage --version ImageMagick 6.7.7-10 2017-07-31 Q16:
montage \
<(convert -size 100x100 xc:green bmp:-) \
<(montage \
<(convert -size 100x100 xc:blue bmp:-) \
<(convert -size 100x100 xc:red bmp:-) \
-geometry +5+5 bmp:- \
) \
-geometry +5+5 -tile 1x2 bmp3:- | display
... or as one-liner:
montage <(convert -size 100x100 xc:green bmp:-) <(montage <(convert -size 100x100 xc:blue bmp:-) <(convert -size 100x100 xc:red bmp:-) -geometry +5+5 bmp:- ) -geometry +5+5 -tile 1x2 bmp3:- | display
The image produced is:
What I want instead is something like this (I edited this manually in an image editor):
... that is, somewhat like that old meme Triforce (Wikipedia)
How could I achieve that with ImageMagick's montage?
This might be a case where ImageMagick's "convert" command would serve you better than "montage". Here is an example that should get you pretty much the same result...
convert -size 100x100 xc:green xc:blue xc:red -bordercolor white -border 5 \
\( -clone 1,2 +append \) -delete 1,2 -gravity center -append -border 5 out.bmp
Using "convert" can give you more freedom to arrange the images using "+append" and "-append" to attach them, "-gravity" for alignment, and "-border" for spacing.

How do I make the images equidistant using imagemagick/montage?

Currently I'm using this command:
montage IMG*.JPG -tile 3x1 -geometry 150x100+40+40 -background '#000000' triptych.jpg
And it produces an output like this (red lines added):
The problem (as indicated) is that my images have too much space between them, and that makes me sad.
I'm looking to create something that looks more like this, with the border equal all the way around:
I checked the manpage and several online guides, but none of the options that I tried (-mode concatenate, changing the geometry to +40+20) did what I wanted.
How do I get the output that I want using imagemagick?
If you are just making a triptych, you may get on better with convert +append to lay out images in a row with spacers. So, if your images are 1.png, 2.png and 3.png:
convert -background black \
1.png xc:black[10x] 2.png xc:black[10x] 3.png +append \
-bordercolor black -border 10 result.png
The xc:black[10] are just the two spacers that you can set the width of explicitly. Then the three images with spacers are set in a horizontal row, using +append. Finally, at the end, I put a border around the whole lot with -border.
Or, showing how you have full control over all aspects:
convert -background black \
1.png xc:black[15x] 2.png xc:black[5x] 3.png +append \
-bordercolor black -border 40 result.png
As Wayne says in the comments, you can resize all the images to a uniform size too, while they are still separate before the -append, so you can do this to make sure no image is wider than 400 pixels.
convert -background black \
1.png xc:black[10x] 2.png xc:black[10x] 3.png -resize 400x\> +append \
-bordercolor black -border 10 result.png
If you want even more control, you can individually resize the images like this:
convert -background black \
\( 1.png -resize WxH \) xc:black[10x] \
\( 2.png -resize AxB \) xc:black[10x] \
\( 3.png -resize MxN \) +append \
-bordercolor black -border 10 result.png
If you want a vertical triptych, use -append in place of +append and set the spacer height with xc:black[x10] rather than xc:black[10x].
convert -background black \
1.png xc:black[x10] 2.png xc:black[x10] 3.png -append \
-bordercolor black -border 10 result.png
Keywords: triptych, diptych, montage, photographer, photography, photo, spacing, spacer, padding
Another method is doing it in two steps.
montage img-*.png -background '#000' -geometry +20+20 step-1.png # step 1
convert step-1.png -bordercolor '#000' -border 20 step-2.png # step 2
With step 1, you got the green spacing. And with step 2, you got the red spacing

Combine and resize multiple images in a square using ImageMagick

So I want to create one large image of size 3600x2280 composed of three smaller images. The first should be resized to 1680x1050 and placed in the top left corner. The 2nd needs to be reiszed to 1920x1200 and placed immediately to the right of it (+1680 over). The 3rd image should be resized to 1920x1080 and placed on the bottom right (+1680+1200). The bottom left will just be blank/transparent.
I've tried various commands I've searched for online and think I'm getting somewhat close with something like the following for just two of the three images:
convert -define png:size=3600x2280 \( Photos/DSC05525-original.jpg -resize 1680x1050 \) -geometry +0+0 -composite \( Photos/Sydney-Loftus-Train-Station-original.jpg -resize 1920x1200 \) -geometry +1680+0 -extent 3600x2280 test.png
...but that places the 2nd image over the first (I think because it doesn't know to extend until the very end?). I've tried various combinations of -composite, -gravity and +repage, but can't seem to find a solution.
There are lots of ways of doing this. Choose the one that corresponds best to the way your mind works! I used test images like this:
1.jpg => red
2.jpg => green (lime actually)
3.jpg => blue
Method 1
convert -background none \
1.jpg -resize 1680x1050! \
\( 2.jpg -resize 1920x1200! \) +append \
\( 3.jpg -resize 1920x1080! -gravity east \) -append \
result.png
That says... "Leave all unpainted areas transparent. Resize image 1. Resize image 2 and place it to the right of image 1 (+append). Resize image 3 and align it East. Append that underneath images 1 and 2 (-append)."
Method 2
convert -background none \
\( 2.jpg -resize 1920x1200! \) \
\( 3.jpg -resize 1920x1080! \) -append \
\( 1.jpg -resize 1680x1050! \) +swap +append result.png
That says... "Load and resize image 2. Load and resize image 3. Place image 3 underneath image 2 (-append). Load and resize image 1. Place image 1 before (+swap) image 2 in the image list. Now append the second image in the list to the right of the first (+append)."
Method 3
convert -background none \
1.jpg -resize 1680x1050! -extent 3600x2280 \
\( 2.jpg -resize 1920x1200! -geometry +1680 \) -composite \
\( 3.jpg -resize 1920x1080! -geometry +1680+1200 \) -composite result.png
That says... "Leave any unpainted areas transparent. Load image 1 resize it then extend the canvas to the full output size to accommodate subsequent images. Load image 2, resize, position and splat onto canvas. Load image 3, resize and splat onto canvas."
Method 4
Just for fun, here's a totally different way of thinking about it:
{ convert 1.jpg -resize 1680x1050! miff:- ; \
convert 2.jpg -resize 1920x1200! miff:- ; \
convert -size 1680x1 xc:none miff:- ; \
convert 3.jpg -resize 1920x1080! miff:- ; } |
montage -background none -geometry +0+0 -tile 2x2 miff:- result.png
That says... "Start a compound statement that will load and resize 4 images and send each of them as a MIFF (Magick Image File Format) to a montage command that will put them all together in 2x2 grid (-tile 2x2) with no spaces between them (-geometry +0+0)."

Resources