John Smith
John Smith

Reputation: 1233

imagemagick: set transparency by similarity to black

I have a image with filled contours of a certain colormap where white is the lowest level. I would not like to add alpha transparency to that map where white is totally transparent, and the less "similar" the color is to white, the less transparent it is (up to a maximum "fuzziness"). How can I do this?

Edit: To clarify: I want to assign for example:

Example: enter image description here

Upvotes: 0

Views: 1809

Answers (2)

Mark Setchell
Mark Setchell

Reputation: 207455

You can also do this a completely different way with ImageMagick's -fx operator that allows you to program anything you like. Be warned, though, it is interpreted, so it is slow for large images.

So, you can take your original image and add an alpha layer (-alpha opaque) to it, then select the new alpha channel (-channel A) as the one for -fx to affect. Then you can program in your response curve, along these lines:

convert image.png -alpha opaque -channel A -fx "lightness < 0.9 ? 1 : 1-lightness" result.png

enter image description here

Basically, -fx is like a function call, that can return a single number, and as a result of the -channel A option, that number will be applied as the alpha channel. The formula says... "if the pixel is less than 90% bright, make it opaque, else make its transparency proportional to its brightness."

By way of explanation, here is a black-white gradient.

convert -size 256x256 gradient:black-white result.png

enter image description here

and here it is with -fx applied to it:

convert -size 256x256 gradient:black-white -fx "lightness < 0.3 ? 1 : 1-lightness" result.png

enter image description here

If you use this as your alpha channel, you can see that darkest 30% of your image will be opaque (alpha is white) and the midtones and highlights will be progressively more transparent (alpha is black).

Without wishing to confuse you, if you like this approach but it is too slow because of the -fx, you could pre-make any gradient/response-curve as in the foregoing paragraph just once, and then apply it repeatedly to your potentially thousands of large images using ImageMagick's -clut operator. So you are in effect creating a pre-built Lookup Table of opacities and then just applying it as a lookup to all your images. I haven't tested this but it would look something like:

Initial step - just do it once:

convert -size 256x1! gradient:black-white -fx "lightness < 0.3 ? 1 : 1-lightness" clut.png

Main processing loop

convert image.jpg \( +clone -colorspace HSL -separate -delete 0,1 -normalize -clut clut.png \) -compose copyopacity -composite result.png

Upvotes: 0

Mark Setchell
Mark Setchell

Reputation: 207455

Updated Answer

I think I have a better handle on what you want now. How about we calculate the lightness of your image, normalise it, split it into a number of discrete values (by binning) and then use that as the transparency... something like this:

convert image.png                                          \
   \(                                                      \
      +clone -colorspace HSL -separate -delete 0,1         \
      -negate -normalize -posterize 5 -black-threshold 60% \
   \) -compose copyopacity -composite result.png

enter image description here

Let me explain that monster command. It says... Load up image.png and then do some processing "on-the-side" - that's everything in the parentheses (...). When you've done the "on-the-side" processing, use whatever you generated as the opacity/alpha channel for the original image -compose copy opacity -composite and save the result.

Now, let's look at the "on-the-side" processing inside the (). That says... Copy the original image (+clone) and convert it to Hue, Saturation and Lightness channels rather than RGB (-colorspace HSL). Then separate the 3 channels (-separate) and discard channels 0 and 1 - so I discard the Hue (colour) and the Saturation (vividness) and I am just left with the Lightness, or Brightness of your original image - which is what we actually want. Now I normalise (-normalize) that brightness range to the full range 0-255 and convert it into 5 steps with the -posterize 5. So the steps will be 0-50, 50-100, 100-150, 150-200 and 200-255. Then I make all the steps less than 60% solid so that all the shadows and midtones in your image are fully opaque and just the highlights have stepped opacity. That is then used as the alpha channel when I finally exit the parentheses as above.

If you want to actually see the opacity channel itself that all the "on-the-side" processing generates, just add -write opacity.png after -black-threshold 60% and before the closing ). Then you can preview the opacity channel in file opacity.png.

I have put it over a chessboard so you can see the transparent areas. You will probably want to change the -posterize parameter to set the granularity and then threshold to leave less than say 80% unaffected.

In order to fine-tune the method, use this command to generate a gradient, convert it to steps and discard some steps:

convert -size 256x256 xc:red                                    \
  \( gradient:white-black -posterize 20 -white-threshold 50% \) \
  -compose copyopacity -composite result.png

enter image description here

Change the 20 and 50% to change the number of steps and the cutoff.

If you want to composite over a chessboard (checkerboard) like Photoshop does, so that you can see the transparency, use this:

composite -compose Dst_Over -tile pattern:checkerboard result.png preview.png

enter image description here

Original Answer

Build a transparency mask first and then use it as the opacity of your original image.

So, make a gradient - or any other transparency you fancy:

convert -size 400x400 gradient:black-white       \
   -fill white -draw "rectangle 100,100 300,300" \
   -fill black -draw "rectangle 150,150 250,250" \
   -define png:color-type=0 alpha.png

enter image description here

And take a solid red image:

convert -size 400x400 xc:red red.png

enter image description here

Now apply the alpha/opacity layer to the solid image:

convert red.png alpha.png -compose copyopacity -composite result.png

enter image description here

Upvotes: 2

Related Questions