bgneal@265
|
1 """
|
bgneal@265
|
2 This file contains common utility functions for manipulating images for
|
bgneal@265
|
3 the rest of the applications in the project.
|
bgneal@265
|
4 """
|
bgneal@265
|
5 from PIL import ImageFile
|
bgneal@265
|
6 from PIL import Image
|
bgneal@265
|
7
|
bgneal@265
|
8
|
bgneal@265
|
9 def parse_image(file):
|
bgneal@265
|
10 """
|
bgneal@265
|
11 Returns a PIL Image from the supplied Django file object.
|
bgneal@265
|
12 Throws IOError if the file does not parse as an image file or some other
|
bgneal@265
|
13 I/O error occurred.
|
bgneal@265
|
14
|
bgneal@265
|
15 """
|
bgneal@265
|
16 parser = ImageFile.Parser()
|
bgneal@265
|
17 for chunk in file.chunks():
|
bgneal@265
|
18 parser.feed(chunk)
|
bgneal@265
|
19 image = parser.close()
|
bgneal@265
|
20 return image
|
bgneal@265
|
21
|
bgneal@265
|
22
|
bgneal@265
|
23 def downscale_image_square(image, size):
|
bgneal@265
|
24 """
|
bgneal@265
|
25 Scale an image to the square dimensions given by size (in pixels).
|
bgneal@265
|
26 The new image is returned.
|
bgneal@265
|
27 If the image is already smaller than (size, size) then no scaling
|
bgneal@265
|
28 is performed and the image is returned unchanged.
|
bgneal@265
|
29
|
bgneal@265
|
30 """
|
bgneal@265
|
31 # don't upscale
|
bgneal@265
|
32 if (size, size) >= image.size:
|
bgneal@265
|
33 return image
|
bgneal@265
|
34
|
bgneal@265
|
35 (w, h) = image.size
|
bgneal@265
|
36 if w > h:
|
bgneal@265
|
37 diff = (w - h) / 2
|
bgneal@265
|
38 image = image.crop((diff, 0, w - diff, h))
|
bgneal@265
|
39 elif h > w:
|
bgneal@265
|
40 diff = (h - w) / 2
|
bgneal@265
|
41 image = image.crop((0, diff, w, h - diff))
|
bgneal@265
|
42 image = image.resize((size, size), Image.ANTIALIAS)
|
bgneal@265
|
43 return image
|
bgneal@837
|
44
|
bgneal@837
|
45
|
bgneal@837
|
46 # Various image transformation functions:
|
bgneal@837
|
47 def flip_horizontal(im):
|
bgneal@837
|
48 return im.transpose(Image.FLIP_LEFT_RIGHT)
|
bgneal@837
|
49
|
bgneal@837
|
50 def flip_vertical(im):
|
bgneal@837
|
51 return im.transpose(Image.FLIP_TOP_BOTTOM)
|
bgneal@837
|
52
|
bgneal@837
|
53 def rotate_180(im):
|
bgneal@837
|
54 return im.transpose(Image.ROTATE_180)
|
bgneal@837
|
55
|
bgneal@837
|
56 def rotate_90(im):
|
bgneal@837
|
57 return im.transpose(Image.ROTATE_90)
|
bgneal@837
|
58
|
bgneal@837
|
59 def rotate_270(im):
|
bgneal@837
|
60 return im.transpose(Image.ROTATE_270)
|
bgneal@837
|
61
|
bgneal@837
|
62 def transpose(im):
|
bgneal@837
|
63 return rotate_90(flip_horizontal(im))
|
bgneal@837
|
64
|
bgneal@837
|
65 def transverse(im):
|
bgneal@837
|
66 return rotate_90(flip_vertical(im))
|
bgneal@837
|
67
|
bgneal@837
|
68 # From http://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/EXIF.html
|
bgneal@837
|
69 # EXIF Orientation tag values:
|
bgneal@837
|
70 # 1 = Horizontal (normal)
|
bgneal@837
|
71 # 2 = Mirror horizontal
|
bgneal@837
|
72 # 3 = Rotate 180
|
bgneal@837
|
73 # 4 = Mirror vertical
|
bgneal@837
|
74 # 5 = Mirror horizontal and rotate 270 CW
|
bgneal@837
|
75 # 6 = Rotate 90 CW
|
bgneal@837
|
76 # 7 = Mirror horizontal and rotate 90 CW
|
bgneal@837
|
77 # 8 = Rotate 270 CW
|
bgneal@837
|
78
|
bgneal@837
|
79 ORIENT_FUNCS = {
|
bgneal@837
|
80 2: flip_horizontal,
|
bgneal@837
|
81 3: rotate_180,
|
bgneal@837
|
82 4: flip_vertical,
|
bgneal@837
|
83 5: transpose,
|
bgneal@837
|
84 6: rotate_270,
|
bgneal@837
|
85 7: transverse,
|
bgneal@837
|
86 8: rotate_90,
|
bgneal@837
|
87 }
|
bgneal@837
|
88
|
bgneal@837
|
89 ORIENT_TAG = 0x112
|
bgneal@837
|
90
|
bgneal@837
|
91
|
bgneal@837
|
92 def orient_image(im):
|
bgneal@837
|
93 """Transforms the given image according to embedded EXIF data.
|
bgneal@837
|
94
|
bgneal@837
|
95 The image instance, im, should be a PIL Image.
|
bgneal@837
|
96 If there is EXIF information for the image, and the orientation tag
|
bgneal@837
|
97 indicates that the image should be transformed, perform the transformation.
|
bgneal@837
|
98
|
bgneal@837
|
99 Returns a tuple of the form (flag, image) where flag is True if the image
|
bgneal@837
|
100 was oriented and False otherwise. image is either a new transformed image or
|
bgneal@837
|
101 the original image instance.
|
bgneal@837
|
102
|
bgneal@837
|
103 """
|
bgneal@837
|
104 if hasattr(im, '_getexif'):
|
bgneal@837
|
105 exif = im._getexif()
|
bgneal@837
|
106 if exif and ORIENT_TAG in exif:
|
bgneal@839
|
107 orientation = exif[ORIENT_TAG]
|
bgneal@839
|
108 func = ORIENT_FUNCS.get(orientation)
|
bgneal@839
|
109 if func:
|
bgneal@839
|
110 return (True, func(im))
|
bgneal@837
|
111
|
bgneal@837
|
112 return (False, im)
|