sami_analyst
sami_analyst

Reputation: 1839

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:-

Upvotes: 1

Views: 981

Answers (3)

fmw42
fmw42

Reputation: 53164

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.

Upvotes: 2

GeeMack
GeeMack

Reputation: 5395

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".

Upvotes: 2

emcconville
emcconville

Reputation: 24439

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:-

Upvotes: 1

Related Questions