Watermarking images with OpenCV and Python

watermark_headerA few weeks ago, I wrote a blog post on creating transparent overlays with OpenCV. This post was meant to be a gentle introduction to a neat little trick you can use to improve the aesthetics of your processed image(s), such as creating a Heads-up Display (HUD) on live video streams.

But there’s another, more practical reason that I wanted to introduce transparent overlays to you — watermarking images. Watermarking an image or video is called digital watermarking, and is the process of embedding a unique and identifying pattern onto the image itself.

For example, professional photographers tend to watermark digital proofs sent to clients (including relevant information such as their name and/or design studio) until the client agrees to purchase the photos, at which the original, unaltered images are released. This allows the photographer to distribute demos and samples of their work, without actually “giving away” the original compositions.

We also see digital watermarks in copyrighted video — in this case, a watermark is embedded into each frame of the video, thereby crediting the original producer of the work.

In both of these cases, the goal of watermarking is to create a unique and identifiable pattern on the image, giving attribution to the original creator, but without destroying the contents of the image itself.

To learn how to utilize OpenCV to watermark your own dataset of images, keep reading.

Looking for the source code to this post?
Jump right to the downloads section.

Watermarking images with OpenCV and Python

The goal of this blog post is to demonstrate how to add watermarks to images using OpenCV and Python. To get started, we’ll need a watermark, which for the purposes of this tutorial, I’ve chosen to be the PyImageSearch logo:

Figure 1: Our example watermark image -- the PyImageSearch logo.

Figure 1: Our example watermark image — the PyImageSearch logo.

This watermark is a PNG image with four channels: a Red channel, a Green channel, a Blue channel, and an Alpha channel used to control the transparency of each of the pixels in the image.

Values in our alpha channel can range [0, 255], where a value of 255 is 100% opaque (i.e., not transparent at all) while a value of 0 is 100% transparent. Values that fall between 0 and 255 have varying levels of transparency, where the smaller the alpha value, the more transparent the pixel is.

In the above figure, all pixels that are not part of the white “PyImageSearch” logo are fully transparent, meaning that you can “see through them” to the background of what the image is laid on top of. In this case, I’ve set the image to have a blue background so we can visualize the logo itself (obviously, you would not be able to see the white PyImageSearch logo if I placed it on a white background — hence using a blue background for this example).

Once we actually overlay the watermark on our image, the watermark will be semi-transparent, allowing us to (partially) see the background of the original image.

Now that we understand the process of watermarking, let’s go ahead and get started.

Creating a watermark with OpenCV

Open up a new file, name it watermark_dataset.py , and let’s get started:

Lines 2-6 import our required Python packages. We’ll be making use of the imutils package here, so if you do not already have it installed, let pip  install it for you:

Lines 9-20 then handle parsing our required command line arguments. We require three command line arguments and can supply two additional (optional) ones. A full breakdown of each of the command line arguments can be found below:

  • --watermark : Here we supply the path to the image we wish to use as the watermark. We presume that (1) this image is a PNG image with alpha transparency and (2) our watermark is smaller (in terms of both width and height) then all images in the dataset we are going to apply the watermark to.
  • --input : This is the path to our input directory of images we are going to watermark.
  • --output : We then need to supply an output directory to store our watermarked images.
  • --alpha : The optional --alpha  value controls the level of transparency of the watermark. A value of 1.0 indicates that the watermark should be 100% opaque (i.e., not transparent). A value of 0.0 indicates that the watermark should be 100% transparent. You’ll likely want to tune this value for your own datasets, but I’ve found that a value of 25% works well for most circumstances.
  • --correct : Finally, this switch is used to control whether or not we should preserve a “bug” in how OpenCV handles alpha transparency. The only reason I’ve included this switch is for a matter of education regarding the OpenCV library. Unless you want to investigate this bug yourself, you’ll likely be leaving this parameter alone.

Now that we have parsed our command line arguments, we can load our watermark image from disk:

Line 24 loads our watermark  image from disk using the cv2.imread  function. Notice how we are using the cv2.IMREAD_UNCHANGED  flag — this value is supplied so we can read the alpha transparency channel of the PNG image (along with the standard Red, Green, and Blue channels).

Line 25 then grabs the spatial dimensions (i.e., height and width) of the watermark  image.

The next code block addresses some strange issues I’ve encountered when working with alpha transparency and OpenCV:

When I first implemented this example, I noticed some extremely strange behavior on the part of cv2.imread  and PNG filetypes with alpha transparency.

