import os import sys import traceback from PIL import Image, ImageOps from multiprocessing import Process # style tuples: max x, max y, rotate ok) # rotate ok means x and y maxes can be swapped if it fits the image's aspect ratio better THUMB_STYLES = {"tiny": (80, 80, False), "small": (100, 100, False), "feed": (250, 250, False), "preview": (1024, 768, True), "big": (2048, 1536, True)} def thumb_path(style_name, uuid): return os.path.join("thumbs", style_name, uuid[0:2], "{}.jpg".format(uuid)) def image_style(image, style_name, orientation): """ Given an input PIL Image object, return a scaled/cropped image object of style_name """ thumb_width, thumb_height, flip_ok = THUMB_STYLES[style_name] i_width, i_height = image.size im_is_rotated = orientation % 2 != 0 or i_height > i_width if im_is_rotated and flip_ok: thumb_width, thumb_height = thumb_height, thumb_width # prevents scale-up (why do i want this?) thumb_width = min(thumb_width, i_width) if i_width > 0 else thumb_width thumb_height = min(thumb_height, i_height) if i_height > 0 else thumb_height image = image.rotate(90 * orientation, expand=True) return ImageOps.fit(image, (thumb_width, thumb_height), Image.ANTIALIAS) def image_file_style(src_path, output_path, style_name, orientation): """ Given an input and output image paths, create a scaled/cropped image file of style_name """ image = Image.open(src_path) if image.mode != "RGB": image = image.convert("RGB") output = image_style(image, style_name, orientation) output.save(output_path, 'JPEG') def _image_file_style_process_worker(src_path, output_path, style_name, orientation): try: image_file_style(src_path, output_path, style_name, orientation) except Exception: traceback.print_exc() if os.path.exists(output_path): os.unlink(output_path) sys.exit(1) def image_file_style_process(src_path, output_path, style_name, orientation): """ Same as image_file_style but the image manipulation is done in a background process """ p = Process(target=_image_file_style_process_worker, args=(src_path, output_path, style_name, orientation)) p.start() p.join() if p.exitcode != 0: return False return True