Practical Imagemagick stack (combined) complex commands - imagemagick

I want a combined command that can perform the following task in single execution. I searched the internet, but hardly found any tutorial that guide us to write any stack command. I can found single command for each operation, such as -composite, -blur, etc, and I know I can pipe the command as such
convert ... mpr:- | convert ... mpr:- | ... | convert ... png:-
However, I want a combined command that use \( ... \) and mpr:{label} since this will increase the performance as all operations are executed in single process (pipeline in shell can degrade the performance, and the process sequence is required to be in order).
The process sequence as such:
put flower.png on top of the frame.png -> mpr:framedFlower
put mpr:framedFlower on top of the background.png -> mpr:out2
blur the heart.png, right-gradient-transparent the smiley.png and put both image on top of mpr:out2 -> mpr:out3
annotate the mpr:out3 with "Hello world" (placement=bottom) -> png:-
I don't include the commands that I have tried because they are too messy and it will be an insult to those users who read it. I tried for many hours, but can't get it done. Please advise.

I haven't spent ages futzing with the exact coordinates as I am only using sample pictures, but this one-liner contains every technique you need to do what you are asking.
There is basically one line of code per element in the final image:
convert frame.png -resize 500x400\! \( flower.png -resize 400x300\! \) -gravity center -composite \
background.png +swap -gravity northwest -geometry +100+150 -composite \
\( heart.png -resize 200x200 -blur 0x8 \) -geometry +1200+250 -composite \
-gravity south -pointsize 72 -fill red -annotate +0+60 'Hello world' \
\( emoji.png -resize 250x250 -channel a -fx "u.a*(1-(i/w))" \) -gravity northwest -geometry +1200+500 -composite result.png
The first line reads in the picture frame and the flower and resizes them each independently because of the parentheses and then composites the flower into the frame.
The next line loads the background and then uses +swap to put it behind the framed picture from the previous line. It then sets -gravity to northwest as the origin for the ensuing -geometry before compositing the framed picture onto the background.
The next line loads the heart and resizes and blurs just the heart before compositing that onto the main picture at your specified position.
Next up is the annotation - the only interesting thing is that I set the -geometry to south which means that the offsets to -annotate are relative to the bottom centre of the background.
Finally, I load the emoji-thing and resize just it in parentheses before compositing over the main image. The only interesting thing is that I use -fx to alter the alpha channel (-channel a) and I multiply the existing transparency (u.a) by the fraction of the inverse of the distance we are across the image, namely (1-(i/w)).
Hope that is fairly clear!
Start Images

Related

ImageMagick: Split image into two with 55% of original image each

I want to split some images by percentage using ImageMagick.
To be more precise, I want to split an image into two images. The output-image of the left side should be 55% of the left side of the original image, the output-image of the right side should be 55% of the right ride of the original image.
(In case I am not making myself clear: The point is that there is sometimes important information in the middle of the images and this information would be cut off if we split the images exactly in the middle.)
What I have so far is this: mogrify -crop 55%x100% +repage -path ./cropped *.jpg
So at the moment, I am splitting the image into two and saving it in the "cropped"-folder. BUT, only the left side is 55% of the left side of the original image, the right side is 45% of the right side of the original image.
As this is my first time working with ImageMagick, I would really appreciate your help!
Here is another approach to splitting an image to create two output images that are 55% from the left and 55% from the right of the original...
convert input.png -set filename:0 "%[t]" \
\( +clone -roll +55+0% \) -extent 55x100% "%[filename:0]_%d.png"
That starts by setting a file name variable to use for the output. Then it makes a clone of the input image, and rolls it 55% to the right. The result is moving the rightmost 55% of the image out of the frame to the right and back into the frame on the left. Then after the parentheses a simple -extent operation keeps the leftmost 55% of each image. The output files are named from the input file with a "_0" or "_1" added.
As fmw42 suggested, you can make a loop command to run on multiple inputs, and include the path names in the command for your output files.
I think you would have to run a script loop over each image and use convert rather than mogrify. Mogrify can only output one image for each input image. You do not say what platform/OS or what version of ImageMagick. The following assumes Unix-like system with IM 6 and is one way to do that.
infile="original_file.suffix"
convert "$infile" -set filename:fname "%t" -write mpr:img +delete \
\( mpr:img -gravity west -crop 55x100%+0+0 +repage +write "path_to/cropped/%[filename]_0.suffix" \) \
\( mpr:img -gravity east -crop 55x100%+0+0 +repage +write "path_to/cropped/%[filename]_1.suffix" \) \
null:
Or, you could just run mogrify twice. Once for the left side and once for the right side. Use -gravity to control which as in the convert command.

How to pass Imagemagick montage output and image properties to the convert command without using any temporary files?

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:
resize bgimg.jpg to fit inside the dimensions of fgimg without any cropping;
apply a fade-to-black effect around the edges;
make it the same size as fgimg, with a black background;
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?
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

ImageMagick gradient ratio