To start, I noticed that even with the cv2.IMREAD_UNCHANGED  flag, the transparency values in the alpha channel were not respected by any of the Red, Green, or Blue channels — these channels would appear to be either fully opaque or semi-transparent, but never the correct level of transparency that I presumed they would be.

However, upon investigating the alpha channel itself, I noticed there were no problems with the alpha channel directly — the alpha channel was loaded and represented perfectly.

Therefore, to ensure that each of the Red, Green, and Blue channels respected the alpha channel, I took the bitwise AND  between the individual color channels and the alpha channel, treating the alpha channel as a mask (Lines 33-37) — this resolved the strange behavior and allowed me to proceed with the watermarking process.

I’ve included the --correct  flag here so that you can investigate what happens when you do not apply this type of correction (more on in this the “Watermarking results” section).

Next, let’s go ahead and process our dataset of images:

On Line 40 we start looping over each of the images in our --input  directory. For each of these images, we load it from disk and grab its width and height.

It’s important to understand that each image  is represented as a NumPy array with shape (h, w, 3), where the 3 is the number of channels in our image — one for each of the Red, Green, and Blue channels, respectively.

However, since we are working with alpha transparency, we need to add a 4th dimension to the image to store the alpha values (Line 45). This alpha channel has the same spatial dimensions as our original image and all values in the alpha channel are set to 255, indicating that the pixels are fully opaque and not transparent.

Lines 51 and 52 construct the overlay  for our watermark. Again, the overlay  has the exact same width and height of our input image.

Note: To learn more about transparent overlays, please refer to this blog post.

Finally, Lines 55 and 56 construct our watermarked image by applying the cv2.addWeighted  function.

Lines 59-61 then take our output  image and write it to the --output  directory.

Watermarking results

To give our watermark_dataset.py  script a try, download the source code and images associated with this post using the “Downloads” form at the bottom of this tutorial. Then, navigate to the code directory and execute the following command:

After the script finishes executing, your output  directory should contain the following five images:

Figure 2: The output from our watermarking script.

Figure 2: The output from our watermarking script.

You can see each of the watermarked images below:

Figure 3: Watermarking images with OpenCV and Python.

Figure 3: Watermarking images with OpenCV and Python.

In the above image, you can see the white PyImageSearch logo has been added as a watermark to the original image.

Below follows a second example of watermarking an image with OpeCV. Again, notice how the PyImageSearch logo appears (1) semi-transparent and (2) in the bottom-right corner of the image:

Figure 4: Creating watermarks with OpenCV and Python.

Figure 4: Creating watermarks with OpenCV and Python.

About a year ago, I went out to Arizona to enjoy the Red Rocks. Along the way, I stopped at the Phoenix Zoo to pet and feed a giraffe:

Figure 5: Feed a giraffe...and watermarking the image with computer vision.

Figure 5: Feeding a giraffe…and watermarking the image with computer vision.

I’m also a huge fan of mid-century modern architecture, so I had to visit Taliesin West:

Figure 6: Watermarking images with OpenCV.

Figure 6: Watermarking images with OpenCV.

Finally, here’s a beautiful photo of the Arizona landscape (even if it was a bit cloudy that day):

Figure 7: Creating watermarks with OpenCV is easy!

Figure 7: Creating watermarks with OpenCV is easy!

Notice how in each of the above images, the “PyImageSearch” logo has been placed in the bottom-right corner of the output image. Furthermore, this watermark is semi-transparent, allowing us to see the contents of the background image through the foreground watermark.

Strange behavior with alpha transparency

So, remember when I mentioned in Lines 32-37 that some strange behavior with alpha transparency can happen if we don’t take the bitwise AND  between each respective Red, Green, and Blue channel and the alpha channel?

Let’s take a look at this strangeness.

Execute the watermark_dataset.py  script again, this time supplying the --correct 0  flag to skip the bitwise AND  step:

Then, opening an output image of your choosing, you’ll see something like this:

Figure 8: (Incorrectly) creating a watermark with OpenCV.

Figure 8: (Incorrectly) creating a watermark with OpenCV.

Notice how the entire watermark image is treated as being semi-transparent instead of only the corresponding alpha pixel values!

Baffling, right?

I’m honestly not sure why this happens and I couldn’t find any information on the behavior in the OpenCV documentation. If anyone has any extra details on this issue, please leave a note in the comments section at the bottom of this post.

Otherwise, if you utilize alpha transparency in any of your own OpenCV image processing pipelines, make sure you take special care to mask each Red, Green, and Blue channels individually using your alpha mask.


In this blog post we learned how to watermark an image dataset using OpenCV and Python. Using digital watermarks, you can overlay your own name, logo, or company brand overtop your original work, thereby protecting the content and attributing yourself as the original creator.

In order to create these digital watermarks with OpenCV, we leveraged PNG with alpha transparency. Utilizing alpha transparency can be quite tricky, especially since it appears that OpenCV does not automatically mask transparent pixels for each channel.

Instead, you’ll need to manually perform this masking yourself by taking the bitwise AND  between the input channel and the alpha mask (as demonstrated in this blog post).

Anyway, I hope you enjoyed this tutorial!

And before you go, be sure to enter your email address in the form below to be notified when new blog posts are published!


If you would like to download the code and images used in this post, please enter your email address in the form below. Not only will you get a .zip of the code, I’ll also send you a FREE 11-page Resource Guide on Computer Vision and Image Search Engines, including exclusive techniques that I don’t post on this blog! Sound good? If so, enter your email address and I’ll send you the code immediately!

, , ,

13 Responses to Watermarking images with OpenCV and Python

  1. Miguel April 25, 2016 at 1:20 pm #

    I have a problem with cv2.bitwise_and nothing show. I have change this lines to
    B = cv2.bitwise_or(B, A, mask=A)
    now this work like your example. Did you know why?, I don’t understand whats happend.
    cv2 only show a black image when I use cv2.imshow(watermark.png) to view it.

    Found my error, I use a watermark whit transparent background and black text. Only change text to white color and all works fine.
    Thanks for your blogs.

    • Adrian Rosebrock April 25, 2016 at 1:56 pm #

      Congrats on resolving the issue Miguel!

      • May 13, 2016 at 10:50 pm #

        Hey, I don’t know how to correct the code to achieve the effect, can you provide the code, thank you very much

  2. Linus April 25, 2016 at 2:52 pm #

    Another great OpenCV technique! I’ll give it a try!

    • Adrian Rosebrock April 26, 2016 at 5:17 pm #

      Thanks Linus! 🙂

  3. Moe August 26, 2016 at 4:04 pm #

    Hello, first of all thank you so much for this great tutorial, your blog/website is a lifesaver.

    I am working on a project where I need to overlay an image inside a circle, the circle does not have a fixed size and position, it is always moving.

    My question is, how can I overlay the image (watermark) inside the moving circle? I am clueless as to how dictate the position of where the watermark is to be placed. How can I specify the (x, y) coordinates?

    Thank you in advance.

    • Adrian Rosebrock August 29, 2016 at 2:05 pm #

      If you’re just getting started learning about OpenCV and Python, I would really recommend that you go through Practical Python and OpenCV to help you build strong fundamentals. Inside the book I detail how to specify the (x, y)-coordinates of an image. In this case, it’s handled on Line 52 via NumPy array slicing.

  4. Deven March 22, 2017 at 2:23 pm #

    Can I implement this in a way that retains the color black? Such as a logo with a black outline.

    • Adrian Rosebrock March 23, 2017 at 9:32 am #

      What do you mean by “retains the color black”? You want the black outline of the logo to be 100% opaque, but parts of the logo itself to be transparent?

      • Deven March 24, 2017 at 12:45 pm #

        I made a watermark that is white with a black border in an attempt to make it visible on any background. When I apply the watermark, any black parts are completely transparent/invisible. Is there a way to avoid this?

        • Adrian Rosebrock March 25, 2017 at 9:19 am #

          That is quite strange. I would check to alpha mask to ensure the black pixels aren’t being accidentally masked. Otherwise, this might be one of the transparency issues that are common with OpenCV.

  5. Bietschhorn April 8, 2017 at 11:30 am #

    Hello Adrian,
    I can’t say how helpfull your tutorials and work are to me.
    A big THANK YOU to you and your team.
    My question is : could it be possible to watermark a camera capture with a video stream ?
    Could it be possible to watermark with a second video capture ?
    What would be the prerequisite apart from the 2 cameras ?
    Thank you for your help.
    I keep learning…

    • Adrian Rosebrock April 8, 2017 at 12:34 pm #

      It’s absolutely possible to watermark a video stream. It’s as simple as accessing your video stream and then applying the watermark technique detailed in this blog post to each individual frame.

Leave a Reply