Skip to content

Instantly share code, notes, and snippets.

@Saigesp
Created August 11, 2023 19:05
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Saigesp/33023f3252cc99a87352cc5493a9531a to your computer and use it in GitHub Desktop.
Save Saigesp/33023f3252cc99a87352cc5493a9531a to your computer and use it in GitHub Desktop.
Python responsive images
# -*- coding: utf-8 -*-
# Create multiple responsive images to generate a srcset
#
# Usage example:
# b64_image = base64.b64decode(b64_image.split(",")[1]) # from base64-encoded image
# file_name = "lorem.ipsum.jpeg"
# srcset_results = []
# for img in create_srcset_images(BytesIO(b64_image), widths):
# srcset_name = f'{".".join(file_name.split(".")[:-1])}-{img["width"]}w.{img["format"]}'
# result = upload_file_to_external_service(img["image"], srcset_name) # <--- it must return a URL
# srcset_results.append(f'{result} {img["width"]}w')
# srcset = ", ".join(srcset_results)
#
# Code adapted from https://github.com/mccarthysean/make-responsive-images/
from io import BytesIO
from PIL import Image
def get_srcset_sizes(widths: list, img_width: int, img_height: int) -> list:
"""
Get srcset proporcional sizes and filter out duplicates sizes
and larger than original
:param widths: list of widths to to create the srcset
:param img_width: image original width
:param img_height: image original height
:return: list of proportinal sizes like [(400, 267), (800, 533), (1200, 800)]
"""
sizes_set = set()
for width in widths:
width = min(width, img_width)
ratio = width / float(img_width)
width = int(img_width * ratio + 0.5)
height = int(img_height * ratio + 0.5)
sizes_set.add((width, height))
return sorted(sizes_set)
def create_srcset_images(
file: BytesIO,
widths: list = [1200, 800, 600, 400],
output_format: str = "webp",
output_quality: int = 100,
resize_method: int = Image.NEAREST,
) -> list:
"""
Resize the image to the widths specified
:param file: file to resize
:param output_format: output files format. Can be jpg or webp
:param output_quality: compression files output_quality
:param resize_method: resize method. Can be Image.NEAREST or Image.ANTIALIAS
:return: list of dicts that contain images with their sizes
"""
output_format = output_format.lower()
if output_format not in ("jpg", "webp", "png"):
raise TypeError('output_format must be either "jpg", "webp" or "png"')
if output_quality > 100 or output_quality < 0:
raise TypeError("output_quality must be between 0 and 100")
image = Image.open(file).convert("RGB") # Convert to RGB so we can save as .webp
sizes = get_srcset_sizes(widths, image.width, image.height)
result = []
for (width, height) in sizes:
if (width, height) == (image.width, image.height):
new_image = image
else:
new_image = image.resize((width, height), resize_method)
in_mem_file = BytesIO()
new_image.save(in_mem_file, quality=output_quality, format=output_format)
in_mem_file.seek(0)
result.append(
{
"image": in_mem_file,
"width": width,
"height": height,
"format": output_format,
}
)
return result
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment