This project has moved. For the latest updates, please go here.

Set opacity of an image and layer it on top of another one

Jul 19, 2015 at 8:23 AM
With ImageMagick I could do:

Wm.Evaluate(ImageMagick.Channels.Opacity, ImageMagick.EvaluateOperator.Multiply, watermark.opacity)
Img.Composite(Wm, watermark.x, watermark.y, ImageMagick.CompositeOperator.Atop)

Now, GraphicsMagick has the operator command to do the same thing:

http://superuser.com/a/444959
http://www.graphicsmagick.org/GraphicsMagick.html#details-operator

I'd be nice if this .NET wrapper supported it.

And the thumbnail command too! :P
Coordinator
Jul 19, 2015 at 10:33 AM
The Evaluate and the Composite method are both available in GraphicsMagick.NET, you could just use them. I just committed a patch to add Thumbnail and this will be available in the next release.
Jul 19, 2015 at 11:05 AM
Edited Jul 19, 2015 at 11:12 AM
Yes, sorry, I din't specify that using Evaluate with GraphicsMagick doesn't give the same results in my case. The image I want to layer on top is a 24 bit PNG containing text. With the instruction I use in ImageMagick, the transparent parts of the PNG are rendered in black (not matte, semi-transaparent, maybe according to watermark.opacity). That's why I thought I'd need the operator command...

Thank you for the thumbnail command. In your experience, does it make a difference in terms of speed compared do resize and strip?
Coordinator
Jul 19, 2015 at 12:07 PM
The -operator option actually calls the same code as the Evaluate method. You probably need to enable the alpha channel (matte) on the image first.

And you can find a good explanation about the different resize methods here: http://stackoverflow.com/questions/8517304/what-is-the-difference-for-sample-resample-scale-resize-adaptive-resize-thumbnai/13078621#13078621
Jul 19, 2015 at 1:35 PM
Edited Jul 19, 2015 at 1:35 PM
Thanks for the link. Interesting stuff! :)

No matter what I try, I can't ouput even a single image with an opacity other than 100%. Can you?
Coordinator
Jul 19, 2015 at 2:00 PM
Can you post the code and the images that you are using? And can you make it as small as possible? You cannot post images here so you should put the on something like dropbox or onedrive.
Jul 19, 2015 at 2:30 PM
Edited Jul 19, 2015 at 2:55 PM
Here's a simple generic handler to test it:
Public Class Handler1 Implements System.Web.IHttpHandler

Sub ProcessRequest(ByVal context As HttpContext) Implements IHttpHandler.ProcessRequest
    context.Response.ContentType = "image/jpeg"

    Using Img As New GraphicsMagick.MagickImage(context.Server.MapPath("bar.jpg")), Wm As New GraphicsMagick.MagickImage(context.Server.MapPath("foo.png"))
        Wm.MatteColor = GraphicsMagick.MagickColor.Transparent ' As you suggested
        Wm.Evaluate(GraphicsMagick.Channels.Opacity, GraphicsMagick.QuantumOperator.Multiply, 0.5)

        Img.Composite(Wm, 0, 0, GraphicsMagick.CompositeOperator.Atop)
        Img.Write(context.Response.OutputStream)
    End Using
End Sub

ReadOnly Property IsReusable() As Boolean Implements IHttpHandler.IsReusable
    Get
        Return False
    End Get
End Property
End Class
bar.jpg = http://i.imgur.com/UF9W9uk.jpg
foo.png = http://i.imgur.com/tJLArt5.png


This is what I see: http://i.imgur.com/gVZ4gFn.jpg
(In this example, the ouput is wrong in 2 ways: bar.jpg become semi-transparent and foo.png has the semi-transparent parts [text shadow] painted black)


The desired result is: http://i.imgur.com/gnARZwB.jpg
Coordinator
Jul 19, 2015 at 3:11 PM
Edited Jul 19, 2015 at 3:11 PM
The problem is that the values for transparency are different between ImageMagick 7 and GraphicsMagick. In ImageMagick 6 we use to do the same as in GraphicsMagick but we fixed that in version 7. In GraphicsMagick fully transparent is Quantum.Max (255 (Q8) / 65535 (Q16)) and fully opaque is zero. In ImageMagick it is inverted. This means that you will have to call Evaluate differently in GraphicsMagick:
using (MagickImage Img = new MagickImage(@"d:\UF9W9uk.jpg"))
{
  using (MagickImage Wm = new MagickImage(@"d:\tJLArt5.png"))
  {
    Wm.Evaluate(Channels.Opacity, QuantumOperator.Max, Quantum.Max / 2);
    Img.Composite(Wm, 0, 0, CompositeOperator.Atop);
    Img.Write(@"d:\test.png");
  }
}
Jul 19, 2015 at 4:58 PM
Niiiiice! :D

I did notice that the opaque/transparent scale was inverted, but I didn't know about the operator you're using...

What does the QuantumOperator.Max do exactly? Is the operation intended as 255 (Max Q8) - value (e.g 127)?

Evidently I totally forgot about the way of doing things in ImageMagick 6, since I started using the wrapper with that version...

I'll test if Max operator works for ImageMagick too (when I was testing the code, I found the multiply operator was the only one that did what I wanted).

I want to thank you again for all the help (beyond the call of duty) you given me. And the wrapper you're writing is simply an indispensable tool in the .NET world (you've saved us from System.Drawing :)!
Coordinator
Jul 19, 2015 at 6:16 PM
Edited Jul 19, 2015 at 6:17 PM
Max does the same as Math.Max it takes the maximum of two values (https://msdn.microsoft.com/en-us/library/cs0a3045(v=vs.110).aspx). This works with GraphicsMagick.NET because it will keep 255 at 255 and raise the other part to a maximum of 127. You can use Multiply in Magick.NET because if you multiply zero (fully transparent) it will stay the same. And the other parts will be divided in half and because they are at 255 (fully opaque) they will become 127.

Please be aware that the GraphicsMagick.NET wrapper is not maintained as much as Magick.NET. I am one of the developers of ImageMagick so I spend almost all my time on ImageMagick and Magick.NET.
Jul 19, 2015 at 8:05 PM
Ah, yes. I use Math.Max() regurarly, I didn't thought of it beacause, as you say, it takes two values. But of course Evaluates works on the value of each pixel (and this I didn't quite realize until now! :O), so that's where the second argument to max is (and that's why multiply worked as it did)... it is a strange way to go about it though, I mean, even as you are a developer on ImageMagick, why not expose opacity as a specific property (or cmd flag)? Is it to keep the tool low level, without hardcoding high level abstractions?

When I chose ImageMagick, GrahicsMagick had the advantage of being distribuited with a more liberal license and some benchmarks said that it was faster than ImageMagick. Now, the only reason I took an interest in Graphics is because it's half the size of Image and it's possible to merge it with my assembly without issues.

In light of the fact that both of the above may no longer be that important, I think I'll stay with Image for the time being. Still, you have made the wrapper, there must be some interest around this fork, isn't there?
Coordinator
Jul 20, 2015 at 8:12 AM
The evaluate method actually is a high level abstraction. Otherwise you would have to manipulate a byte/short array list that contains the values of each pixel.

And yes I made this fork because there was some interest. It is half the size because it is lacking a lot of features that are available in Magick.NET but you might not need those. I will probably change this assembly in the future and that will 'disable' the ILMerge feature. I am also planning to write about a performance comparison between GraphicsMagick.NET and Magick.NET. I really wonder if there is still such a big difference in speed.
Sep 15, 2015 at 5:08 PM
dlemstra wrote:
I really wonder if there is still such a big difference in speed.
I was intersted in the HaldClut feature a while ago and I did a test using the following code on 16bit versions of the library
Stopwatch sw = new Stopwatch();

MagickImage img_src = new MagickImage(@"w:\source.jpg");
MagickImage img_clut = new MagickImage(@"w:\clut.png");

sw.Start();
img_src.HaldClut(img_clut);
sw.Stop();
the average was:
GM: 60ms / IM: 1500ms - 4mp image
GM: 435ms / IM: 11400ms - 27mp image
GM: 885ms / IM: 22400ms - 55mp image
Coordinator
Sep 15, 2015 at 8:12 PM
I have heard other reports about haldclut being slow. I'll take a look at it later this week/weekend.