SoFunction
Updated on 2024-11-10

Python implementation of adding watermarks to images

1. Introduction

Adding watermarks to images is a common task in everyday image processing. There are various methods and tools available and today we will focus on adding watermarks in bulk using Python language combined with PIL library.

Note that the image format chosen should not be JPG or JPEG, as these two formats do not support transparency settings.

2. Overview of the PIL library

  • PIL is an image processing library for Python that supports a variety of file formats.
  • PIL provides powerful image and graphic processing functions, including scaling, cropping, overlaying, and adding lines, text, and other operations.
  • The following command can be used to install the PIL library:
pip install Pillow

3. Classes covered in the PIL library

module or class clarification
image module For image processing
ImageDraw 2D image objects
ImageFont Font Storage
ImageEnhance image enhancement

4. Principles of realization

The main goal of this article is to batch add watermarks to images in a folder, the principle of implementation is as follows:

  • Set the watermark content;
  • Use the open() method of the Image object to open the original image;
  • Use the new() method of the Image object to create the object that stores the watermarked image;
  • Use the text() method of the object to draw the watermark text;
  • Use the enhance() method of Brightness in ImageEnhance to set the watermark transparency.

5. Realization process

5.1 Original images

Set the storage directory for the original image, for example:

F:\python_study\image\image01

5.2 Importing relevant modules

Import the required PIL module or class:

from PIL imort Image, ImageDraw, ImageFont, ImageEnhance
import os

5.3 Initializing data

By user manually inputting relevant information such as image storage path, watermark text, watermark position, watermark transparency, etc:

class WatermarkText():
    def __init__(self):
        super(WatermarkText, self).__init__()
        self.image_path = input('Image path:')
        self.watermark_text = input('Watermark text:')
        self.position_flag = int(input('Watermark position (1: top left, 2: bottom left, 3: top right, 4: bottom right, 5: center):'))
         = float(input('Watermark transparency (1 decimal place between 0 and 1):'))

5.4 Watermark Font Settings

Select a font from the system font library:

 = ("", size=35)

5.5 Opening raw images and creating storage objects

Open the original image and convert it to RGBA:

image = (img).convert('RGBA')

Creates a drawing object:

new_img = ('RGBA', , (255, 255, 255, 0))
image_draw = (new_img)

5.6 Calculating the size of images and watermarks

Calculate the image size:

w, h = 

Calculate the text size:

w1 = (self.watermark_text)[0]
h1 = (self.watermark_text)[1]

5.7 Selectively setting watermark text

This is accomplished through if statements:

if self.position_flag == 1:  # Upper left corner
    location = (0, 0)
elif self.position_flag == 2:  # Bottom left
    location = (0, h - h1)
elif self.position_flag == 3:  # Upper right corner
    location = (w - w1, 0)
elif self.position_flag == 4:  # Bottom right
    location = (w - w1, h - h1)
elif self.position_flag == 5:  # Centered
    location = (h/2, h/2)

5.8 Drawing text and setting transparency

Drawing text:

image_draw.text(location, self.watermark_text, font=, fill="blue")

Set the transparency:

transparent = new_img.split()[3]
transparent = (transparent).enhance()
new_img.putalpha(transparent)

Image.alpha_composite(image, new_img).save(img)

5.9 Iterate over the image file and call the draw method

watermark_text = WatermarkText()
try:
    file_list = (watermark_text.image_path)
    for i in range(0, len(file_list)):
        filepath = (watermark_text.image_path, file_list[i])
        if (filepath):
            filetype = (filepath)[1]
            if filetype == '.png':
                watermark_text.add_text_watermark(filepath)
            else:
                print("The image is in the wrong format, please use a png image")
    print('Batch add watermark complete')
except:
    print('The input file path is wrong, please check~~')

6. Full source code

from PIL import

 Image, ImageDraw, ImageFont, ImageEnhance
import os

class WatermarkText():
    def __init__(self):
        super(WatermarkText, self).__init__()
        self.image_path = input('Image path:')
        self.watermark_text = input('Watermark text:')
        self.position_flag = int(input('Watermark position (1: top left, 2: bottom left, 3: top right, 4: bottom right, 5: center):'))
         = float(input('Watermark transparency (1 decimal place between 0 and 1):'))

        # Set the font
         = ("", size=35)

    # Text watermark
    def add_text_watermark(self, img):
        global location
        image = (img).convert('RGBA') 
        new_img = ('RGBA', , (255, 255, 255, 0)) 
        image_draw = (new_img) 
        w, h =   # Image size
        w1 = (self.watermark_text)[0]  # Font width
        h1 = (self.watermark_text)[1]  # Font Height

        # Set the watermark text position
        if self.position_flag == 1:  # Upper left corner
            location = (0, 0)
        elif self.position_flag == 2:  # Bottom left
            location = (0, h - h1)
        elif self.position_flag == 3:  # Upper right corner
            location = (w - w1, 0)
        elif self.position_flag == 4:  # Bottom right
            location = (w - w1, h - h1)
        elif self.position_flag == 5:  # Centered
            location = (h/2, h/2)
        # Drawing text
        image_draw.text(location, self.watermark_text, font=, fill="blue")

        # Set transparency
        transparent = new_img.split()[3]
        transparent = (transparent).enhance()
        new_img.putalpha(transparent)

        Image.alpha_composite(image, new_img).save(img)

if __name__ == "__main__":
    watermark_text = WatermarkText()
    try:
        file_list = (watermark_text.image_path) 
        for i in range(0, len(file_list)): 
            filepath = (watermark_text.image_path, file_list[i])
            if (filepath): 
                filetype = (filepath)[1] 
                if filetype == '.png': 
                    watermark_text.add_text_watermark(filepath) 
                else:
                    print("The image is in the wrong format, please use a png image")
        print('Batch add watermark complete')
    except:
        print('The input file path is wrong, please check~~')

7. Demonstration of results

Run the process:

D:\Python37\ F:/python_study/python_project/watermark_text.py
Image path: F:\python_study\image\image01
Watermarked text:
Watermark position (1: top left, 2: bottom left, 3: top right, 4: bottom right, 5: center): 1
Watermark transparency (1 decimal place between 0 and 1): 0.5
F:/python_study/python_project/watermark_text.py:32: DeprecationWarning: getsize is deprecated and will be removed in Pillow 10 (2023-07-01). Use getbbox or getlength instead.
w1 = (self.watermark_text)[0] # get the width of the font
F:/python_study/python_project/watermark_text.py:33: DeprecationWarning: getsize is deprecated and will be removed in Pillow 10 (2023-07-01). Use getbbox or getlength instead.
h1 = (self.watermark_text)[1] # get the font height
Batch add watermark complete

8. Improvements and recommendations

8.1 Optimization of parameter input methods

In the part of initializing the data, we can consider entering the relevant information by means of command line parameters or configuration files to improve the user experience. For example, usingargparselibrary to parse command line arguments.

import argparse

class WatermarkText():
    def __init__(self):
        parser = (description='Add watermark to images.')
        parser.add_argument('--image_path', type=str, help='Path to the image directory.')
        parser.add_argument('--watermark_text', type=str, help='Text for watermark.')
        parser.add_argument('--position_flag', type=int, help='Position flag for watermark (1: top-left, 2: bottom-left, 3: top-right, 4: bottom-right, 5: center).')
        parser.add_argument('--opacity', type=float, help='Opacity for watermark (0-1 with 1 decimal place).')
        
        args = parser.parse_args()

        self.image_path = args.image_path or input('Image path: ')
        self.watermark_text = args.watermark_text or input('Watermark text: ')
        self.position_flag = args.position_flag or int(input('Watermark position (1: top-left, 2: bottom-left, 3: top-right, 4: bottom-right, 5: center): '))
         =  or float(input('Watermark opacity (0-1 with 1 decimal place): '))

8.2 Exception handling improvements

In the section on handling exceptions, we can be more specific about the types of exceptions we catch and provide more user-friendly hint messages.

try:
    # existing code...
except FileNotFoundError:
    print('Error: The specified image directory does not exist.')
except PermissionError:
    print('Error: Permission denied to access the specified image directory.')
except Exception as e:
    print(f'An unexpected error occurred: {e}')

8.3 Code Structure Optimization

Consider modularizing some of the functionality to improve the readability and maintainability of the code. For example, the function to add a text watermark is separated into a separate method.

class WatermarkText():
    # existing code...

    def add_text_watermark(self, img):
        # existing code...

8.4 Logging

Consider adding logging to your program to record critical steps and error messages to make it easier to troubleshoot problems.

import logging

(level=)

class WatermarkText():
    # existing code...

    def add_text_watermark(self, img):
        try:
            # existing code...
            (f'Successfully added watermark to {img}')
        except Exception as e:
            (f'Error adding watermark to {img}: {e}')

8.5 Extended functionality

Consider adding more features to the program, such as support for different watermark colors, font sizes, and other options to make the program more flexible.

These improvements and suggestions will help improve the stability, ease of use and maintainability of the program.

Of course, we will continue to improve and refine your code. In this section, we will consider some further optimizations and improvements.

9. Optimize image format checking

When working with image files, you can optimize the way you check the image format. Use theThe resulting file extension may contain uppercase letters, which can be converted to lowercase to ensure a match.

if () == '.png':
    watermark_text.add_text_watermark(filepath)
else:
    print("Error: Image format is not supported. Please use PNG format.")

10. Increasing user interactivity

Consideration could be given to adding more user interactivity to the program, such as asking the user if they want to continue adding watermarks after successfully adding a watermark.

while True:
    try:
        # existing code...

        print('Watermark added successfully.')
        
        another = input('Do you want to add watermark to another image? (yes/no): ').lower()
        if another != 'yes':
            break
    except Exception as e:
        (f'Error: {e}')

This allows the user to choose whether or not to continue adding watermarks, improving the interactivity of the program.

11. Multi-threading

If you need to process a large number of images, consider using multithreading to speed up the process. This can be done with theModule Realization.

from  import ThreadPoolExecutor

# existing code...

if __name__ == "__main__":
    watermark_text = WatermarkText()
    try:
        file_list = (watermark_text.image_path) 

        with ThreadPoolExecutor() as executor:
            (watermark_text.add_text_watermark, [(watermark_text.image_path, file) for file in file_list])

        print('Batch watermarking completed.')
    except Exception as e:
        (f'Error: {e}')

This will allow multiple images to be processed at the same time, increasing processing speed.

12. Other optimization recommendations

  • Consider supporting more image formats than just PNG. you can use the Pillow library'sImage.register_open()Methods to register image openers in other formats.
  • If the watermark text is long, consider automatically resizing the text to fit the image.

to this article on the implementation of this Python image to add a watermark on this article, more related Python image to add a watermark content, please search for my previous posts or continue to browse the following related articles I hope that you will support me more in the future!