annotate core/images/utils.py @ 1168:90e8cc6eff77

Fix ambiguous date errors in forum feeds.
author Brian Neal <bgneal@gmail.com>
date Sun, 05 Nov 2017 14:59:26 -0600
parents 4f265f61874b
children
rev   line source
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@891 105 try:
bgneal@891 106 exif = im._getexif()
bgneal@891 107 except IndexError:
bgneal@891 108 # Work around issue seen in Pillow
bgneal@891 109 # https://github.com/python-pillow/Pillow/issues/518
bgneal@891 110 exif = None
bgneal@891 111
bgneal@837 112 if exif and ORIENT_TAG in exif:
bgneal@839 113 orientation = exif[ORIENT_TAG]
bgneal@839 114 func = ORIENT_FUNCS.get(orientation)
bgneal@839 115 if func:
bgneal@839 116 return (True, func(im))
bgneal@837 117
bgneal@837 118 return (False, im)