Imagemagick convert resize then crop - resize

I have over 1000 images on different resolutions, (for example 1234x2122, 4400x5212 , etc) and I want to convert all of them to fixed 100x100 size, so.
first I need to resize the images keeping proportions, and get 100xA or Ax100, where A > 100 (it depends width and height of image, for some images width > height, and for some images height > width).
Crop this image to 100x100 from center
Is there a simple convert command, that I can use for all my images?

You would use the area-fill (^) geometry modifier on the -resize operation to unify the down-scale. For cropping the center, -extent with -gravity Center will work.
convert input.jpg -resize 100x100^ \
-gravity Center \
-extent 100x100 \
output.jpg
Update
As Mark Setchell pointed out in the comments, the mogrify utility can be leveraged to batch convert items.
mogrify -path ./path/to/write/results/ \
-resize 100x100^ \
-gravity Center \
-extent 100x100 \
./path/to/source/files/*
Reminder: Mogrify will overwrite original file with resulting image(s), unless you set the -path parameter.

To keep the aspect ratio and don't fill anything (extent), rather crop from the longer edge which is out of aspect you could do
convert input.jpg -resize 100x100^ \
-gravity Center \
-crop 100x100+0+0 +repage \
output.jpg
with option to crop more from one side if you like

Related

ImageMagick convert to png8 (paletted PNG with binary alpha) using background color for partially transparent pixels

I have a 32-bit PNG image like this:
Displayed here with a checkerboard background for visibility, but instead of the checkerboard the image is actually transparent (for your reference: the original 32-bit image). As you can see, around the edges and towards the right, the red pixels are gradually fading out from opaque to transparent.
If I would display this 32-bit image on top of a blue background, the pixels around the edges and towards the right would gradually fade from red to blue.
Now I need to convert this image to an 8-bit PNG with binary alpha. I want the fully transparent area to remain fully transparent, but the partially transparent pixels to gradually blend with the intended background/matte color, in this example blue.
So something like this:
However, I can't seem to figure out how to do this with ImageMagick (using ImageMagick 7.0.8-26 on Mac). I tried as follows:
magick original.png -background 'rgba(0,0,255,0)' png8:output.png
But this results in:
It seems to ignore the blue background color altogether, and just convert the transparency to binary (probably taking fully opaque for ≥128 or fully transparent for <128).
Also if I specifiy rgb(0,0,255) as background color (i.e. no alpha) the result is the same.
Does ImageMagick have some smart 'background blending for partially transparent pixels' option that I don't know about?
Otherwise, I'm guessing I should somehow extract a binary bitmap where the original image is fully transparent (i.e. has alpha value or opacity 0), then flatten the image on a background of my choosing, and then re-apply the boolean alpha bitmap again. But not quite sure how to do this.
I can think of two techniques, but I'm sure folks have better answers.
First Option
Composite the blue background, and then copy the alpha channel from the original source.
magick original.png \( \
+clone \( +clone -fill BLUE -draw 'color 0,0 reset' \) \
-compose DstOver -composite \) \
-swap 0,1 -compose CopyAlpha -composite PNG8:output.png
Second Option
Replace transparent with blue color, but then call -transparent without any -fuzz operation.
magick original.png \( \
+clone -fill BLUE -draw 'color 0,0 reset' \) \
-compose DstOver -composite -transparent BLUE \
PNG8:output.png
Again, I'm sure the above examples can be improved on. YMMV
One way to do that with ImageMagick is to extract the alpha channel, colorize essentially everything except the totally transparent pixels, and composite the original image over the result.
convert input.png \( +clone -alpha extract -transparent black -fill blue \
-channel RGB -colorize 100 +channel \) +insert -composite output.png
That reads the input image, clones it inside the parentheses, and extracts the alpha channel to a black and white mask.
The pure black is made transparent with "-transparent black".
The white, everything except pure black, is changed to blue with "-colorize 100". Setting "-channel RGB" applies that fill color to only the white part of the mask and ignores the transparent.
Then after the parentheses the "+insert" moves that blue-on-transparent image to the back, and "-composite" places the input image over that blue and transparent one.
If you're using ImageMagick version 7, use "magick" instead of "convert". If you're running on Windows, change the end of line backslash "\" to a caret "^", and remove the backslashes that escape the parentheses "\(...\)" to simply "(...)".
Here is one more way in ImageMagick.
convert img.png \
\( -clone 0 -alpha extract -channel rgba \
\( xc:none xc:blue +append \) -clut +channel \
-channel alpha -threshold 0 +channel \) \
+swap -compose over -composite PNG8:result.png
OR FOR Imagemgick 7
magick img.png \
\( -clone 0 -alpha extract -alpha copy -channel rgba \
\( xc:none xc:blue +append \) -clut \
-channel alpha -threshold 0 +channel \) \
+swap -compose over -composite PNG8:result.png
Read the image.
Clone it, extract the alpha channel, create transparent blue color map and apply to the alpha channel, then threshold the alpha channel.
Swap the two images and composite the original over the processed alpha channel.
Save the output.
Thanks to the posted suggestions, however the problem is I don't want to mask any particular color (like black or blue) as transparent. That exact same color may also appear in the non-transparent parts.
After some tweaking, I got the exact result I need:
magick original.png -write MPR:foo -background 'rgb(0,0,255)' -flatten \( MPR:foo -alpha extract -threshold 0 \) -compose copy_opacity -composite png8:result.png
Explanation:
I start with original.png and use -write MPR:foo to save a temporary copy in memory called 'foo'
Then I flatten the image on a blue background, giving the correct color values everywhere (including blended with the background for partially transparent pixels) but losing the opacity data.
Then I side-process a 2nd layer, opening the same original input image (restoring from memory using MPR:foo) and extract its alpha channel which now becomes a grayscale image, containing the original alpha data.
I apply -threshold 0 on that layer, which changes all channel values that were >0 to the maximum (i.e. alpha 255 or fully opaque) and ≤0 remains 0 (fully transparent). So this is now my 'binary alpha' channel.
Then finally I merge these two layers by using a copy_opacity composite which just copies the 2nd layer (the binary alpha) and writes that as the resulting layer's alpha data.
Saving the end result gives me the exact image I need.

Convert PDF/Images to a flip based effect in GIF

I want to convert a PDF or sequence-of-images to a flip based effect in GIF (similar to the one below).
Is there any softwares available handy I could use to produce this output? or do I have to write scripts using imageMagicK? please suggest?
Thanks in advance!
Cool project! This isn't production-ready, military-hardened, bullet-proof code, but the following will do most of the heavy lifting as regards getting set up, appending the individual pages, distorting pages as they turn and finally putting the whole lot together in an animated GIF sequence.
#!/bin/bash
################################################################################
# flipbook
# Mark Setchell
#
# Give me 4 pages as parameters and I create an animated GIF book out of them
# called book.gif.
#
# Requires ImageMagick
################################################################################
# Names of the 4 pages
p0=${1:-page0.gif} # Use first arg, or "page-0.gif" if none given
p1=${2:-page1.gif}
p2=${3:-page2.gif}
p3=${4:-page3.gif}
# Get width and height of images - I assume, but do not check all are identical sizes
read w h < <(convert "$p0" -format "%w %h" info: )
((twow=w+w))
# Layout first and last flat double-page spreads
convert "$p0" "$p1" +append frame0.png
convert "$p2" "$p3" +append frame4.png
# Make right page taller and thinner and save as "distorted.png"
((deltah=20*h/100))
((deltaw=20*w/100))
((hplusdeltah=h+deltah))
((wminusdeltaw=w-deltaw))
((hplus2deltah=h+deltah+deltah))
points="0,0 0,$deltah $wminusdeltaw,0 $wminusdeltaw,0 $wminusdeltaw,$hplus2deltah $wminusdeltaw,$hplus2deltah 0,$hplus2deltah 0,$hplusdeltah"
convert "$p1" +matte -virtual-pixel transparent \
-resize ${wminusdeltaw}x${hplus2deltah}! +repage \
-distort Perspective "$points" +repage distorted.png
# Make second frame by overlaying distorted right page ontop of pages 0 and 3
convert "$p0" "$p3" +append \
-bordercolor white -border 0x$deltah \
+repage \
distorted.png \
-geometry +${w}x \
-composite frame1.png
# Make left page taller and thinner and save as "distorted.png"
((deltaw=70*w/100))
((wminusdeltaw=w-deltaw))
points="0,0 0,0 $wminusdeltaw,0 $wminusdeltaw,$deltah $wminusdeltaw,$hplus2deltah $wminusdeltaw,$hplusdeltah 0,$hplus2deltah 0,$hplus2deltah"
convert "$p2" +matte -virtual-pixel transparent \
-resize ${wminusdeltaw}x${hplus2deltah}! +repage \
-distort Perspective "$points" +repage distorted.png
# Make third frame by overlaying distorted left page ontop of pages 0 and 3
convert "$p0" "$p3" +append \
-bordercolor white -border 0x$deltah \
+repage \
distorted.png \
-geometry +${deltaw}x \
-composite frame2.png
# Make left page taller and thinner and save as "distorted.png"
((deltaw=20*w/100))
((wminusdeltaw=w-deltaw))
points="0,0 0,0 $wminusdeltaw,0 $wminusdeltaw,$deltah $wminusdeltaw,$hplus2deltah $wminusdeltaw,$hplusdeltah 0,$hplus2deltah 0,$hplus2deltah"
convert "$p2" +matte -virtual-pixel transparent \
-resize ${wminusdeltaw}x${hplus2deltah}! +repage \
-distort Perspective "$points" +repage distorted.png
# Make fourth frame by overlaying distorted right page ontop of pages 0 and 3
convert "$p0" "$p3" +append \
-bordercolor white -border 0x$deltah \
+repage \
distorted.png \
-geometry +${deltaw}x \
-composite frame3.png
# Make final animation from frame0.png...frame4.png
convert -gravity center -delay 100 frame*.png -background white -extent ${twow}x${hplus2deltah} book.gif
So, if you start with the following as page0.gif, page1.gif, page2.gif and page3.gif...
You will get this as book.gif
If your book has more than 4 pages, you can do four at a time and then append the animations quite simply.
Updated Answer
It seems you are unfortunate enough to have to use Windows - which is very cumbersome in BATCH. I am no expert, but can get around in BATCH a little. I think the script above is pretty easy to translate though. I'll get you started but you will need to do some yourself - you can always ask a new question if you get stuck - questions are free!
The first part of the script just picks up the parameters supplied on the command line, so it'll look like this:
REM Pick up commandline parameters
set p0=%1
set p1=%2
set p2=%3
set p3=%4
Then we need to work out the width and height of the input images, something like this:
REM Get width and height of images in variable "w" and "h"
FOR /F %%A IN ('identify -format "w=%%w\nh=%%h" %p0%') DO set %%A
All the stuff in my original script inside ((..)) is just simple maths which can be done in BATCH using SET /A, so the lines that look like this:
((twow=w+w))
((deltah=20*h/100))
will look like this:
SET /A TWOW=w+w
SET /A DELTAH=20*h/100
The rest is just convert commands - you will need to do a couple of things there:
Replace line continuations at ends of lines, so change \ to ^
Where I use $variable or ${variable}, replace it with %variable%
Double any % signs I have, so % becomes %%
Change \( to ^( - I think
change any single quotes ' to double quotes "
Best to just work through it and see what happens as you convert each line and ask another question if you can't work it out.
There is some good info at these places - ss64 - general, ss64 - set command on BATCH in general. Also, an English guy called Alan Gibson, uses IM with Windows very competently and you can see his scripts here, and also more generally here for inspiration on how to be effective with IM under Windows.

ImageMagick copy the iOS 7 blur effect

I have an image that I want to blur like on the iOS 7, see image below:
I'm not sure what combination of transformations I need to execute to get the same result. I tried something very basic so far (not sure what I'm doing), but the result isn't here:
convert {$filename} -filter Gaussian -define filter:sigma=2.5 \
-blur 0x40 {$newFilename}
The above code gets executed by php exec function.
If I take this as background.png
and a plain grey rgb(200,200,200) image with a couple of black and white bits and pieces on it, as foreground.png since I don't have any iPhone grabs of the slide-up menu thingy
convert background.png \
\( +clone -gravity south -crop 360x450+0+0 \
-filter Gaussian -define filter:sigma-2.5 -blur 0x40 \) \
-composite \
\( foreground.png -matte -channel a -fx "(u.r<0.1||u.r>0.9)?1:0.3" \) \
-composite result.png
So, I basically clone the background, and select the bottom part with the -crop and blur it, then composite it onto the real background. Then I take the foregound and anywhere it is not black or white, I set it to 30% transparent (so as not to fade the black and white aspects). Then I composite that ontop of the background which by now already has the lower part blurred.
It's probably not 100% but you can diddle around with the numbers and techniques till you achieve Apple-y perfection :-)

Is it possible to distinguish grayscale from (scanned) monochrome within a shell script?

I have several thousand images that I want to run various IM commands on depending on which of three categories they fall into:
Color (often with bright colors)
Grayscale (scanned from paper, the "white" often has a yellowish tinge)
Monochrome (scanned, with the yellowish tinge, as above)
Can this be sorted out from a shell script?
Color Example #1
Grayscale Example #1
Monochrome Examples #1 and #2
I would say that the Hue and Saturation would be good discriminants for the colour image especially. A mono or grayscale image is very unsaturated, so its mean saturation will tend to be low whereas it will be higher for a colour image. Also, the hue (basically colour) of a colour image will tend to vary a lot between the different colours whereas the hue will tend to be a fairly constant value for a grey or mono image, so the amount of variation in the Hue should be a good measure - i.e. its standard deviation.
We can calculate the mean saturation using ImageMagick like this:
convert image.png -colorspace HSL -channel S -separate -format "%[mean]" info:
and the standard deviation of the Hue like this:
convert image.png -colorspace HSL -channel H -separate -format "%[standard-deviation]" info:
So, if we put all that together in a bash script and run it over your images we get this:
#!/bin/bash
for i in colour.png grey.png mono.png; do
SatMean=$(convert $i -colorspace HSL -channel S -separate -format "%[mean]" info:)
HueStdDev=$(convert $i -colorspace HSL -channel H -separate -format "%[standard-deviation]" info:)
echo $i: Mean saturation: $SatMean, Hue Std-Dev: $HueStdDev
done
Output
colour.png: Mean saturation: 17,807.9, Hue Std-Dev: 16,308.3
grey.png: Mean saturation: 7,019.67, Hue Std-Dev: 2,649.01
mono.png: Mean saturation: 14,606.1, Hue Std-Dev: 1,097.36
And it seems to differentiate quite well - I have added the thousands separator for clarity. The range of the values is based on your IM Quantisation level - mine is Q16 so the range is 0-65535.
Differentiating the mono from the grey is harder. Essentially, in the mono image you have a more starkly bi-modal histogram, and in the grey image, you have a more continuous histogram. We can plot the histograms like this:
convert colour.png histogram:colorhist.png
convert grey.png histogram:greyhist.png
convert mono.png histogram:monohist.png
Updated
To differentiate between the greyscale and mono, I want to look at the pixels in the middle of the histogram, basically ignoring blacks (and near blacks) and whites (and near whites). So I can do this to set all blacks and near blacks and whites and near whites to fully black:
convert image.png \
-colorspace gray \
-contrast-stretch 1% \
-black-threshold 20% \
-white-threshold 80% -fill black -opaque white \
out.png
If I now clone that image and set all the pixels in the clone to black, I can then calculate the difference between the histogram-chopped image and the black one
convert image.png \
-colorspace gray \
-contrast-stretch 1% \
-black-threshold 20% \
-white-threshold 80% -fill black -opaque white \
\( +clone -evaluate set 0 \) \
-metric ae -compare -format "%[distortion]" info:
Now, if I calculate the total number of pixels in the image, I can derive the percentage of pixels that are in the midtones and use this as a measure of whether the image is very grey or lacking in midtones.
#!/bin/bash
for i in colour.png grey.png mono.png; do
SatMean=$(convert $i -colorspace HSL -channel S -separate -format "%[mean]" info:)
HueStdDev=$(convert $i -colorspace HSL -channel H -separate -format "%[standard-deviation]" info:)
NumMidTones=$(convert $i -colorspace gray -contrast-stretch 1% -black-threshold 20% -white-threshold 80% -fill black -opaque white \( +clone -evaluate set 0 \) -metric ae -compare -format "%[distortion]" info:)
NumPixels=$(convert $i -ping -format "%[fx:w*h]" info:)
PctMidTones=$((NumMidTones*100/NumPixels))
echo $i: Mean saturation: $SatMean, Hue Std-Dev: $HueStdDev, PercentMidTones: $PctMidTones
done
Output
colour.png: Mean saturation: 17807.9, Hue Std-Dev: 16308.3, PercentMidTones: 70
grey.png: Mean saturation: 7019.67, Hue Std-Dev: 2649.01, PercentMidTones: 39
mono.png: Mean saturation: 14606.1, Hue Std-Dev: 1097.36, PercentMidTones: 27
First of all: Your question's headline is misleading.
"Is it possible to distinguish grayscale from (scanned) monochrome within a shell script?"
Straightforward identify tells color space and bit depth
It is misleading, because all the example images you provide are in fact in 8-bit sRGB colorspace:
identify http://i.stack.imgur.com/lygAE.png \
http://i.stack.imgur.com/H7vBP.png \
http://i.stack.imgur.com/ZOCTK.png
http://i.stack.imgur.com/lygAE.png=>lygAE.png PNG 236x216 236x216+0+0 8-bit sRGB 127KB 0.000u 0:00.000
http://i.stack.imgur.com/H7vBP.png=>H7vBP.png[1] PNG 259x192 259x192+0+0 8-bit sRGB 86.2KB 0.000u 0:00.000
http://i.stack.imgur.com/ZOCTK.png=>ZOCTK.png[2] PNG 264x179 264x179+0+0 8-bit sRGB 86.7KB 0.000u 0:00.000
As you can see, the identify command (part of the ImageMagick suite of commands) can tell you the depth and color space of an image easily.
identify with -format parameter tells specific image properties
You can include the -format parameter with 'percent escapes' in order to get to specific properties only of the image:
f : for image file name
d : for directory component of image
z : for image depth
r : for image class and color space
So try this:
identify -format "%f %d : %z %r\n" \
http://i.stack.imgur.com/lygAE.png \
http://i.stack.imgur.com/H7vBP.png \
http://i.stack.imgur.com/ZOCTK.png
Result:
lygAE.png //i.stack.imgur.com : 8 DirectClass sRGB
H7vBP.png //i.stack.imgur.com : 8 DirectClass sRGB
ZOCTK.png //i.stack.imgur.com : 8 DirectClass sRGB
Convert one image to real monochrome
Now to show you how a real "monochrome" image looks like, let's convert one of your samples accordingly:
convert \
-colorspace gray \
http://i.stack.imgur.com/lygAE.png \
+dither \
-colors 2 \
-depth 1 \
bmp3:monochrome.bmp
and
identify -format "%f : %z %r\n" monochrome.bmp http://i.stack.imgur.com/lygAE.png
monochrome.bmp : 1 PseudoClass Gray
lygAE.png : 8 DirectClass sRGB
Here are the respective images:
Telling the number of unique colors
If you have (as you do) all your images in sRGB color space with 8-bit depth, then in theory, each image can have as many as 16.777.216 (16 million) colors (also called "TrueColor"). However, most actual images do not use the full scope of this spectrum, and the "gray-ish" appearing images will actually use an even smaller number of them.
So ImageMagick has two other 'percent escape' to return information about images:
%k : returns the number of unique colors within an image. This is a calculated value. IM has to process the image and analyse every single pixel of it to arrive at this number.
So here is a command:
identify -format "%f - number of unique colors: %k\n" \
http://i.stack.imgur.com/lygAE.png \
http://i.stack.imgur.com/H7vBP.png \
http://i.stack.imgur.com/ZOCTK.png
Results:
lygAE.png - number of unique colors: 47583
H7vBP.png - number of unique colors: 7987
ZOCTK.png - number of unique colors: 5208
As you can see, your image with obvious coloring uses about 6 times as many uniq colors than the "gray-ish" scans do.
However, this is not necessarily so. See for instance this image:
It is color, isn't it?
I generated it with this command:
convert -size 100x100 \
xc:red \
xc:green \
xc:blue \
xc:white \
xc:black \
xc:cyan \
xc:magenta \
xc:yellow \
+append \
out.png
You can even count the number of unique colors by simply looking at it: 8.
Now what does identify tell us about it?
identify \
-format "%f:\n \
-- number of unique colors: %k\n \
-- depth: %z\n \
-- class/space: %r\n \
-- image type: %[type]\n" \
out.png
Result:
out.png:
-- number of unique colors: 8
-- depth: 8
-- class/space: PseudoClass sRGB
-- image type: Palette
So a low number of unique colors does not necessarily proof that the image is "gray-ish"!
You'lll have to play with this parameters a bit and see if you can come up with a combination that helps you to correctly classify your real-world "thousands of images".
Consider image statistics too
More values you could look at with the help of identify -format %... filename.suffix:
%[gamma] : value of image gamma
%[entropy] : CALCULATED: entropy of image
%[kurtosis] : CALCULATED: kurtosis value statistic of image
%[max] : CALCULATED: maximum value statistic of image
%[mean] : CALCULATED: mean value statistic of image
%[min] : CALCULATED: minimum value statistic of image
%[profile:icc] : ICC profile info
%[profile:icm] : ICM profile info
Last hint: look at the metadata!
Just in case your images were scanned by a device that leaves its own identifying meta data behind: check for them!
The command line tool exiftool is a good utility to do so.

How can I make a montage with ImageMagick from images with different size and aspect ratio?

I have a lot of images, different sizes and aspect ratios. Is it possible to make a montage of them? I mean to optimally arrange them in rows after I set a common height for the images which build up a common row. Images aspect ratios aren't allowed to modify of course, and none of the images are allowed to omit from the final montage nor duplication.
The height of picture rows in the montage usually aren't equal, but their values should be kept in a minimal range (in statistical sense) or in other words: standard deviation from the mean value of the row heights must be minimized.
Desired width and height of the montage are given (a.)
Or the width and an allowed ratio range (or equivalently height range) is given (b.), for example width must be 1024 pixel, height must be so that w/h < 0.9 and w/h > 0.8
1.) Images must be packed in the montage in their initial fixed order. In this case one must find the images after what a new image row should be started in the montage (easy).
2.) Order of images is allowed to be altered. In this case one must find a permutation which lead to a minimalization of the standard deviation of the final row heights when each image is packed into the montage (hard).
For example:
I'm not sure I understand your question correctly.
Here is what I make of it. Assuming you have 8 different images of different sizes. For demo purposes, I'll let ImageMagick create these as 8 different color patches:
convert -size 90x90 xc:yellow y.png
convert -size 120x120 xc:red r.png
convert -size 60x210 xc:green g.png
convert -size 150x180 xc:blue b.png
convert -size 30x60 xc:cyan c.png
convert -size 150x90 xc:magenta m.png
convert -size 90x120 xc:gray Gr.png
convert -size 120x90 xc:black K.png
You can montage these patches in many different ways:
convert \( y.png r.png g.png b.png +append \) \
\( c.png m.png Gr.png K.png +append \) \
-append \
-mattecolor lightblue \
-frame 1x1 \
montage0.png
This command does not scale the different patches. It places them in 2 rows a 4 patches and montages them in their original sizes. The white spaces is where the patches do not "fit":
convert \( y.png r.png g.png b.png -resize x60 +append \) \
\( c.png m.png Gr.png K.png -resize x60 +append \) \
-append \
-mattecolor lightblue \
-frame 1x1 \
montage1.png
This command scales all different patches to a common height of 60 pixels (preserving their respective aspect ratios) and places them in 2 rows a 4 patches:
convert \( y.png r.png g.png b.png -resize 60x +append \) \
\( c.png m.png Gr.png K.png -resize 60x +append \) \
-append \
-mattecolor lightblue \
-frame 1x1 \
montage2.png
This command scales all different patches to a common width of 60 pixels (preserving their respective aspect ratios) and places them in 2 rows a 4 patches:
convert \( y.png r.png g.png b.png -resize 60x80\! +append \) \
\( c.png m.png Gr.png K.png -resize 60x80\! +append \) \
-append \
-mattecolor lightblue \
-frame 1x1 \
montage3.png
This command scales all different patches to dimensions of 60x80 pixels (overriding their original aspect ratios) and places them in 2 rows a 4 patches: