Detect Alpha Channel with ImageMagick
Scenario
I would like to save images with alpha transparency as .png
开发者_Go百科and images without alpha transparency as .jpg
(even if their original format is .png
or .gif
). How can I detect whether or not an image has alpha transparency using ImageMagick?
Check if the image has an alpha channel
Use -format %A
, as per serhiy.h's answer. From the documentation:
%A image transparency channel enabled (true/false)
Check if the image has no transparent or translucent pixels
Use -format %[opaque]
. From the documentation:
%[opaque] CALCULATED: is image fully-opaque?
Example
To demonstrate, lets create an image a.png
that has an alpha channel but is fully opaque, and an image b.png
that is the same except for one translucent pixel:
$ convert rose: PNG32:a.png
$ convert rose: -fill '#0008' -draw "matte 10,10 point" PNG32:b.png
As expected, the %A
escape produces True
for both images, since both have an alpha channel:
$ identify -format '%A' a.png
True
$ identify -format '%A' b.png
True
Whereas the %[opaque]
escape produces true
in one case, and false
in the other due to the one transparent pixel:
$ identify -format '%[opaque]' a.png
true
$ identify -format '%[opaque]' b.png
false
The ImageMagik command:
identify -format '%[channels]' foo.png
will print rgba
or rgb
if there is or is not an alpha channel, respectively.
There could be an alpha channel present with no data in it which wouldn't actually have any transparency, but that is a bit more complicated.
If you want to check that a picture is actually transparent... (not only an alpha channel which might be unused)
Use this command:
convert some_pic.png -verbose info:
(yes, there is a :
at the end of the command)
It is quite verbose. Look for the channels list:
(...)
Channel depth:
red: 16-bit
green: 16-bit
blue: 16-bit
Channel statistics:
(...)
In this example, there are three channels, one for each primary color. But non for alpha. So this image is not transparent.
But you can also get this kind of output:
(...)
Channel depth:
red: 16-bit
green: 16-bit
blue: 16-bit
alpha: 1-bit
Channel statistics:
(...)
Here, there is an alpha channel. However, this does no prove that the image is transparent. It just says that it might be. In the outputs of the command, look for the information about alpha channel:
(...)
Alpha:
min: 255 (1)
max: 255 (1)
mean: 255 (1)
standard deviation: 0 (0)
kurtosis: 0
skewness: 0
(...)
In this example, the alpha says that the picture is opaque: min
= max
= 1 (1 = opaque, 0 = transparent). So even if the image has an alpha channel, the user sees an opaque picture.
You can also get this:
(...)
Alpha:
min: 95 (0.372549)
max: 212 (0.831373)
mean: 111.187 (0.436028)
standard deviation: 19.5635 (0.0767196)
kurtosis: 7.52139
skewness: -2.80445
(...)
This time, min
= 0.372549. This means that some pixels are partly transparent. mean
is also low. It seems that a large part of the image uses transparency.
Depending of the type of check you want to achieve (full opacity, "almost opaque", etc.), you should check min
, mean
and maybe standard deviation
if your request is a bit tricky.
Note: you might be tempted to check integer values for min
, mean
and others, as I did in the first place. After all, it is easier to deal with 95
than 0.372549
. If you choose this route, beware the alpha channel depth. If it is 8 bits, then 255 is the maximum and means "opaque". If it is 16 bits, the maximum is now 65535 and 255 means "almost transparent". Better check the floats in parenthesis, which always range from 0 to 1.
If you suspect that a lot of pictures you will process have no alpha channel at all, it might be useful to first run:
identify -format '%[channels]' some_pic.png
If it dumps:
rgba
there is an alpha channel (the a
in the output) and convert
should be used to check min
, etc.. But if there isn't, there is no need to run convert
. Although I didn't benchmarked these two commands, identify
should be much faster than convert
.
identify -format %A some_pic.png
Will return True
if image contains alpha channel.
If you want to be sure that alpha transparency is really used, the only solution I see it to iterate all pixels and get the color info to check if it's transparent or not. For large images this will be very slow, so, as an optimization you can first make a thumbnail of the image (let's say 20x20) and then check the thumbnail pixels. This was a good solution for me.
Here's a couple of bash functions to check if a png has transparency or not, using ImageMagick.
Fast check, which just checks for the presence of an alpha channel:
checkAlphaFast() {
test $(identify -format %A "$1") == True
}
Slow check, verifies the pixels themselves (even if alpha channel is present):
checkAlphaSlow() {
test $(identify -format '%[opaque]' "$1") == false
}
You should only consider using the fast check if you're doing this a whole lot, because it's pretty fast either way. Thanks to the other people who answered, from which I made these functions :)
As a bonus: I did not make any benchmarking, but this may be almost as fast as the fast mode and as accurate as the slow mode:
checkAlpha() {
checkAlphaFast "$1" && checkAlphaSlow "$1"
}
Use these functions like this:
if checkAlpha "$myPNG"; then
# is transparent
else
# is opaque
fi
And this will give you the opaqueness of the image, where 0 is fully transparent and 1 is fully opaque:
checkOpaqueness() {
if checkAlphaFast "$1"; then
convert "$1" -verbose info: | grep -A3 Alpha: | tail +4 | head -n1 | sed -r 's/.*\(|\)//g'
else
echo 1
fi
}
And this one outputs as a rounded percentage, because integers are so much more natural for bash:
checkPercentage() {
printf %3.2f $(checkOpaqueness "$1") | tr -d . | sed -r 's/^0?0?//'
}
精彩评论