Core C API - Manipulating Alpha Channnel

Questions and postings pertaining to the development of ImageMagick, feature enhancements, and ImageMagick internals. ImageMagick source code and algorithms are discussed here. Usage questions which are too arcane for the normal user list should also be posted here.
Post Reply
Minok
Posts: 21
Joined: 2014-04-24T15:51:58-07:00
Authentication code: 6789

Core C API - Manipulating Alpha Channnel

Post by Minok »

I've got my monochrome TIFF in memory and want to add an alpha channel and then apply the correct alpha values via TransparentPaintImage.
But I either end up with no alpha (bitonal image again - does ImageMagick optimize on output ?) Or a completely transparent on every pixel image.

Currently, I'm trying this:

Code: Select all

		TransformImageColorspace(imgMagickImage, RGBColorspace);
		imgMagickImage->matte= MagickTrue; // force an alpha channel
		// set the transparency (alpha channel on the image)
		SetImageAlphaChannel( imgMagickImage, AlphaChannelType::ActivateAlphaChannel);
		//SetImageAlphaChannel( imgMagickImage, AlphaChannelType::OpaqueAlphaChannel);	// set an opaque alpha channel
		MagickPixelPacket imgFilter;	// determines which pixels are set to transparent
		imgFilter.red=0;
		imgFilter.green=0;
		imgFilter.blue=0;
		imgFilter.fuzz = 0; // anything near white is also transparent
		Quantum opacity = QuantumRange;	// quantum range is 0 to QuantumRange (eg 65535)
		MagickBooleanType invertBool = MagickFalse;	//
		TransparentPaintImage( imgMagickImage, &imgFilter, opacity, invertBool);
What I end up with is my image, in 32 bit color space, but all of the pixels are transparent. With a black and white image (colors 0 or 1) I'd expect that either all the white or all the black pixels were transparent.

Changing my opacity value to 0 has no effect - same image, 100% transparent pixels.

Does TransparentPaintImage work as expected?
User avatar
dlemstra
Posts: 1570
Joined: 2013-05-04T15:28:54-07:00
Authentication code: 6789
Contact:

Re: Core C API - Manipulating Alpha Channnel

Post by dlemstra »

You should use 'SetAlphaChannel' instead of 'ActivateAlphaChannel'. The latter only does this: imgMagickImage->matte=MagickTrue. With SetAlphaChannel the alpha channel will be set to OpaqueOpacity. And you do know that rgb(0,0,0) is black and not white as stated in the comment?
.NET + ImageMagick = Magick.NET https://github.com/dlemstra/Magick.NET, @MagickNET, Donate
Minok
Posts: 21
Joined: 2014-04-24T15:51:58-07:00
Authentication code: 6789

Re: Core C API - Manipulating Alpha Channnel

Post by Minok »

Yes, sorry about the confusion of white and black. The ultimate goal is to get the 'white' (or specific color pixels) to be transparent, but for now I'm trying to just get any color to be transparent and the others to be opaque.

My current code, that is not working still, is this:

Code: Select all

			// set the transparency (alpha channel on the image) - per http://studio.imagemagick.org/RMagick/doc/image1.html#alpha this adds alpha layer set to opaque
			SetImageAlphaChannel( imgMagickPngImage, AlphaChannelType::ActivateAlphaChannel);
			
			MagickPixelPacket imgFilter;	// determines which pixels are set to transparent
			imgFilter.red = 0; //bitonalTileIn->image_atts->transparent_cell_color.red *(255.0/65535.0);
			imgFilter.green = 0; //bitonalTileIn->image_atts->transparent_cell_color.green *(255.0/65535.0);
			imgFilter.blue = 0; //bitonalTileIn->image_atts->transparent_cell_color.blue *(255.0/65535.0);
			imgFilter.fuzz = 0; // anything near white is also transparent
			Quantum opacity = QuantumRange;	// quantum range is 0 to QuantumRange (eg 65535)
			MagickBooleanType invertBool = MagickFalse;	//
			TransparentPaintImage( imgMagickPngImage, &imgFilter, opacity, invertBool);
What I am getting is a semi-transparent image that is semi-transparent on ALL colors (the black and the white).
If I could get just the black pixels to be transparent and the non-black to be opaque, that's my current goal. Something isn't working as expected.


Image
http://imgur.com/IIlvnGA
User avatar
dlemstra
Posts: 1570
Joined: 2013-05-04T15:28:54-07:00
Authentication code: 6789
Contact:

Re: Core C API - Manipulating Alpha Channnel

Post by dlemstra »

Did you change your code to this as I suggested?

Code: Select all

SetImageAlphaChannel( imgMagickPngImage, AlphaChannelType::SetAlphaChannel);
And I think your filter should be white instead of black?

Code: Select all

MagickPixelPacket imgFilter;   // determines which pixels are set to transparent
imgFilter.red = QuantumRange;
imgFilter.green = QuantumRange;
imgFilter.blue = QuantumRange;
imgFilter.fuzz = 0; // anything near white is also transparent
.NET + ImageMagick = Magick.NET https://github.com/dlemstra/Magick.NET, @MagickNET, Donate
Minok
Posts: 21
Joined: 2014-04-24T15:51:58-07:00
Authentication code: 6789

Re: Core C API - Manipulating Alpha Channnel

Post by Minok »

When I make that change, I get no transparency what-so-ever - ie its all opaque. Its is causing more things than just activating/not activating alpha channels.
Let me add more of the code to show the full set of variables, in case those others are affecting things...

Code: Select all

		MagickCoreGenesis(".", MagickTrue);	// initialize the imagemagick environment

		Image* imgMagickImage;			// an imagemagick image handle
		ImageInfo* imgMagickInfo= AcquireImageInfo(); // the imagemagick image info object
		ExceptionInfo* imgMagickException= AcquireExceptionInfo();	// where exception information is stored from calls

		// create an im image object from the tiff encoded blob that contains CCITT Group 4 bitonal encoding
		imgMagickImage = BlobToImage(imgMagickInfo, (void*) tiffBlob, (size_t)tiffSize, imgMagickException); 

		CatchException(imgMagickException);	// test for and process imagemagick exceptions
		if (!imgMagickImage) { std::cerr << "ERROR: no magicImage returned - could not parse the blob" << std::endl; }

		//-- cause the TIFF sourced blob to be converted to a PNG blob
		strcpy(imgMagickImage->filename, "temp.internal.png" );
		strcpy(imgMagickImage->magick, "png" );
		unsigned int pngSize;
		unsigned char* pngBlob = ImageToBlob( imgMagickInfo, imgMagickImage, &pngSize, imgMagickException);	// create the PNG blob
		CatchException(imgMagickException);	// test for and process imagemagick exceptions
		if (!imgMagickImage) { std::cerr << "ERROR: could not convert image to PNG blob" << std::endl; }

		// Create PNG image from the PNG blob
		Image* imgMagickPngImage = BlobToImage(imgMagickInfo, (void*) pngBlob, (size_t)pngSize, imgMagickException); 
		strcpy(imgMagickPngImage->filename, "SVGBuilder.internal.png" );
		strcpy(imgMagickPngImage->magick, "png" );
		//--

		SetImageAlphaChannel( imgMagickPngImage, AlphaChannelType::ActivateAlphaChannel);
		MagickPixelPacket imgFilter;	// determines which pixels are set to transparent
		imgFilter.red = 0; 
		imgFilter.green = 0; 
		imgFilter.blue = 0; 
		imgFilter.fuzz = 0;
		Quantum opacity = QuantumRange;	// quantum range is 0 to QuantumRange (eg 65535)
		MagickBooleanType invertBool = MagickFalse;	//
		TransparentPaintImage( imgMagickPngImage, &imgFilter, opacity, invertBool);

		// Write out the PNG image to a file
		strcpy(imgMagickPngImage->filename, "binaryImage-magick_0.png");
		WriteImage(imgMagickInfo, imgMagickPngImage);
In the code above, if the ONLY thing I change is that SetImageAlphaChannel call to change the type, I get these results:
(note I used the type definitions found here (http://studio.imagemagick.org/RMagick/d ... html#alpha) to guide me initially)

SetImageAlphaChannel( imgMagickPngImage, AlphaChannelType::ActivateAlphaChannel);
  • Produces a 32 bit depth PNG, which is the PNG linked to in the prior message, and repeated here, that has a semitransparent alpha applied to all colors of the image.
  • Image
SetImageAlphaChannel( imgMagickPngImage, AlphaChannelType::SetAlphaChannel);
  • Produces a 1 bit depth PNG, which I've linked to HERE. So in this case, the PNG doesn't seem to get an alpha channel at all, as the image regains the 2 color PNG that comes from the 2 color TIFF.
  • Image
Note: I had added the ImageToBlob and BlobToImage steps (between the "//--" comments) in order to get the image out of the TIFF encoding and into the PNG encoding I need to generate an output as, since I need PNG and I need an alpha layer. It may well be that those 2 steps are not necessary.

Its a bitonal source image (1 bit depth), I need an alpha channel PNG out of
Yes, the end goal is to get the white pixels to be transparent, BUT for now I'll be happy with just getting the black pixels transparent.
The issue may well be that its not clear what the actual COLOR of the pixels is in the internal image, since the source image is a bitonal indexed (the colors could be indexed, the color white could be 0 and black could be 1, or black could be 0 and white could be some #FFFFFF, etc). This is because the source TIFF is a 1 bit depth encoded image where bit value 0 represents white and bit value 1 represents black. It may be that this TIFF bitonal isn't properly getting converted out of 1 bit depth mode.

I'd share the TIFF image but IMGUR converts it to PNG automatically and I cannot find a hosting service that leaves the TIFF as is that my employer allows me to post to. I don't see a way to attache the TIFF to this post directly.
Thoughts?
Minok
Posts: 21
Joined: 2014-04-24T15:51:58-07:00
Authentication code: 6789

Re: Core C API - Manipulating Alpha Channnel

Post by Minok »

Hm, I seem to have finally figured out the missing magic sauce.

Using SetImageAlphaChannel( imgMagickPngImage, AlphaChannelType::ActivateAlphaChannel);
produces a fully semi-transparent image - that turns on the alpha channel with some value used across the board (56285 in my case).

Using SetImageAlphaChannel( imgMagickPngImage, AlphaChannelType::SetAlphaChannel);
produces an opaque image - the alpha channel is set to opacity of 0 (which is the counter-intuitive FULLY OPAQUE - ImageMagick really should call that parameter transparency)

Using SetImageAlphaChannel( imgMagickImage, AlphaChannelType::OpaqueAlphaChannel);
produces the same effect I think.

But that wasn't the primary problem I was having ... the magic sauce that was as non-obvious as the fact that opaque is opacity of 0 is that ...

one needs to initilaize the MagickPixelPacket from the image, and not just rely on the default initialization and fill in the color info into the R G and B attributes.
( and TransparentOpacity is a synonym for the fully transparent value (opacity QuantumRange) )

So what works is:

Code: Select all

			SetImageAlphaChannel( imgMagickImage, AlphaChannelType::OpaqueAlphaChannel);	// set an opaque alpha channel
			MagickPixelPacket imgFilter;	// determines which pixels are set to transparent
			GetMagickPixelPacket(imgMagickPngImage,&imgFilter); // initialize the pixel packet from our image

			// scale our 16Bit SDI transparent RGB color (0->FFFF) into the RGB 0->QuantumRange of ImageMagick
			imgFilter.red = bitonalTileIn->image_atts->transparent_cell_color.red *(QuantumRange/65535.0);	
			imgFilter.green = bitonalTileIn->image_atts->transparent_cell_color.green *(QuantumRange/65535.0);
			imgFilter.blue = bitonalTileIn->image_atts->transparent_cell_color.blue *(QuantumRange/65535.0);

			imgFilter.fuzz = 0; // anything near white is also transparent
			Quantum opacity = TransparentOpacity;	// quantum range is 0 to QuantumRange (eg 65535); QuantumRange is 'transparent', 0 is opaque
			MagickBooleanType invertBool = MagickFalse;	//
			TransparentPaintImage( imgMagickPngImage, &imgFilter, opacity, invertBool);
So for the benefit of others, the key appears to be

MagickPixelPacket imgFilter; // determines which pixels are set to transparent
GetMagickPixelPacket(imgMagickPngImage,&imgFilter); // initialize the pixel packet from our image

// scale our 16Bit SDI transparent RGB color (0->FFFF) into the RGB 0->QuantumRange of ImageMagick
imgFilter.red = bitonalTileIn->image_atts->transparent_cell_color.red *(QuantumRange/65535.0);
imgFilter.green = bitonalTileIn->image_atts->transparent_cell_color.green *(QuantumRange/65535.0);
imgFilter.blue = bitonalTileIn->image_atts->transparent_cell_color.blue *(QuantumRange/65535.0);

And then we finally get:
Image
Post Reply