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

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);
    }
}

Hello, @msdos41 ,
Let me process your request and the attached sources. You will be answered shortly!

@Denis.Sitko Thank you!

Also I tried the bw watermark, and just use the save method without options as you advised before.
But it will also get the error “Attempted to divide by zero.” which confused me a lot.

@djsomers
We have opened the following new ticket(s) in our internal issue tracking system and will deliver their fixes according to the terms mentioned in Free Support Policies.

Issue ID(s): IMAGINGNET-6428

You can obtain Paid Support Services if you need support on a priority basis, along with the direct access to our Paid Support management team.

As a workaround I recommend using the next save options:

  1. Using original save options with losing the watermark colors but keeping low output size:
    image.Save(savePath);

  2. Using the next save options (this one produces output without errors, but increases size and keeps colors of watermark)

     var options = new Aspose.Imaging.ImageOptions.TiffOptions(TiffExpectedFormat.TiffLzwRgb)
     {
         RowsPerStrip = 128,
     };
    

As for the large TIFF file the options below seem to work. But it takes much time to save due to very large size. Using the option 2 above speeds up processing, but increases the size. Option 1 will output totally BW output (seems to me the best option due to time/performance).

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),
};

As concerns processing images with large dimensions, it is important to use LoadOptions.BufferSizeHint. It also takes longer to process such images.

@Denis.Sitko

You did not get the error “Attempted to divide by zero.” when dealing with test_1.tif ?

@msdos41 , as I said before I managed to reproduce error for tiff1. You can see the link to the ticket. This error does not occur for the second tiff.