Combine Multiple Alpha Channels using C++

Magick++ is an object-oriented C++ interface to ImageMagick. Use this forum to discuss, make suggestions about, or report bugs concerning Magick++.
Post Reply
User avatar
jonoomph
Posts: 27
Joined: 2011-03-27T14:25:09-07:00
Authentication code: 8675308

Combine Multiple Alpha Channels using C++

Post by jonoomph »

Hi! I am trying to combine multiple alpha channels together in C++, and am having trouble figuring out the best approach. For example, I have an image with an existing alpha channel, and I want to apply a grayscale mask to it, modifying its alpha channel (but not completely replacing it).

I've tried using a composite with CopyOpacityCompositeOp, but that completely replaces the original image's alpha channel. Any suggestions on how to approach this task using the C++ API?

Thanks in advance!
-Jonathan
User avatar
jonoomph
Posts: 27
Joined: 2011-03-27T14:25:09-07:00
Authentication code: 8675308

Re: Combine Multiple Alpha Channels using C++

Post by jonoomph »

I'm just thinking out loud, but it seems like there might be a need for another composite operator, called AddOpacityCompositeOp, which adds the opacity values together and prevents any overflows. This would be very useful when trying to combine alpha channels together. =)
User avatar
fmw42
Posts: 25562
Joined: 2007-07-02T17:14:51-07:00
Authentication code: 1152
Location: Sunnyvale, California, USA

Re: Combine Multiple Alpha Channels using C++

Post by fmw42 »

copy your alpha channel, composite it via multiply with your mask, remove the original alpha channel and use copy_opacity to put the modified alpha back into the image.

Sorry I only use the command line and am not familiar with any of the APIs.
User avatar
jonoomph
Posts: 27
Joined: 2011-03-27T14:25:09-07:00
Authentication code: 8675308

Re: Combine Multiple Alpha Channels using C++

Post by jonoomph »

Thanks for the suggestion fmw42. I'm not sure the equivalent C++ way to do those things. However, i did find a manual way to add the opacity values of 2 images in the C++ API. Warning, it is slow and I'm sure there is a better way. =)

Code: Select all

	// Add 2 alpha channels together (i.e. maintain the original alpha while adding more)
	// Prepare to update image
	original_image->modifyImage();
	
	// Loop through each row
	for (int row = 0; row < original_image->size().height(); row++)
	{
		const Magick::PixelPacket* original_pixels = original_image->getConstPixels(0, row, original_image->columns(), 1);
		const Magick::PixelPacket* alpha_pixels = alpha_image->getConstPixels(0, row, alpha_image->columns(), 1);

		// Loop through each column
		for (int col = 0; col < original_image->size().width(); col++)
		{
			// Calculate new opacity value (and prevent overflow)
			int new_alpha = original_pixels[col].opacity + alpha_pixels[col].opacity;
			if (new_alpha > 65535.0)
				new_alpha = 65535.0;
			
			// Set the new opacity
			original_image->pixelColor(col, row, Magick::Color(original_pixels[col].red, original_pixels[col].green, original_pixels[col].blue, new_alpha));
		}
	}
	// Transfer image cache pixels to the image
	original_image->syncPixels();
I will investigate using composite with multiply, and copying the modified alpha back to the image. Thanks!
-Jonathan
User avatar
jonoomph
Posts: 27
Joined: 2011-03-27T14:25:09-07:00
Authentication code: 8675308

Re: Combine Multiple Alpha Channels using C++

Post by jonoomph »

Hi fmw42, I gave your approach a try, to the best of my understanding (using the C++ API). However, when trying to use the MultiplyCompositeOp composite operator, it does not produce a noticeable effect. In other words, it does not seem like the alpha channels are combining. In fact, i don't see any alpha channels with this operator.

However, when I switch to the AddCompositeOp, it almost works perfectly, accept that overflow happens, and wraps some of the alpha numbers incorrectly. But, in general, it does successfully add the 2 alpha channels... where as the MultiplyCompositeOp seemed to do nothing. Hmmmm. Any thoughts?
User avatar
fmw42
Posts: 25562
Joined: 2007-07-02T17:14:51-07:00
Authentication code: 1152
Location: Sunnyvale, California, USA

Re: Combine Multiple Alpha Channels using C++

Post by fmw42 »

AddCompositeOp
Is there a PlusCompositeOp? In command line, -compose add will wrap around and -compose plus will be clipped if any overflow.

In command line, I would do

convert image \( -clone -alpha extract mask -compose multiply -composite \) -alpha off -compose copy_opacity -composite result.

This makes a copy of the input image.
Extracts the alpha channel only and multiplies it by the mask image.
Then turns off alpha for both images (of which only the first has any alpha) and then puts the new alpha channel (second result in the parns) into the alpha channel of the first image (replacing the original one since it was turned off).
User avatar
jonoomph
Posts: 27
Joined: 2011-03-27T14:25:09-07:00
Authentication code: 8675308

Re: Combine Multiple Alpha Channels using C++

Post by jonoomph »

Yes, there is a PlusCompositeOp... but just like the MultiplyCompositeOp operator, it does not seem to work (or do anything) for me. I will also try these commands on a newer version of ImageMagick, as my current one is a few versions old. However, if anyone here knows how to do the above command line example using the C++ API, it would be awesome if you could share some sample code! =)
User avatar
fmw42
Posts: 25562
Joined: 2007-07-02T17:14:51-07:00
Authentication code: 1152
Location: Sunnyvale, California, USA

Re: Combine Multiple Alpha Channels using C++

Post by fmw42 »

what does your mask image look like? can you provide a link to it and to your input image? perhaps I misunderstand what you are trying to do? if I had your images, I could test out my command line and see if that does what you want.
User avatar
jonoomph
Posts: 27
Joined: 2011-03-27T14:25:09-07:00
Authentication code: 8675308

Re: Combine Multiple Alpha Channels using C++

Post by jonoomph »

Here are a few test images that demonstrates what should happen.

1) The grayscale mask I need to apply to the 'front' image
http://openshot.org/files/mask.png

2) The 'front' image, which needs the mask applied
http://openshot.org/files/front.png

3) The background image, which already contains an alpha channel (gradient to alpha)
http://openshot.org/files/back.png

4) The final result that I am looking to achieve. The 'front' image has the mask applied to it's alpha channel, and the 'back' maintains its own alpha channel... plus the 'front' image.
http://openshot.org/files/final-composite.png

Please let me know if you have any questions, and thanks again for your help!
User avatar
fmw42
Posts: 25562
Joined: 2007-07-02T17:14:51-07:00
Authentication code: 1152
Location: Sunnyvale, California, USA

Re: Combine Multiple Alpha Channels using C++

Post by fmw42 »

This works for me in command line mode. However, I had to negate the mask and the results are slightly different in the gradients. Note that all your images have an alpha channel, though in the mask and front images, they are totally opaque alpha channels.


convert back.png \
\( front.png \( mask.png -negate \) -alpha off -compose copy_opacity -composite \) \
-compose over -background none -flatten final.png

Note that at least in the command line, one needs to reset the compose method before -flatten, since it was left in the copy_opacity mode from earlier.
User avatar
jonoomph
Posts: 27
Joined: 2011-03-27T14:25:09-07:00
Authentication code: 8675308

Re: Combine Multiple Alpha Channels using C++

Post by jonoomph »

Hi fmw42! Thanks for the example, that makes perfect sense. However, I realized that my example was flawed and did not reveal my true issue. My primary issue is preserving the alpha channel in the layer that the mask is applied to. So, here is an example that correctly demonstrates what I am trying to achieve... and I fear this may be a more difficult scenario. =)

1) The grayscale mask I need to apply to the 'front' image (to make the front image more transparent)
http://openshot.org/files/mask.png

2) The 'front' image, which needs the mask applied (which has some transparency in the alpha channel)
http://openshot.org/files/front3.png

3) The background image, which also contains some transparency (gradient to transparent)
http://openshot.org/files/back.png

4) The final result that I am looking to achieve. The 'front' image has the mask applied to it's alpha channel, and both the 'front' and 'back' images maintain their own alpha channel.
http://openshot.org/files/output-final.png

As you can see, in the final "output-final.png" image, it applies the mask to the alpha channel, but never makes things more opaque than they already are. In other words, the alpha mask needs to make things 'more transparent', but never more opaque. I hope this makes sense. =)

Thanks again for your help!
User avatar
fmw42
Posts: 25562
Joined: 2007-07-02T17:14:51-07:00
Authentication code: 1152
Location: Sunnyvale, California, USA

Re: Combine Multiple Alpha Channels using C++

Post by fmw42 »

The following makes the result more transparent than your output.


convert back.png front3.png \
\( -clone 1 -alpha extract \( mask.png -negate \) -compose multiply -composite \) \
\( -clone 1 -clone 2 -alpha off -compose copy_opacity -composite \) \
-delete 1,2 -background none -compose over -flatten new_final.png

Is that what you want?

read both image
copy the front and extract its alpha channel, then negate the mask and multiply the two
copy the front and the new alpha channel and replace old alpha in front with the new alpha
delete temps
flatten back and new front with transparent background.
User avatar
jonoomph
Posts: 27
Joined: 2011-03-27T14:25:09-07:00
Authentication code: 8675308

Re: Combine Multiple Alpha Channels using C++

Post by jonoomph »

Thanks for the quick reply! You rock! Let me take a look at your command line version, and see if I can figure out how to reproduce the steps with the C++ API. On first look, the only think I am unclear on is why you are negating the mask?
User avatar
jonoomph
Posts: 27
Joined: 2011-03-27T14:25:09-07:00
Authentication code: 8675308

Re: Combine Multiple Alpha Channels using C++

Post by jonoomph »

I finally figured out the C++ API version of these commands, and I also understood why you negated the extracted (i.e. grayscale) alpha channel before multiplying the mask's grayscale. For those who are interested, here is my working C++ code to apply a transparency mask to an image that already contains an alpha channel. This simple code example will hopefully save someone many days of frustration! :D

Code: Select all

	// Get the mask image (this is actually a copy of another image already in scope)
	tr1::shared_ptr<Magick::Image> mask_image = tr1::shared_ptr<Magick::Image>(new Magick::Image(*mask.get()));

	// Get copy of our source (i.e. front) image
	tr1::shared_ptr<Magick::Image> copy_source = tr1::shared_ptr<Magick::Image>(new Magick::Image(*source_image.get()));
	copy_source->channel(Magick::MatteChannel); // extract alpha channel as grayscale image
	copy_source->matte(false); // remove alpha channel
	copy_source->negate(true); // negate source alpha channel before multiplying mask
	copy_source->composite(*mask_image.get(), 0, 0, Magick::MultiplyCompositeOp); // multiply mask grayscale (i.e. combine the 2 grayscale images)

	// Copy the combined alpha channel back to the frame
	source_image->composite(*copy_source.get(), 0, 0, Magick::CopyOpacityCompositeOp);
All credit goes to fmw42, for being awesome and helping me make sense of all this.

Thanks again!
-Jonathan
Post Reply