A 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 SectionWatermarking 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:
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:
# import the necessary packages from imutils import paths import numpy as np import argparse import cv2 import os # construct the argument parse and parse the arguments ap = argparse.ArgumentParser() ap.add_argument("-w", "--watermark", required=True, help="path to watermark image (assumed to be transparent PNG)") ap.add_argument("-i", "--input", required=True, help="path to the input directory of images") ap.add_argument("-o", "--output", required=True, help="path to the output directory") ap.add_argument("-a", "--alpha", type=float, default=0.25, help="alpha transparency of the overlay (smaller is more transparent)") ap.add_argument("-c", "--correct", type=int, default=1, help="flag used to handle if bug is displayed or not") args = vars(ap.parse_args())
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:
$ pip install imutils
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:
# load the watermark image, making sure we retain the 4th channel # which contains the alpha transparency watermark = cv2.imread(args["watermark"], cv2.IMREAD_UNCHANGED) (wH, wW) = watermark.shape[:2]
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:
# split the watermark into its respective Blue, Green, Red, and # Alpha channels; then take the bitwise AND between all channels # and the Alpha channels to construct the actaul watermark # NOTE: I'm not sure why we have to do this, but if we don't, # pixels are marked as opaque when they shouldn't be if args["correct"] > 0: (B, G, R, A) = cv2.split(watermark) B = cv2.bitwise_and(B, B, mask=A) G = cv2.bitwise_and(G, G, mask=A) R = cv2.bitwise_and(R, R, mask=A) watermark = cv2.merge([B, G, R, A])
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:
# loop over the input images for imagePath in paths.list_images(args["input"]): # load the input image, then add an extra dimension to the # image (i.e., the alpha transparency) image = cv2.imread(imagePath) (h, w) = image.shape[:2] image = np.dstack([image, np.ones((h, w), dtype="uint8") * 255]) # construct an overlay that is the same size as the input # image, (using an extra dimension for the alpha transparency), # then add the watermark to the overlay in the bottom-right # corner overlay = np.zeros((h, w, 4), dtype="uint8") overlay[h - wH - 10:h - 10, w - wW - 10:w - 10] = watermark # blend the two images together using transparent overlays output = image.copy() cv2.addWeighted(overlay, args["alpha"], output, 1.0, 0, output) # write the output image to disk filename = imagePath[imagePath.rfind(os.path.sep) + 1:] p = os.path.sep.join((args["output"], filename)) cv2.imwrite(p, output)
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:
$ python watermark_dataset.py --watermark pyimagesearch_watermark.png \ --input input --output output
After the script finishes executing, your output
directory should contain the following five images:
You can see each of the watermarked images below:
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:
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:
I’m also a huge fan of mid-century modern architecture, so I had to visit Taliesin West:
Finally, here’s a beautiful photo of the Arizona landscape (even if it was a bit cloudy that day):
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:
$ python watermark_dataset.py --correct 0 --watermark pyimagesearch_watermark.png \ --input input --output output
Then, opening an output image of your choosing, you’ll see something like this:
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.
What's next? We recommend PyImageSearch University.
86 total classes • 115+ hours of on-demand code walkthrough videos • Last updated: October 2024
★★★★★ 4.84 (128 Ratings) • 16,000+ Students Enrolled
I strongly believe that if you had the right teacher you could master computer vision and deep learning.
Do you think learning computer vision and deep learning has to be time-consuming, overwhelming, and complicated? Or has to involve complex mathematics and equations? Or requires a degree in computer science?
That’s not the case.
All you need to master computer vision and deep learning is for someone to explain things to you in simple, intuitive terms. And that’s exactly what I do. My mission is to change education and how complex Artificial Intelligence topics are taught.
If you're serious about learning computer vision, your next stop should be PyImageSearch University, the most comprehensive computer vision, deep learning, and OpenCV course online today. Here you’ll learn how to successfully and confidently apply computer vision to your work, research, and projects. Join me in computer vision mastery.
Inside PyImageSearch University you'll find:
- ✓ 86 courses on essential computer vision, deep learning, and OpenCV topics
- ✓ 86 Certificates of Completion
- ✓ 115+ hours of on-demand video
- ✓ Brand new courses released regularly, ensuring you can keep up with state-of-the-art techniques
- ✓ Pre-configured Jupyter Notebooks in Google Colab
- ✓ Run all code examples in your web browser — works on Windows, macOS, and Linux (no dev environment configuration required!)
- ✓ Access to centralized code repos for all 540+ tutorials on PyImageSearch
- ✓ Easy one-click downloads for code, datasets, pre-trained models, etc.
- ✓ Access on mobile, laptop, desktop, etc.
Summary
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!
Download the Source Code and FREE 17-page Resource Guide
Enter your email address below to get a .zip of the code and a FREE 17-page Resource Guide on Computer Vision, OpenCV, and Deep Learning. Inside you'll find my hand-picked tutorials, books, courses, and libraries to help you master CV and DL!
Miguel
I have a problem with cv2.bitwise_and nothing show. I have change this lines to
B = cv2.bitwise_or(B, A, mask=A)
G………………..
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
Congrats on resolving the issue Miguel!
郑
Hey, I don’t know how to correct the code to achieve the effect, can you provide the code, thank you very much
Linus
Another great OpenCV technique! I’ll give it a try!
Adrian Rosebrock
Thanks Linus! 🙂
Moe
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
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.
Deven
Can I implement this in a way that retains the color black? Such as a logo with a black outline.
Adrian Rosebrock
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
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
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.
Bietschhorn
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
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.
Rosy
How to remove water mark or unwanted printed text from images?
Humberto
Hello Adrian,
My results keep showing the blue background (i used your logo to test it) and i have no idea why. do you?
Adrian Rosebrock
As I mentioned in the blog post, using alpha channels with OpenCV is a bit buggy. What version of OpenCV are you using?
Humberto
OpenCV 3.2.0
Adrian Rosebrock
I’m honestly not sure what the error is. Can you try with an older version of OpenCV, in particular OpenCV 2.4 and see if the error still occurs?
Alex
How to remove watermark ?
Ankit Pitroda
Hello sir,
Amazing tutorial by you as usual.
I want to watermark video instead of the image.
Like there is a live video feed from the USB camera and there is another offline video in my system and that will be watermark over that USB camera feed.
Thank a lot
Adrian Rosebrock
So if I understand your question you want to process frames of a video, add the watermark to each individual frame, and then write the frame back out to a separate video? If so, take a look at the cv2.VideoWriter class.
mansix
thanks a lot for informations very very good
Mojtaba Tabatabaie
Hi Adrian, Thanks very much for this useful tutorial. I tried it and got it working with my webcam feed. The only problem I have is that whatever value I set for alpha , I can’t have my image to be without transparency and always has a bit of transparency. I tried multiple images but it is still the case. I just wanted to have two an icon which has transparency in some areas to be overlaid on another larger image.
Thanks again very much
Adrian Rosebrock
Hey Mojtaba — alpha transparency with OpenCV can be a bit of a pain. Have you tried using ImageMagick instead? This would be useful for bulk-processing a set of images or frames.
Mojtaba Tabatabaie
Thanks very much Adrian, Yeah it was a pain , It can be done by setting ROI but I was thinking that maybe there is a simpler method or such.
Thanks
Kin
Hi Adrian, I have a question, I observed my watermark size varies from image to image. Is it because my images are not of same size? Can we fix the size of the watermark irrespective of the image size on which it has to be embedded?
Thanks.
Adrian Rosebrock
Correct, that is because your images vary in size. To “fix” your watermark size define a constant watermark size and a constant image size. Then, compute the ratio of any new image to the constant image size and use that ration to scale your watermark. In essence, it’s a ratio calculation.
Muthu Mariappan H
Hi Adrian thanks for the blog it helped us lot. The code is working for images. But when I tried to include watermarking in video it shows the error like,
overlay[h – wH – 10:h – 10, w – wW – 10:w – 10] = watermark
TypeError: long() argument must be a string or a number, not ‘function’
I am extracting all the frames one by one and applying watermarking.
Adrian Rosebrock
It sounds like you’ve modified the code for your own application but along the way have introduced a bug. Go back and double-check your variables as it looks like one is a “function” type.
ZARA LONE
I have an original image and in that a message is encrypted(its a logo) how to I provide border for that encrypted image.
Adrian Rosebrock
Sorry, I don’t think I’m understanding the question. Could you elaborate a bit?
nav
Hi Sir, can anybody help me on detecting watermark through image using python. Struggling for same and any code would help
Adrian Rosebrock
I would suggest you train a custom object detector to detect the watermark. I cover how to train your own custom object detectors inside Deep Learning for Computer Vision with Python and the PyImageSearch Gurus course.