How do I adjust the color ratio of a gradient?
I currently use the following to create my gradient.
convert -size 200x600 gradient:none-black output.png
Although at least one acceptable solution has been provided, here are a couple other ideas...
Example 1: This command creates a red-blue gradient of the finished dimensions, crops it into a top and bottom half, resizes them to 40 and 60 percent of the input height, and appends them back to make a single image. What started as the color at the exact vertical center is now at 40% down from the top with clean gradients going up and down from there.
convert -size 200x600 gradient:red-blue -crop 1x2# \
\( -clone 0 -resize 100x40% \) \( -clone 1 -resize 100x60% \) \
-delete 0,1 -append result.png
That splits the gradient image into a top and bottom half, then inside parentheses it resizes each to the required proportion. After that it deletes the 50/50 crops from before the parentheses, appends the two resized remaining images, and writes the output.
Example 2: This next example starts by creating the red-blue gradient in the final dimensions, then sets variables to hold the top color, the exact middle color, and the bottom color.
Then inside the first parentheses it clones and crops the image to 60% its original height. It uses "-sparse-color" to fill that with a gradient from "color1" to "color2".
Inside the second parentheses it clones and crops the image to 40% its original height, and using "-sparse-color" again it fills it with a gradient from "color2" to "color3".
After creating those two gradients, delete the original, append the other two together, and write the output.
convert -size 200x600 gradient:red-blue \
-set option:color1 "%[pixel:p{0,0}]" \
-set option:color2 "%[pixel:p{0,h/2}]" \
-set option:color3 "%[pixel:p{0,h}]" \
\( -clone 0 -extent 100x60% \
-sparse-color barycentric "0,0 %[color1] 0,%[h] %[color2]" \) \
\( -clone 0 -extent 100x40% \
-sparse-color barycentric "0,0 %[color2] 0,%[h] %[color3]" \) \
-delete 0 -append result.png
Maybe you want this, where you get to the half-red/half-blue colour just 20% of the way down the height of the image. It is done by creating two gradients of different lengths and putting them back-to-back:
midcolour="rgb(127,0,127)"
convert -size 100x20 gradient:red-"$midcolour" \
-size 100x80 gradient:"$midcolour"-blue \
-append result.png
Another way is to put 3 single pixels together in a row and then resize that up to what you want. I know you want the middle to be 40% red and 60% blue, but, for ease of viewing, I'll make it lime green:
convert -size 1x1 xc:red xc:lime xc:blue -append -resize 100x100\! result.png
You would change lime to something like "rgb(100,0,155)".
I am not quite sure I understand. But if you want to start with 90% transparent (10% opaque black) and end with black. You can do:
convert -size 200x600 gradient:"graya(0,0.1)-black" output.png
graya means gray with alpha. So graya(0,0.1) is gray(0) or black with 0.1 fraction opacity, so 90% transparent.
Perhaps this is what you want:
Normal 50-50:
convert -size 200x600 gradient:red-blue red_blue1.png
60-40:
rr=`convert xc: -format "%[fx:0.6*255]" info:`
bb=`convert xc: -format "%[fx:0.4*255]" info:`
convert -size 200x600 gradient:"rgb($rr,0,$bb)-rgb(0,0,255)" red_blue2.png
Or perhaps this is what you want:
bb=`convert xc: -format "%[fx:0.1*255]" info:`
convert -size 200x600 gradient:"rgb(255,0,$bb)-rgb(0,0,255)" red_blue3.png
I have an Imagemagick bash shell script called, multigradient, which may do what you want. It allows you to create gradients of many colors each with stops to set where they start. For example:
multigradient -w 200 -h 600 -s "blue 0 red 80" -d to-top result.png
The first color must start at 0, but the direction can be many options. Here I go from bottom to top with pure blue at the bottom and pure red starting at 80 up from the bottom going to the top
(See)

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

Merge two Imagemagick convert commands

I'm trying to Create a thumbnail from GIF 1st command and then merge another image on top of it 2nd command.
1st command:-
convert -thumbnail 398x398 -auto-orient -quality 85 giphy.gif[0] output.jpg
2nd command:- convert -size 1920x1080 xc:none output.jpg -blur 5x4 -composite out1.png -gravity center -composite outfinal.png
I've tried using like this but didn't work out:
convert -thumbnail 398x398 -auto-orient -quality 85 giphy.gif[0] output.jpg | convert -size 398x398 xc:none output.jpg -blur 5x4 -composite out1.png -gravity center -composite outfinal.png
Image extracted from gif:-
2nd image:
Final output as merge two images :
Thank you:)
This is a pretty easy way to do it:
convert -gravity center background.jpg -blur 5x4 \
\( yinyang.png -resize 130x78\! \) \
-composite result.jpg
I put a spurious -resize 130x78\! in there so you can see where to do extra operations that only affect the little "yin yang" picture in the middle - you can obviously remove it.
Maybe I can explain the logic... the first line of the command deals with the background, the second line deals with the overlay. So, first, load up the background picture and make all the edits you want to it, i.e. blurring. Then start some "aside-processing" in parentheses which loads up the "yin yang" and applies some edits to that exclusively without affecting the background image. Then, when you are happy with the overlay, exit the parentheses and take the result of the "aside-processing" and overlay it on top of the background. It overlays into the centre because I set the gravity beforehand.
Hope that helps.
Added in response to your comment... if you want to do the extraction as well, just do that in the "aside-processing":
convert -gravity center \
giphy.gif[0] -thumbnail 398x398 -auto-orient -blur 5x4 \
\( yin yang -resize 300x100\! \) \
-composite result.jpg

Resources