Python Image Compression and Conversion to WebP Format
In modern web development, image optimization is a key step to improve website loading speed and user experience. The WebP format, with its excellent compression efficiency and good image quality, is gradually becoming the mainstream image format. This article introduces how to use Python with the Pillow library to achieve batch image compression, add watermarks, and finally convert to WebP format.
Core Function Overview
We will expand based on a Python script imgtools.py
, which mainly includes the following functions:
- Add Text Watermark: Overlay a custom text watermark on the image, supporting stroke effects to enhance readability.
- Image Format Conversion and Compression: Convert common image formats (such as JPG, PNG, BMP) to WebP format and control compression quality.
- Batch Processing: Support processing all eligible images in the specified directory.
Key Library: Pillow
Pillow is a friendly fork of the Python Imaging Library (PIL), providing powerful image processing capabilities for Python. In our script, Pillow is used to open, manipulate, and save images.
from PIL import Image, ImageDraw, ImageFont
1. Add Text Watermark
Before sharing images, adding a watermark is a common means of copyright protection or brand promotion. The add_watermark
function implements this feature.
def add_watermark(
image: Image.Image,
watermark_text: str,
font_path: str,
font_size: int,
font_color: Tuple[int, int, int, int],
stroke_color: Tuple[int, int, int, int],
stroke_width: int = 1,
margin: int = 10
) -> Image.Image:
"""
Add a text watermark with stroke to the image.
Args:
image (Image.Image): Pillow Image object (should be in RGBA mode).
watermark_text (str): Watermark text content.
font_path (str): Path to the font file.
font_size (int): Font size.
font_color (Tuple[int, int, int, int]): Font color (R, G, B, Alpha).
stroke_color (Tuple[int, int, int, int]): Stroke color (R, G, B, Alpha).
stroke_width (int): Stroke width.
margin (int): Distance from the watermark to the image edge.
Returns:
Image.Image: Pillow Image object with watermark added.
"""
# Create a transparent watermark layer the same size as the original image
watermark_layer = Image.new("RGBA", image.size, (0, 0, 0, 0))
draw = ImageDraw.Draw(watermark_layer)
# Load font
try:
font = ImageFont.truetype(font_path, font_size)
except IOError:
print(f"Warning: Font '{font_path}' not found, using default font.")
font = ImageFont.load_default()
# Calculate watermark text size
bbox = draw.textbbox((0, 0), watermark_text, font=font)
text_width = bbox[2] - bbox[0]
text_height = bbox[3] - bbox[1]
# Calculate watermark position (bottom right corner)
x = image.width - text_width - margin
y = image.height - text_height - margin
# Draw stroke
if stroke_width > 0 and stroke_color:
for i in range(-stroke_width, stroke_width + 1):
for j in range(-stroke_width, stroke_width + 1):
if i != 0 or j != 0: # Avoid drawing at the center point repeatedly
draw.text((x + i, y + j), watermark_text, font=font, fill=stroke_color)
# Draw main text
draw.text((x, y), watermark_text, font=font, fill=font_color)
# Merge watermark layer onto the original image
return Image.alpha_composite(image, watermark_layer)
Main Steps:
- Create a transparent image layer (
watermark_layer
) the same size as the original image for drawing the watermark. - Load the specified font and size. If font loading fails, use Pillow's default font.
- Calculate the width and height of the watermark text to determine its position in the bottom right corner of the image (adjust logic as needed).
- Stroke Effect: Draw the text in the stroke color at multiple offset positions around the main text to achieve a stroke effect, enhancing visibility on different backgrounds.
- Draw the main text.
- Use
Image.alpha_composite
to overlay the transparent watermark layer onto the original image (the original image needs to be in "RGBA" mode).
2. Process a Single Image: Add Watermark, Convert and Save
The process_image_and_save
function is responsible for processing a single image, including opening the image, adding a watermark, converting to WebP format, and saving.
def process_image_and_save(
input_path: str,
output_path: str,
watermark_text: str,
font_path: str,
font_color: Tuple[int, int, int, int],
stroke_color: Tuple[int, int, int, int],
quality: int = 80
):
"""
Open image, add watermark, convert to WebP format and save.
"""
try:
# Open the original image and convert to RGBA mode to support transparency
with Image.open(input_path).convert("RGBA") as base_image:
width, height = base_image.size
# Dynamically calculate font size
font_size = int(min(width, height) * 0.04)
# Add watermark
watermarked_image = add_watermark(
image=base_image,
watermark_text=watermark_text,
font_path=font_path,
font_size=font_size,
font_color=font_color,
stroke_color=stroke_color
)
# Convert back to RGB mode for saving as WebP
final_image = watermarked_image.convert("RGB")
# Save as WebP
final_image.save(output_path, "webp", quality=quality, method=6)
print(f"Processed: {os.path.basename(input_path)} -> {os.path.basename(output_path)}")
except Exception as e:
print(f"Failed to process: {os.path.basename(input_path)}, error: {e}")
Core Process:
- Open Image: Use
Image.open(input_path)
to open the image, and ensure it is in RGBA mode via.convert("RGBA")
for correct transparency handling (especially for watermark transparency). - Dynamic Font Size: Dynamically calculate the watermark font size based on the image dimensions (the smaller of width and height), ensuring a relatively consistent visual effect on images of different sizes.
- Add Watermark: Call the previously introduced
add_watermark
function. - Convert Back to RGB: Although WebP format supports transparency, if the final goal is to reduce file size and transparency is not needed, or for broader compatibility, you can convert back to "RGB" mode. If you need to retain transparency, save directly in RGBA mode.
- Save as WebP: Use
final_image.save(output_path, "webp", quality=quality, method=6)
to save."webp"
: Specify the save format.quality
: Controls WebP compression quality, range 0-100, higher values mean better quality but larger files. Default is 80 in the script.method
: WebP encoding method, range 0-6. Higher values mean slower encoding but potentially better compression.method=6
is the slowest but offers the highest compression.
3. Batch Process Images
For a large number of images, manual processing one by one is obviously impractical. The batch_process_images
function implements the logic for batch processing.
def batch_process_images(
input_dir: str,
output_dir: str,
watermark_text: str,
font_path: str,
font_color: Tuple[int, int, int, int],
stroke_color: Tuple[int, int, int, int],
quality: int = 80,
exts: Tuple[str, ...] = ('.jpg', '.jpeg', '.png', '.bmp')
):
"""
Batch process all images in the specified directory.
"""
if not os.path.exists(output_dir):
os.makedirs(output_dir)
print(f"Created output directory: {output_dir}")
for filename in os.listdir(input_dir):
if filename.lower().endswith(exts): # Check file extension
input_path = os.path.join(input_dir, filename)
name, _ = os.path.splitext(filename)
output_path = os.path.join(output_dir, name + ".webp") # Output file name as .webp
process_image_and_save(
input_path=input_path,
output_path=output_path,
watermark_text=watermark_text,
font_path=font_path,
font_color=font_color,
stroke_color=stroke_color,
quality=quality
)
Working Method:
- Check if the output directory exists, create it if not.
- Traverse all files in the input directory (
input_dir
). - Use
filename.lower().endswith(exts)
to check if the file is one of the specified image types (default:.jpg
,.jpeg
,.png
,.bmp
). - For each eligible image, construct the input and output paths (output file extension changed to
.webp
). - Call
process_image_and_save
to process and save the image.
4. How to Use
The script provides a if __name__ == "__main__":
code block for direct execution and parameter configuration.
if __name__ == "__main__":
# --- Please configure your parameters here ---
# 1. Directory settings (it is recommended that input and output directories are different to avoid overwriting original images)
INPUT_DIRECTORY = "/Users/workspace/src/.vuepress/public/test" # Input image directory
OUTPUT_DIRECTORY = "/Users/workspace/src/.vuepress/public/test" # Output directory
# 2. Watermark content
WATERMARK_TEXT = "[email protected]"
# 3. Font path (please modify according to your OS)
# macOS: "/Library/Fonts/Arial.ttf"
# Windows: "C:/Windows/Fonts/arial.ttf"
# Linux: "/usr/share/fonts/truetype/liberation/LiberationSans-Regular.ttf"
FONT_PATH = "/Library/Fonts/Arial Unicode.ttf"
# 4. Color configuration (R, G, B, Alpha), Alpha range 0-255, 0 is fully transparent, 255 is opaque
FONT_COLOR = (255, 255, 255, 180) # White semi-transparent text
STROKE_COLOR = (0, 0, 0, 180) # Black semi-transparent stroke
# 5. WebP compression quality (0-100, recommended 75-85)
WEBP_QUALITY = 80
# --- End of configuration ---
# Execute batch processing
batch_process_images(
input_dir=INPUT_DIRECTORY,
output_dir=OUTPUT_DIRECTORY,
watermark_text=WATERMARK_TEXT,
font_path=FONT_PATH,
font_color=FONT_COLOR,
stroke_color=STROKE_COLOR,
quality=WEBP_QUALITY
)
Configuration Instructions:
INPUT_DIRECTORY
: Folder path where the original images are stored.OUTPUT_DIRECTORY
: Folder path to save processed images. It is strongly recommended to set a different path from the input directory to avoid accidentally overwriting original images.WATERMARK_TEXT
: The text to be added as a watermark.FONT_PATH
: The full path to the font file used for the watermark text. The script provides example paths for common operating systems.FONT_COLOR
: Watermark text color, format is(R, G, B, Alpha)
. Alpha controls transparency.STROKE_COLOR
: Watermark text stroke color, same format as above.WEBP_QUALITY
: WebP image compression quality.
After modifying these parameters, simply run this Python script to start batch processing images.
Summary
With Python and the Pillow library, we can easily automate image processing workflows, including adding watermarks, format conversion, and compression. Converting images to WebP format not only effectively reduces file size, but also speeds up website loading and improves user experience while maintaining good visual effects. This is especially important for websites with a large amount of image content.