Skip to content

Instantly share code, notes, and snippets.

@MarkParker5
Last active May 8, 2024 17:57
Show Gist options
  • Save MarkParker5/2f08d1e9a858363273e03ea664a964f4 to your computer and use it in GitHub Desktop.
Save MarkParker5/2f08d1e9a858363273e03ea664a964f4 to your computer and use it in GitHub Desktop.
Minimize images for web: resize to max width and convert to webp
from PIL import Image
import os
def add_transparent_border(img: Image.Image) -> Image.Image:
border_size = 50
new_size = (img.width + 2 * border_size, img.height + 2 * border_size)
new_img = Image.new('RGBA', new_size)
new_img.paste(img, (border_size, border_size))
return new_img
def add_background(img: Image.Image) -> Image.Image:
bg_color = (28, 28, 28)
if img.mode != 'RGBA':
img = img.convert('RGBA')
background = Image.new('RGBA', img.size, bg_color + (255,))
combined = Image.alpha_composite(background, img)
return combined.convert('RGB')
def resize_image(img: Image.Image, max_width: int) -> Image.Image:
if img.width > max_width:
scale_ratio = max_width / img.width
new_height = int(img.height * scale_ratio)
img = img.resize((max_width, new_height), Image.LANCZOS)
return img
def convert_to_webp(img: Image.Image, output_path: str) -> None:
img.save(output_path, 'WEBP')
def print_image_stats(name: str, before_size: int, after_size: int, before_dims: tuple, after_dims: tuple) -> None:
before_size_mb = before_size / (1024 * 1024)
after_size_mb = after_size / (1024 * 1024)
pixel_reduction = int((after_dims[0] * after_dims[1]) / (before_dims[0] * before_dims[1]) * 100)
print(f'\n{name}')
print(f'Before: {before_dims[0]}x{before_dims[1]}, {before_size_mb:.2f} MB')
print(f'After: {after_dims[0]}x{after_dims[1]}, {after_size_mb:.2f} MB')
print(f'Size reduction: {100 - pixel_reduction}%')
def process_image(img_path: str, directory: str, max_width: int) -> None:
before_size = os.path.getsize(img_path)
with Image.open(img_path) as img:
before_dims = img.size
img = add_transparent_border(img)
img = add_background(img)
img = resize_image(img, max_width)
after_dims = img.size
output_path = img_path.rsplit('.', 1)[0] + '.webp'
convert_to_webp(img, output_path)
after_size = os.path.getsize(output_path)
print_image_stats(img_path, before_size, after_size, before_dims, after_dims)
os.remove(img_path)
def resize_and_convert_directory(directory: str, max_width: int) -> None:
total_before_size = 0
total_after_size = 0
num_images = 0
for filename in os.listdir(directory):
if filename.lower().endswith(('.png', '.jpg', '.jpeg')):
num_images += 1
img_path = os.path.join(directory, filename)
before_size = os.path.getsize(img_path)
total_before_size += before_size
process_image(img_path, directory, max_width)
new_filename = filename.rsplit('.', 1)[0] + '.webp'
new_path = os.path.join(directory, new_filename)
after_size = os.path.getsize(new_path)
total_after_size += after_size
if num_images > 0:
avg_reduction = int((total_after_size / total_before_size) * 100)
total_before_mb = total_before_size / (1024 * 1024)
total_after_mb = total_after_size / (1024 * 1024)
print(f'\n--------------\nTotal Before: {total_before_mb:.2f} MB, Total After: {total_after_mb:.2f} MB')
print(f'Average Size Reduction: {100 - avg_reduction}%')
print(f'Total Images Processed: {num_images}')
def adapt_bg_directory(directory: str) -> None:
for filename in os.listdir(directory):
if filename.lower().endswith(('.png', '.jpg', '.jpeg', '.webp')):
img_path = os.path.join(directory, filename)
with Image.open(img_path) as img:
img = add_transparent_border(img)
img = add_background(img)
img.save(img_path)
if __name__ == '__main__':
resize_and_convert_directory('public/images/articles/welcome-to-majordom', 1010)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment