Use Orientation EXIF tag in photo to determine orientation on upload

parent c9a53c76
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
......@@ -2,17 +2,29 @@ import hashlib
import os
import random
from PIL.JpegImagePlugin import JpegImageFile
from django.conf import settings
from django.db import models
from django.urls import reverse
from django.utils.functional import cached_property
from django.utils.translation import ugettext_lazy as _
from PIL import Image
from PIL import Image, ExifTags
from utils.translation import ModelTranslateMeta, MultilingualField
COVER_FILENAME = 'cover.jpg'
1: 0,
2: 0,
3: 180,
4: 180,
5: 90,
6: 90,
7: 270,
8: 270,
def photo_uploadto(instance, filename):
num = instance.album.photo_set.count()
......@@ -21,7 +33,20 @@ def photo_uploadto(instance, filename):
return os.path.join(Album.photosdir, instance.album.dirname, new_filename)
def determine_rotation(pil_image):
if isinstance(pil_image, JpegImageFile) and pil_image._getexif():
exif = {
ExifTags.TAGS[k]: v
for k, v in pil_image._getexif().items()
if k in ExifTags.TAGS
if exif.get('Orientation'):
return EXIF_ORIENTATION[exif.get('Orientation')]
return 0
class Photo(models.Model):
album = models.ForeignKey(
......@@ -61,11 +86,14 @@ class Photo(models.Model):
def save(self, *args, **kwargs):
super(Photo, self).save(*args, **kwargs)
super().save(*args, **kwargs)
if self._orig_file != self.file.path:
image_path = self.file.path
image =
self.rotation = determine_rotation(image)
# Image.thumbnail does not upscale an image that is smaller
image.thumbnail(settings.PHOTO_UPLOAD_SIZE, Image.ANTIALIAS), "JPEG")
......@@ -77,6 +105,9 @@ class Photo(models.Model):
self._digest = hash_sha1.hexdigest()
# Save again, to update changes in digest and rotation
super().save(*args, **kwargs)
class Meta:
ordering = ('file', )
......@@ -143,7 +174,7 @@ class Album(models.Model, metaclass=ModelTranslateMeta):
# dirname is only set for new objects, to avoid ever changing it
if is None:
self.dirname = self.slug
super(Album, self).save(*args, **kwargs)
super().save(*args, **kwargs)
def access_token(self):
from zipfile import ZipFile
from io import BytesIO
from zipfile import ZipFile
from PIL import Image
from django.test import Client, TestCase
from members.models import Member
from photos.models import Photo, Album
from .models import Photo, Album, determine_rotation
def create_zip(photos):
......@@ -16,6 +17,27 @@ def create_zip(photos):
return output_file
class PhotoRotationTest(TestCase):
fixtures = ['members.json']
def setUpTestData(cls):
cls.member = Member.objects.filter(last_name="Wiggers").first()
def setUp(self):
self.client = Client()
def test_rotation_detection(self):
orientations = [0, 0, 180, 180, 90, 90, 270, 270]
for i in range(1, 9):
with self.subTest(orentation=i):
with open('photos/fixtures/poker_{}.jpg'.format(i), 'rb') as f:
rot = determine_rotation(
self.assertEqual(orientations[i - 1], rot)
class AlbumUploadTest(TestCase):
"""Tests album uploads in the admin."""
......@@ -102,3 +124,15 @@ class AlbumUploadTest(TestCase):
self.assertEqual(Album.objects.all().count(), 1)
self.assertEqual(Photo.objects.all().count(), 2)
def test_album_upload_rotated_photo_in_album(self):
output_file = create_zip(["photos/fixtures/rotated_janbeleid.jpg"])'/admin/photos/album/add/',
{"title_nl": "test album",
"title_en": "test album",
"date": "2017-04-12",
"slug": "2017-04-12-test-album",
"album_archive": output_file},
self.assertEqual(Photo.objects.first().rotation, 90)
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment