Skip to content

Instantly share code, notes, and snippets.

@alanhamlett
Last active September 24, 2022 08:07
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 alanhamlett/aa5c0ac69c49a47e8450dbf5a77159df to your computer and use it in GitHub Desktop.
Save alanhamlett/aa5c0ac69c49a47e8450dbf5a77159df to your computer and use it in GitHub Desktop.
Utility functions to resize, make square, and optimize (pngquant) images using Python
from io import BytesIO
from subprocess import PIPE, Popen
from typing import Optional
from PIL import Image, ImageFile, UnidentifiedImageError
from PIL.Image import DecompressionBombError
SUPPORTED_IMAGE_FORMATS = ["PNG", "JPEG", "JPEG2000"]
def resize_png(image_data: bytes = None, size: int = None) -> Optional[bytes]:
if not image_data:
return None
if not size:
return None
ImageFile.LOAD_TRUNCATED_IMAGES = True
with BytesIO(image_data) as fh:
try:
image = Image.open(fh, formats=SUPPORTED_IMAGE_FORMATS)
except (UnidentifiedImageError, DecompressionBombError):
return None
if image.mode != "RGB":
image = image.convert("RGB")
with BytesIO() as out:
image.resize((size, size)).save(out, format="png")
return out.getvalue()
def convert_image_to_png(image_data: bytes = None) -> Optional[bytes]:
if not image_data:
return None
ImageFile.LOAD_TRUNCATED_IMAGES = True
with BytesIO(image_data) as fh:
try:
image = Image.open(fh, formats=SUPPORTED_IMAGE_FORMATS)
except (UnidentifiedImageError, DecompressionBombError):
return None
if image.mode != "RGB":
image = image.convert("RGB")
with BytesIO() as out:
try:
image.save(out, format="png")
except OSError:
return None
return out.getvalue()
def make_image_square(image_data: bytes = None) -> Optional[bytes]:
if not image_data:
return None
ImageFile.LOAD_TRUNCATED_IMAGES = True
with BytesIO(image_data) as fh:
try:
image = Image.open(fh, formats=SUPPORTED_IMAGE_FORMATS)
except (UnidentifiedImageError, DecompressionBombError):
return None
width, height = image.size
if width == height:
return image_data
left, upper, right, lower = 0, 0, width, height
diff = abs(width - height)
if width < height:
upper += int(diff / 2)
lower -= int(diff / 2)
if diff % 2 != 0:
lower -= 1
else:
left += int(diff / 2)
right -= int(diff / 2)
if diff % 2 != 0:
right -= 1
with BytesIO() as out:
image.crop((left, upper, right, lower)).save(out, format="png")
return out.getvalue()
def optimize_png(image_data: bytes = None) -> Optional[bytes]:
if not image_data:
return None
try:
proc = Popen(["pngquant", "--strip", "-"], stdin=PIPE, stdout=PIPE, stderr=PIPE)
(stdout, stderr) = proc.communicate(input=image_data)
return stdout
except:
return image_data
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment