How do you add a transparent image watermark to an image?

Hello,

How do you add a transparent image watermark to an image?

I have found documentation on how to add a transparent text watermark to an image but not a transparent watermark image to an image.

Adding a Watermark to an Image|Documentation (aspose.com)

Can you point me in the right direction, please?

@djsomers, you can draw image on image using Graphics.DrawImage. Drawing Images using Graphics|Documentation
DrawImage | Aspose.Imaging for .NET API Reference
Please note, that your image and watermark image should support transparency, for example transparency supports PNG.

// load an existing PNG with Image.Load
using (var image = Aspose.Imaging.Image.Load(@"template.png"))
{
    // create and initialize an instance of Graphics class and Initialize an object of SizeF to store image Size
   var graphics = new Aspose.Imaging.Graphics(image);
   using (Image watermarkImage = Image.Load("watermark.png"))
   {
       graphics.DrawImage(watermarkImage,10,10);
   }

   image.Save("result.png", new PngOptions(){ColorType = PngColorType.TruecolorWithAlpha});
}

@samer.el-khatib
Hello, my original image is tiff(black and white), and input watermark is png (colored, not transparency).

I don’t know how to keep the watermark colored and set transparency of the watermark.

Here is my code

using (var image = Aspose.Imaging.Image.Load(img))
{
    // create and initialize an instance of Graphics class and Initialize an object of SizeF to store image Size
    var graphics = new Aspose.Imaging.Graphics(image);
    using (var watermarkImage = Aspose.Imaging.Image.Load(@"D:\AI\水印.png"))
    {
        graphics.DrawImage(watermarkImage, 10, 10);

        var options = new Aspose.Imaging.ImageOptions.TiffOptions(Aspose.Imaging.FileFormats.Tiff.Enums.TiffExpectedFormat.Default)
        {
            Compression = Aspose.Imaging.FileFormats.Tiff.Enums.TiffCompressions.CcittFax4,
            ImageLength = (uint)watermarkImage.Width,
            ImageWidth = (uint)watermarkImage.Height,
        };

        string savePath = Path.Combine(@"C:\Users\xj2ssf\Desktop\EDM_test",
                                        string.Format("{0}_{1}{2}", Path.GetFileNameWithoutExtension(img),
                DateTime.Now.ToString("yyyy_MM_dd_hh_mm_fff"),
                ".tiff"));

        image.Save(savePath, options);
    }
}

Hello, @msdos41,
if you want to keep colorful watermark and bw image, you should use
new TiffOptions(TiffExpectedFormat.TiffDeflateRgb) when saving the result. In other words, you should not use CcittFax4 compression as it converts image to bw, which leads to color loss.
If the input image is also bw or <4 bits per pixel, I would recommend to export it into 24/32-bit PNG at first and then watermarking it. Base image pixel format may affect the watermark color data.
As concerns setting transparency, you can do the next:

        using var image = Image.Load(inputPath) as RasterImage;
        var pixels = image.LoadArgb32Pixels(image.Bounds);

        const byte DesiredAlpha = 127;
        const int AlphaMask = DesiredAlpha << 24;
        const int PixelMask = 0x00ffffff;
        for (int i = 0; i < pixels.Length; i++)
        {
            pixels[i] = AlphaMask | (pixels[i] & PixelMask);
        }

        image.SaveArgb32Pixels(image.Bounds, pixels);

Hello, @Denis.Sitko,
Thank you for the support, and I tried your transparency code and it worked.
Currently I encounter a problem is that the original tiff size is around 400KB and watermark png size is less than 100KB, but after I put the watermark onto the tiff and export it ,the size grows too big like almost 3MB. I checked the details of the output tiff and found out the bit depth is 24 and the origin tiff is 1.

If I want to keep the colored watermark and compress the size of the output tiff, what should I do?

Here is my current code:

using (var image = Aspose.Imaging.Image.Load(file.InputStream))
{
    // create and initialize an instance of Graphics class and Initialize an object of SizeF to store image Size
    var graphics = new Aspose.Imaging.Graphics(image);

    using (var watermarkImage = Aspose.Imaging.Image.Load(@"D:\AI\水印.png") as Aspose.Imaging.RasterImage)
    {
        //设置水印图片透明
        var pixels = watermarkImage.LoadArgb32Pixels(watermarkImage.Bounds);
        const byte DesiredAlpha = 127;
        const int AlphaMask = DesiredAlpha << 24;
        const int PixelMask = 0x00ffffff;
        for (int i = 0; i < pixels.Length; i++)
        {
            pixels[i] = AlphaMask | (pixels[i] & PixelMask);
        }

        watermarkImage.SaveArgb32Pixels(watermarkImage.Bounds, pixels);

        //图片左上是原点
        double height = image.Height;
        double width = image.Width;
        double wmHeight = watermarkImage.Height;
        double wmWidth = watermarkImage.Width;

        float x = (float)(width - wmWidth);
        float y = (float)(0.5 * (height - wmHeight));

        graphics.DrawImage(watermarkImage, x, y);

        var options = new Aspose.Imaging.ImageOptions.TiffOptions(Aspose.Imaging.FileFormats.Tiff.Enums.TiffExpectedFormat.TiffDeflateRgb)
        {
            Compression = Aspose.Imaging.FileFormats.Tiff.Enums.TiffCompressions.Deflate,
            //ImageLength = (uint)watermarkImage.Width,
            //ImageWidth = (uint)watermarkImage.Height,
        };

        string savePath = Path.Combine(saveFolder,
                                        string.Format("{0}_{1}{2}", Path.GetFileNameWithoutExtension(file.FileName),
                DateTime.Now.ToString("yyyy_MM_dd_hh_mm_fff"),
                Path.GetExtension(file.FileName)));

        image.Save(savePath, options);

        return savePath;
    }
}

Hello @Denis.Sitko,

There is another weird thing. When the program run in the line “graphics.DrawImage(watermarkImage, x, y);” , sometimes the program will be stuck for a very very long time like no response and I have to shut down manually. And next time running might be or still might not ok.

I dont know what is the reason.

Thank you in advance!

@msdos41, could you share the bw TIFF and colorful watermark PNG images, so I can review their specs and provide efficient memory solution?

@Denis.Sitko

Sorry I cannot send you the tiff because it is a confidential file.
Could you please show me some possible improvement ways? I will try on my side first.

Thank you!

@msdos41, as concerns output size optimization you can try using the next export options:

new TiffOptions(TiffExpectedFormat.Default)
{
BitsPerSample = new ushort[] { 8 }, // 8 bits per pixel
Compression = TiffCompressions.Deflate,
Photometric = TiffPhotometrics.Palette,
Palette = ColorPaletteHelper.GetCloseImagePalette(image, (int)Math.Pow(2, 8)) // trimming 24 bit palette to 8 bits (256 colors) which may be enough in case the background image is BW
});

The next option is to use new TiffOptions(TiffExpectedFormat.TiffJpegRgb) { CompressedQuality = 75 } , because Jpeg compression seem to perform better than Deflate. By setting decreasing CompressedQuality value, you decrease quality and size.

As concerns the “weird thing” issue, I did not manage to reproduce this freeze. Maybe it happens in debug mode due to slow symbol loading. With no samples you use it is really complicated to determine possible reason. You could try clearing the VisualStudio cache or changing debugging config, which may actually help.

Hope this helps!

@Denis.Sitko

Thank you for the support.
I tried the deflate compression and it worked fine and the output tiff size decreased a little bit.
But the jpg compression does not work for me.
Here is my option code. Some errors occur, such as bitsample should be [8,8,8] ,[7,7,7]…, The color map may be defined for samples per pixel equal to 1 only, and outofmemory exception and so on.

I tried some modifications but none of them worked.

var options = new Aspose.Imaging.ImageOptions.TiffOptions(Aspose.Imaging.FileFormats.Tiff.Enums.TiffExpectedFormat.TiffJpegRgb)
{
    //BitsPerSample = new ushort[] { 2,2,2 }, // 8 bits per pixel
    Compression = Aspose.Imaging.FileFormats.Tiff.Enums.TiffCompressions.Jpeg,
    CompressedQuality = 50,
    Photometric = Aspose.Imaging.FileFormats.Tiff.Enums.TiffPhotometrics.Rgb,
    Palette = Aspose.Imaging.ColorPaletteHelper.GetCloseImagePalette((Aspose.Imaging.RasterImage)image, (int)Math.Pow(2, 8)) // trimming 24 bit palette to 8 bits (256 colors) which may be enough in case the background image is BW
};

@msdos41, in my previous message the export options examples are separate. You should either use this

new TiffOptions(TiffExpectedFormat.Default)
{
BitsPerSample = new ushort[] { 8 }, // 8 bits per pixel
Compression = TiffCompressions.Deflate,
Photometric = TiffPhotometrics.Palette,
Palette = ColorPaletteHelper.GetCloseImagePalette(image, (int)Math.Pow(2, 8)) // trimming 24 bit palette to 8 bits (256 colors) which may be enough in case the background image is BW
});

or
new TiffOptions(TiffExpectedFormat.TiffJpegRgb) { CompressedQuality = 75 // higher value - higher size }.
Jpeg compression does not support palleted images. In fact there are many details on configuring Tiff image, so in order to configure TiffOptions by yourself, I highly recommend this link: Manipulating TIFF Images|Documentation, especially starting from here: Manipulating TIFF Images|Documentation.

@Denis.Sitko

Thank you for the explanation.

I put a no-background watermark png onto a tif file. But the result shown as the attachment. Is it possible to solve it or I have to put white background to watermark png.Capture.PNG (25.6 KB)

@msdos41, you should put a white background to the watermark, so it fully covers the background area and with no transparency.
There are few ways to implement it:

  1. easy way of exporting transparent watermark image using new PngOptions { ColorType = TrueColor, // 24 bit color without A for alpha } or other export options into raster with 24 bits per pixel.
  2. more efficient way of manually loading its pixels and modifying them so the ones having Alpha to be changed.

Hello @Denis.Sitko

I am testing a large tiff to add png watermark on it. The origin tif file is larger than 40MB. But it shows Image saving failed error.

The tif is a drawing picture, which cannot be uploaded. The pixel size is 14391 x 339747. It is so huge.

The origin tif is 1 bit depth which is bw, but after put colored png watermark on it, the size will increase at least 2~3 times.

Is there a way to solve the saving error?

Here is my code, basically the same as before

using (var image = Aspose.Imaging.Image.Load(fileStream))
{
    // create and initialize an instance of Graphics class and Initialize an object of SizeF to store image Size
    var graphics = new Aspose.Imaging.Graphics(image);

    using (var watermarkImage = Aspose.Imaging.Image.Load(stampStream) as Aspose.Imaging.RasterImage)
    {
        //设置水印图片透明
        var pixels = watermarkImage.LoadArgb32Pixels(watermarkImage.Bounds);
        const byte DesiredAlpha = 127;
        const int AlphaMask = DesiredAlpha << 24;
        const int PixelMask = 0x00ffffff;
        for (int i = 0; i < pixels.Length; i++)
        {
            pixels[i] = AlphaMask | (pixels[i] & PixelMask);
        }

        watermarkImage.SaveArgb32Pixels(watermarkImage.Bounds, pixels);

        //图片左上是原点
        double height = image.Height;
        double width = image.Width;
        double wmHeight = watermarkImage.Height;
        double wmWidth = watermarkImage.Width;

        //增加水印缩放的判定
        double zoom = 1;
        if (wmHeight / height > 0.06)
        {
            zoom = 0.06 * height / wmHeight;
            watermarkImage.Resize((int)(zoom * wmWidth), (int)(zoom * wmHeight), Aspose.Imaging.ResizeType.NearestNeighbourResample);
        }

        float x = (float)(width - zoom * wmWidth);
        float y = (float)(0.5 * (height - zoom * wmHeight));

        graphics.DrawImage(watermarkImage, x, y);

        var options = new Aspose.Imaging.ImageOptions.TiffOptions(Aspose.Imaging.FileFormats.Tiff.Enums.TiffExpectedFormat.TiffDeflateRgb)
        {
            BitsPerSample = new ushort[] { 8 }, // 8 bits per pixel
            Compression = Aspose.Imaging.FileFormats.Tiff.Enums.TiffCompressions.Deflate,
            Photometric = Aspose.Imaging.FileFormats.Tiff.Enums.TiffPhotometrics.Palette,
            Palette = Aspose.Imaging.ColorPaletteHelper.GetCloseImagePalette((Aspose.Imaging.RasterImage)image, (int)Math.Pow(2, 8)) // trimming 24 bit palette to 8 bits (256 colors) which may be enough in case the background image is BW
        };

        /*
        var options = new Aspose.Imaging.ImageOptions.TiffOptions(Aspose.Imaging.FileFormats.Tiff.Enums.TiffExpectedFormat.TiffJpegRgb)
        {
            //BitsPerSample = new ushort[] { 2,2,2 }, // 8 bits per pixel
            Compression = Aspose.Imaging.FileFormats.Tiff.Enums.TiffCompressions.Jpeg,
            CompressedQuality = 50,
            //Photometric = Aspose.Imaging.FileFormats.Tiff.Enums.TiffPhotometrics.Rgb,
            //Palette = Aspose.Imaging.ColorPaletteHelper.GetCloseImagePalette((Aspose.Imaging.RasterImage)image, (int)Math.Pow(2, 8)) // trimming 24 bit palette to 8 bits (256 colors) which may be enough in case the background image is BW
        };
        */

        image.Save(savePath, options);
    }
}

Hello, @msdos41, could you share the exception message, so I can also know what line of your code throws it?
It seems like you get an OutOfMemory exception due to big TIFF size. Try using new LoadOptions { BufferSizeHint = 500 }, which sets your memory limit to avoid such errors.

@Denis.Sitko

I use try catch to get the error message: “image saving failed error” in the last line of code “image.Save(savePath, options)”

@msdos41, as concerns size optimizations,
it is almost unreal to avoid the size increasing in case of keeping colors for watermark, but there are some options for the input TIFF as BW 14391 x 339747 and watermark as color PNG image.

  1. Best memory solution: saving the watermarked TIFF as BW by using Image.Save(path / stream) with no options specified, so it uses the original TIFF options, or by using new TiffOptions(TiffExpectedFormat.Ccitt).
    It will result the whole image including the watermarked area to be BW, 1 bit - per - pixel(BPP).

  2. Trimming the watermark colors to 4 BPP (16 colors):

             var bpp = 4;
             image.Save(outputPath, new TiffOptions(TiffExpectedFormat.TiffDeflateRgb)
             {
                 BitsPerSample = new ushort[] { (ushort)bpp },
                 Compression = TiffCompressions.Deflate,
                 Photometric = TiffPhotometrics.Palette,
                 Palette = ColorPaletteHelper.GetCloseImagePalette(image, 1 << bpp, PaletteMiningMethod.ColorClustering),
             });
    

it can also cause color loss/inaccuracy, but reduces the output size.

  1. Try using LZW compression:

             var bpp = 4; // or 8 for better color representation
             image.Save(outputPath, new TiffOptions(TiffExpectedFormat.TiffDeflateRgb)
             {
                 BitsPerSample = new ushort[] { (ushort)bpp },
                 Compression = TiffCompressions.Deflate,
                 Photometric = TiffPhotometrics.Palette,
                 Palette = ColorPaletteHelper.GetCloseImagePalette(image, 1 << bpp, PaletteMiningMethod.ColorClustering),
             });
    

Also you could try to reduce output TIFF size by setting TiffOptions.RowsPerStrip or TiffOptions.TileWidth&TileLength properties in export options.

@Denis.Sitko

My confusion is even though the output file size will be increased to 2~3 times as the origin file. In my case, the output should be around 80MB, which this case is not the worst. This aspose.imaging library cannot write a tiff of 80MB?

Your solutions are all trying to reduce the output size, based on my current 30MB input. But I still have like 100MB, 200MB input file. Is there some ways to write the output file using buffer or something else configuration?

Thank you!

@msdos41,
As concerns image saving error, I would recommend to load large TIFF as
using var image = Image.Load(fileStream, new LoadOptions { BufferSizeHint = 500 // desired value }) as RasterImage;
to avoid possible OutOfMemoryException.
If the issue is not memory related, I need the original TIFF to reproduce the error with code example above. You could share cloud link to your large image file.

On output file size:
The output size inevitably increases due to changing original TIFF Compression and BitsPerPixel. BW TIFF images mainly use 1 BPP CCITT compression, very size efficient. CCITT supports only BW.
Maintaining colors of watermark requires increasing BPP and using other compression. You can try the next export options:

ushort bpp = 8;
var options = new TiffOptions(TiffExpectedFormat.Default)
{
    BitsPerSample = new ushort[] { bpp },
    Compression = TiffCompressions.Lzw,
    Photometric = TiffPhotometrics.Palette,
    Palette = ColorPaletteHelper.GetCloseImagePalette(image, 1  <<  bpp),
};

@Denis.Sitko,

watermark.png (14.5 KB)

https://drive.google.com/file/d/1scEZd78Yo2T6LE7t3thd7ub35Vx06UZB/view?usp=drive_link

I have uploaded 3 files, one is watermark.png, another two are test tif files.
These 2 test files represent 2 major problems when putting watermark on it.
test_1.tif is small but when I put watermark on it, it will get ‘Attempted to divide by zero.’ error.
test_2.tif is large so please help me to find if there is possible solution to put watermark on it.

Thank you!

Here is the code

using (var image = Aspose.Imaging.Image.Load(file.InputStream, new Aspose.Imaging.LoadOptions { BufferSizeHint = 1000 }) as Aspose.Imaging.RasterImage)
{
    // create and initialize an instance of Graphics class and Initialize an object of SizeF to store image Size
    var graphics = new Aspose.Imaging.Graphics(image);

    using (var watermarkImage = Aspose.Imaging.Image.Load(stamp.InputStream) as Aspose.Imaging.RasterImage)
    {
        //设置水印图片透明
        var pixels = watermarkImage.LoadArgb32Pixels(watermarkImage.Bounds);
        const byte DesiredAlpha = 127;
        const int AlphaMask = DesiredAlpha << 24;
        const int PixelMask = 0x00ffffff;
        for (int i = 0; i < pixels.Length; i++)
        {
            pixels[i] = AlphaMask | (pixels[i] & PixelMask);
        }

        watermarkImage.SaveArgb32Pixels(watermarkImage.Bounds, pixels);

        //图片左上是原点
        double height = image.Height;
        double width = image.Width;
        double wmHeight = watermarkImage.Height;
        double wmWidth = watermarkImage.Width;

        //增加水印缩放的判定
        double zoom = 1;
        if (wmHeight / height > 0.06)
        {
            zoom = 0.06 * height / wmHeight;
            watermarkImage.Resize((int)(zoom * wmWidth), (int)(zoom * wmHeight), Aspose.Imaging.ResizeType.NearestNeighbourResample);
        }

        float x = (float)(width - zoom * wmWidth);
        float y = (float)(0.5 * (height - zoom * wmHeight));

        graphics.DrawImage(watermarkImage, x, y);

        //var options = new Aspose.Imaging.ImageOptions.TiffOptions(Aspose.Imaging.FileFormats.Tiff.Enums.TiffExpectedFormat.TiffDeflateRgb)
        //{
        //    BitsPerSample = new ushort[] { 8 }, // 8 bits per pixel
        //    Compression = Aspose.Imaging.FileFormats.Tiff.Enums.TiffCompressions.Deflate,
        //    Photometric = Aspose.Imaging.FileFormats.Tiff.Enums.TiffPhotometrics.Palette,
        //    Palette = Aspose.Imaging.ColorPaletteHelper.GetCloseImagePalette((Aspose.Imaging.RasterImage)image, (int)Math.Pow(2, 8)) // trimming 24 bit palette to 8 bits (256 colors) which may be enough in case the background image is BW
        //};

        ushort bpp = 8;
        var options = new Aspose.Imaging.ImageOptions.TiffOptions(Aspose.Imaging.FileFormats.Tiff.Enums.TiffExpectedFormat.Default)
        {
            BitsPerSample = new ushort[] { bpp },
            Compression = Aspose.Imaging.FileFormats.Tiff.Enums.TiffCompressions.Lzw,
            Photometric = Aspose.Imaging.FileFormats.Tiff.Enums.TiffPhotometrics.Palette,
            Palette = Aspose.Imaging.ColorPaletteHelper.GetCloseImagePalette(image, 1 << bpp),
        };

        /*
        var options = new Aspose.Imaging.ImageOptions.TiffOptions(Aspose.Imaging.FileFormats.Tiff.Enums.TiffExpectedFormat.TiffJpegRgb)
        {
            //BitsPerSample = new ushort[] { 2,2,2 }, // 8 bits per pixel
            Compression = Aspose.Imaging.FileFormats.Tiff.Enums.TiffCompressions.Jpeg,
            CompressedQuality = 50,
            //Photometric = Aspose.Imaging.FileFormats.Tiff.Enums.TiffPhotometrics.Rgb,
            //Palette = Aspose.Imaging.ColorPaletteHelper.GetCloseImagePalette((Aspose.Imaging.RasterImage)image, (int)Math.Pow(2, 8)) // trimming 24 bit palette to 8 bits (256 colors) which may be enough in case the background image is BW
        };
        */

        //using (MemoryStream ms = new MemoryStream())
        //{
        //    image.Save(ms, options);
        //    using (FileStream fs = new FileStream(savePath, FileMode.Create))
        //    {
        //        ms.WriteTo(fs);
        //    }
        //}
        //using (FileStream fs = new FileStream(savePath, FileMode.Create))
        //{
        //    image.Save(fs/*, options*/);
        //}
        image.Save(savePath, options);
    }
}