Image magick to put images in row and then generate reflection - imagemagick

I was wondering if I can use imagemagick to produce a result image like this
My initial image will be like this
I am able to create the dark image to be put in the background like this -
convert -brightness-contrast -20x10 GARDENS-ILLUSTRATED_JAN-14.jpg out_lighter2.jpg
But I do not know how to put them in line like that and then generate a reflection.

I have got something close, but have no more time, so maybe you can fiddle with it and get it to what you want:
#!/bin/bash
input=input.png
# Calculate width and height
w=$(convert $input -ping -format "%w" info:)
h=$(convert $input -ping -format "%h" info:)
# Calculate how much to chop off bottom and height of reflection
chop=$(echo $h*60/100|bc)
refl=$(echo $h*20/100|bc)
convert $input -alpha on \
\( +clone -flip -size ${w}x${refl} gradient:gray40-black -alpha off -compose CopyOpacity -composite \) \
-append -background white -compose Over -flatten -gravity South -chop 0x${chop} output1.png
# Darken and reduce for second layer
convert output1.png -brightness-contrast -25x8 -resize 90% output2.png
# Darken and reduce for third layer
convert output2.png -brightness-contrast -25x8 -resize 90% output3.png
# Make big new output canvas
neww=$(echo $w*1.6|bc)
newh=$(echo $h*1.6|bc)
# Splat images onto it with offsets
convert -size ${neww}x${newh} xc:transparent -page +0+80 output3.png \
-page +40+40 output2.png \
-page +80+0 output1.png -flatten output.png
My basic idea is to generate the bright, frontmost image first in output1.png, then darken and reduce it into output2.png and again into output3.png. Then composite them all together into output.png with a little offset.

Related

ImageMagick: Remove 2 pixels wide lines

How can I remove two pixels wide lines (like the three lines in the image below) without altering the rest of the image, and repage it to its new minimum border ?
What you want to do is extract the alpha channel. Then use morphology close. Then put the result back into the alpha channel, then trim and save the result. In Imagemagick, that would be:
Input:
convert image.png \
\( -clone 0 -alpha extract -morphology open octagon:2 \) \
-alpha off -compose copy_opacity -composite \
-trim +repage \
result.png
Something like this maybe:
convert captcha.png -morphology erode disk:2 -trim +repage result.png

How to scale and crop a folder of images?

I've been looking at the documentation for imagemagick and looking at examples of scripts others have made, but I haven't been able to get this working.
The goal is to have imagemagick scale, crop, and save (appending the dimensions to the file names) multiple resized images of various aspect ratios.
For example, a folder containing Image1.png and Image2.png would result in:
Image1_1571x2646.png, Image1_1350x2150.png, Image1_1281x2039.png
Image2_1571x2646.png, Image2_1350x2150.png, Image2_1281x2039.png
Visual aid:
The animation above shows the simplest examples: a 1:1 square, a vertical rectangle, and a horizontal rectangle.
The images should scale to fit the longest dimension of the rectangle, and then crop any leftover pixels. The scaling and cropping should be done relative to the image centers.
Here's what I have so far (using macOS Terminal) but it doesn't work:
convert *.png -path /Users/user/Resized \
\( +clone -resize "1571x2646^” -gravity center -crop 1571x2646+0+0 +repage resultimage -write 1571x2646.png +delete \) \
\( +clone -resize "1350x2150^” -gravity center -crop 1350x2150+0+0 +repage resultimage -write 1350x2150.png +delete \) \
-resize "1281x2039^” -gravity center -crop 1281x2039+0+0 +repage resultimage 1281x2039.png
I'm not sure if I should use mogrify or convert, but if I use mogrify clone gives an error. I'm also not sure if multi-line instructions need to be put into a .sh file or something. The ^ denotes the dimension that should take priority (the larger one). I believe -gravity center is supposed to keep scaling and cropping relative to the image centers.
With Imagemagick, you must use convert. Mogrify cannot handle the parenthesis process and clones, nor can it write multiple outputs for a given input. The ^ is the correct way and -gravity center is correct. You will have to loop over each input image. I do not think you can use wild cards to process more than one image at a time with this type of command. I think -path is only for mogrify.
I would write a loop over each of your input images (bash unix syntax):
cd
cd /Users/user/Resized/
list=`ls`
for img in $list; do
name=`convert "$img" -format "%t" info:`
convert "$img" \
\( -clone 0 -resize "1571x2646^" -gravity center -crop 1571x2646+0+0 +repage +write ${name}_1571x2646.png +delete \) \
\( -clone 0 -resize "1350x2150^" -gravity center -crop 1350x2150+0+0 +repage +write ${name}_1350x2150.png +delete \) \
\( -clone 0 -resize "1281x2039^" -gravity center -crop 1281x2039+0+0 +repage +write ${name}_1281x2039.png +delete \) \
null:
done
The above assumes that your input images have no spaces in their names.
I have changed from +clone to -clone 0, since I am not sure if you change aspect ratio from output to output whether that will cause problems. You can try both ways and see which looks best.

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

Convert multiple Imagemagick commands into 1 command (trim, resize, square)

Currently, I am using multiple imagemagick commands to trim, resize (if width or height > 5000) and square. Is it possible to combine into 1 single command?
step 1: convert input_file.tif -fuzz 1% -trim output_file_trim.tif
step 2: get new image width and height using identify command from output_file_trim.tif
step 3: get max dimension from image width and height
step 4: if max dimension > 5000 then
convert output_file_trim.tif -resize 5000x5000 output_file_trim.tif
Step 5: Finally, finish the image conversion
convert output_file_trim.tif -flatten -gravity center -background white -extent "$max_dimension"x"$max_dimension" -format jpg output_file_final.jpg
#fmw42. Is the following single command correct to achieve this requirement:
convert `input_file.tif` -fuzz 1% -trim +repage \( +clone -rotate 90 +clone -mosaic +level-colors white \) +swap -flatten -gravity center -extent 105x105% -composite -format jpg `output_file_final.jpg`
This command will read the input image and trim it. Then it resizes it to fit in a 5000x5000 box if it's larger than 5000x5000. Then it re-dimensions the canvas to a square with both dimensions being the larger of the width or height. It finishes by placing the image in the center of that square canvas with a white background.
convert input_file.tif -fuzz 1% -trim +repage -resize "5000x5000>" \
-set option:distort:viewport "%[fx:max(w,h)]x%[fx:max(w,h)]" -virtual-pixel white \
-distort affine "0,0 %[fx:h>w?(h-w)/2:0],%[fx:w>h?(w-h)/2:0]" \
output_file_final.jpg
Putting your 5 steps into one command can only be done in IM 7 as follows (unix syntax):
magick -quiet input_file.tif -fuzz 1% -trim +repage \
-resize "5000>" \
-flatten -gravity center -background white \
-extent "%[fx:max(w,h)>5000?5000:max(w,h)]x%[fx:max(w,h)>5000?5000:max(w,h)]" \
output_file_final.jpg
In IM 6, you need to do it in two command. First find the larger of the max(w,h) and 500 as dim and save a temp image from your step one. Then do another command to finish it using that dim
dim=$(convert -quiet input_file.tif -fuzz 1% -trim +repage \
+write output_file_final.jpg -format "%[fx:max(w,h)>5000?5000:max(w,h)]" info:)
convert output_file_final.jpg -resize "5000>" \
-flatten -gravity center -background white \
-extent ${dim}x${dim} output_file_final.jpg
I do not understand your last command. It does not relate to the steps you outlined.

How to find out the average color (rgb) of masked area of an Image

I would like to get the average color in rgb of the mask area of a png image.
the following just outputs the average color of the whole image
convert demo.png -mask demo_mask.png -resize 1x1 txt:-
In ImageMagick, -scale 1x1! will ignore the transparent pixels and give you the average of the opaque pixels. So if you want the average of the masked region, you can put the mask into the alpha channel and then use -scale 1x1! to average it down to one pixel. The mask should be white where you want to get the average and black where it should be transparent and ignore those pixels in the average. So this should do it.
convert image.png mask.png -alpha off -compose copy_opacity -composite -scale 1x1! -alpha off -format "%[pixel:u.p{0,0}]" info:
For example if I make the logo: image transparent where it is white and then get the average, I get
convert logo: -transparent white -scale 1x1! -alpha off -format "%[pixel:u.p{0,0}]" info:
srgb(100,81,99)
You can show that this works, by doing it the long way. Multiply the mask by the image, then get the average of each channel of the product. Then get the average of the mask. Then compute the ratios scaled to the range 0 to 255.
convert logo: -transparent white logot.png
convert logot.png -alpha extract mask.png
declare `convert \( logot.png -alpha off \) mask.png -compose multiply -composite -format "IR=%[fx:mean.r]\nIG=%[fx:mean.g]\nIB=%[fx:mean.b]\n" info:`
echo "IR=$IR; IG=$IG; IB=$IB"
IR=0.0651798; IG=0.0529989; IB=0.0641607
MM=`convert mask.png -format "%[fx:mean]\n" info:`
echo "MM=$MM"
MM=0.165872
convert xc: -format "srgb(%[fx:round(255*$IR/$MM)],%[fx:round(255*$IG/$MM)],%[fx:round(255*$IB/$MM)])\n" info:
srgb(100,81,99)
Result is the same as above.
ASIDE: Note that
convert \( logot.png -alpha off \) mask.png -compose multiply -composite ...
in this case is the same as just
convert logot.png -alpha remove ...
But I show it the long way, if the user has a separate mask and image with no transparency.
You should be able to get very nearly what you want with a command something like this...
convert image.png mask.png -compose copyopacity -composite -resize 1x1! txt:-
To output only the color information you can try something like this...
convert image.png mask.png \
-compose copyopacity -composite -resize 1x1! -format "%[pixel:p]" info:
I haven't tried it, but you may have to "-negate" your mask image depending on which version of IM you're using because there have been changes in the way the alpha channel is handled.
You'll get a very slightly different result if you "-trim" before the "-resize".
If you don't want to output the alpha channel information, you can add "-alpha off" after the "-resize".
Perhaps...
convert demo.png -mask demo_mask.png -trim -fx mean -extent 1x1 txt:- |\
tail -1 | cut -d ' ' -f 4
This would work because -trim will reduce the masked image down to the MBR of the ROI. Fx operator -fx mean will convert all pixels into the overall average. And finally the -extent 1x1 will isolate the first pixel in the image. The rest is basic unix utilities.
Another option with better performance...
MEAN=$(convert demo.png -mask demo_mask.png -trim -format '%[fx:mean]' info:-)
convert null: -depth 8 -format "%[pixel:$MEAN]" info:-
Or from the Quantization documentation...
convert demo.png -mask demo_mask.png -trim -scale 1x1\! '%[pixel:s]' info:-

Resources