faster image resizing and smaller results

This commit is contained in:
thomas girod 2024-10-02 23:15:37 +02:00
parent 655d72a2b1
commit d0f17bd41a
2 changed files with 32 additions and 21 deletions

View File

@ -82,40 +82,47 @@ def get_semester_code(d: Optional[date] = None) -> str:
return "P" + str(start.year)[-2:] return "P" + str(start.year)[-2:]
def resize_image(im: Image, edge: int, img_format: str): def resize_image(
"""Resize an image to fit the given edge length and format.""" im: Image, edge: int, img_format: str, *, optimize: bool = True
) -> ContentFile:
"""Resize an image to fit the given edge length and format.
Args:
im: the image to resize
edge: the length that the greater side of the resized image should have
img_format: the target format of the image ("JPEG", "PNG", "WEBP"...)
optimize: Should the resized image be optimized ?
"""
(w, h) = im.size (w, h) = im.size
ratio = edge / max(w, h) ratio = edge / max(w, h)
(width, height) = int(w * ratio), int(h * ratio) (width, height) = int(w * ratio), int(h * ratio)
return resize_image_explicit(im, (width, height), img_format) return resize_image_explicit(im, (width, height), img_format, optimize=optimize)
def resize_image_explicit(im: Image, size: tuple[int, int], img_format: str): def resize_image_explicit(
"""Resize an image to the given size and format.""" im: Image, size: tuple[int, int], img_format: str, *, optimize: bool = True
) -> ContentFile:
"""Resize an image to the given size and format.
Args:
im: the image to resize
size: the target dimension, as a [width, height] tuple
img_format: the target format of the image ("JPEG", "PNG", "WEBP"...)
optimize: Should the resized image be optimized ?
"""
img_format = img_format.upper() img_format = img_format.upper()
content = BytesIO() content = BytesIO()
# use the lanczos filter for antialiasing and discard the alpha channel # use the lanczos filter for antialiasing and discard the alpha channel
im = im.resize((size[0], size[1]), Resampling.LANCZOS) if size != im.size:
im = im.resize((size[0], size[1]), Resampling.LANCZOS)
if img_format == "JPEG": if img_format == "JPEG":
# converting an image with an alpha channel to jpeg would cause a crash # converting an image with an alpha channel to jpeg would cause a crash
im = im.convert("RGB") im = im.convert("RGB")
try: try:
im.save( im.save(fp=content, format=img_format, optimize=optimize)
fp=content,
format=img_format,
quality=90,
optimize=True,
progressive=True,
)
except IOError: except IOError:
PIL.ImageFile.MAXBLOCK = im.size[0] * im.size[1] PIL.ImageFile.MAXBLOCK = im.size[0] * im.size[1]
im.save( im.save(fp=content, format=img_format, optimize=optimize)
fp=content,
format=img_format,
quality=90,
optimize=True,
progressive=True,
)
return ContentFile(content.getvalue()) return ContentFile(content.getvalue())

View File

@ -117,7 +117,11 @@ class Picture(SasFile):
# meant to be shown on the website, but rather to keep the real image # meant to be shown on the website, but rather to keep the real image
# for less frequent cases (like downloading the pictures of an user) # for less frequent cases (like downloading the pictures of an user)
extension = self.mime_type.split("/")[-1] extension = self.mime_type.split("/")[-1]
file = resize_image(im, max(im.size), extension) # the HD version of the image doesn't need to be optimized, because :
# - it isn't frequently queried
# - optimizing large images takes a lot time, which greatly hinders the UX
# - photographers usually already optimize their images
file = resize_image(im, max(im.size), extension, optimize=False)
thumb = resize_image(im, 200, "webp") thumb = resize_image(im, 200, "webp")
compressed = resize_image(im, 1200, "webp") compressed = resize_image(im, 1200, "webp")
if overwrite: if overwrite: