Hot-keys on this page
r m x p toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
1"""
2Taken from https://gist.github.com/olooney/1601455
4PIL's Image.thumbnail() returns an image that fits inside of a given size (preserving aspect ratios)
5but the size of the actual image will vary and is certainly not guaranteed to be the requested size.
6This is often inconvenient since the size of the returned thumbnail cannot be predicted. The django-thumbs
7library solves this for square thumbnails by cropping the image to a square and then resizing it. However,
8this only works for exact squares.
10This function generalizes that approach to work for thumbnails of any aspect ratio. The returned thumbnail
11is always exactly the requested size, and edges (left/right or top/bottom) are cropped off to adjust to
12make sure the thumbnail will be the right size without distorting the image.
13"""
15# TODO: this is only used for the Image.ANTIALIAS constant. seems kind of trivial...
16from PIL import Image
19def flat(*nums):
20 "Build a tuple of ints from float or integer arguments. Useful because PIL crop and resize require integer points."
22 return tuple(int(round(n)) for n in nums)
25class Size(object):
26 def __init__(self, pair):
27 self.width = float(pair[0])
28 self.height = float(pair[1])
30 @property
31 def aspect_ratio(self):
32 return self.width / self.height
34 @property
35 def size(self):
36 return flat(self.width, self.height)
39def cropped_thumbnail(img, size):
40 """
41 Builds a thumbnail by cropping out a maximal region from the center of the original with
42 the same aspect ratio as the target size, and then resizing. The result is a thumbnail which is
43 always EXACTLY the requested size and with no aspect ratio distortion (although two edges, either
44 top/bottom or left/right depending whether the image is too tall or too wide, may be trimmed off.)
45 """
47 original = Size(img.size)
48 target = Size(size)
50 if target.aspect_ratio > original.aspect_ratio:
51 # image is too tall: take some off the top and bottom
52 scale_factor = target.width / original.width
53 crop_size = Size((original.width, target.height / scale_factor))
54 top_cut_line = (original.height - crop_size.height) / 2
55 img = img.crop(
56 flat(0, top_cut_line, crop_size.width, top_cut_line + crop_size.height,)
57 )
58 elif target.aspect_ratio < original.aspect_ratio:
59 # image is too wide: take some off the sides
60 scale_factor = target.height / original.height
61 crop_size = Size((target.width / scale_factor, original.height))
62 side_cut_line = (original.width - crop_size.width) / 2
63 img = img.crop(
64 flat(side_cut_line, 0, side_cut_line + crop_size.width, crop_size.height,)
65 )
67 return img.resize(target.size, Image.ANTIALIAS)