Hide keyboard shortcuts

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 

3 

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. 

9 

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""" 

14 

15# TODO: this is only used for the Image.ANTIALIAS constant. seems kind of trivial... 

16from PIL import Image 

17 

18 

19def flat(*nums): 

20 "Build a tuple of ints from float or integer arguments. Useful because PIL crop and resize require integer points." 

21 

22 return tuple(int(round(n)) for n in nums) 

23 

24 

25class Size(object): 

26 def __init__(self, pair): 

27 self.width = float(pair[0]) 

28 self.height = float(pair[1]) 

29 

30 @property 

31 def aspect_ratio(self): 

32 return self.width / self.height 

33 

34 @property 

35 def size(self): 

36 return flat(self.width, self.height) 

37 

38 

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 """ 

46 

47 original = Size(img.size) 

48 target = Size(size) 

49 

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 ) 

66 

67 return img.resize(target.size, Image.ANTIALIAS)