django-stdimage
django-stdimage copied to clipboard
Rotate variations or keep exif info for JPEG
http://stackoverflow.com/a/11543365/649951
I'm using the following function as the callable in render_variations
keyword argument of StdImageField
. Maybe this can be useful for this issue.
from django.core.files.base import ContentFile
from stdimage.utils import render_variations
def resize_and_autorotate(file_name, variations, storage):
with storage.open(file_name) as f:
with Image.open(f) as image:
file_format = image.format
exif = image._getexif()
# if image has exif data about orientation, let's rotate it
orientation_key = 274 # cf ExifTags
if exif and orientation_key in exif:
orientation = exif[orientation_key]
rotate_values = {
3: Image.ROTATE_180,
6: Image.ROTATE_270,
8: Image.ROTATE_90
}
if orientation in rotate_values:
image = image.transpose(rotate_values[orientation])
with BytesIO() as file_buffer:
image.save(file_buffer, file_format)
f = ContentFile(file_buffer.getvalue())
storage.delete(file_name)
storage.save(file_name, f)
# render stdimage variations
render_variations(file_name, variations, replace=True, storage=storage)
return False # prevent default rendering
@baxeico How did you call your render_variations function? I've tried this to no avail:
img = StdImageField(upload_to=UploadToUUID(path='posters'), blank=True, render_variations ,variations={
'app': (179, 260),
'thumbnail': (207, 299),
'thumbnail_2x': (413, 598),
'thumbnail_4x': (826, 1196),
'full': (427, 616),
'full_2x': (854, 1232),
})
Solved it.
Put your function in the models.py and called it with render_variations=resize_and_autorotate in the img declaration
img = StdImageField(upload_to=UploadToUUID(path='posters'), blank=True, render_variations=resize_and_autorotate ,variations={
'app': (179, 260),
'thumbnail': (207, 299),
'thumbnail_2x': (413, 598),
'thumbnail_4x': (826, 1196),
'full': (427, 616),
'full_2x': (854, 1232),
})
But then I get an error, claiming: NameError name 'Image' is not defined
What happened there? Am I implementing this wrong @baxeico?
@baxeico you will need to import pil.Image
;)
I know this is an old issue.. but is there a chance this will be implemented into the library? I would like to avoid using a custom callable everywhere where I've used a StdImageField.
@jckw it's not implemented yet, but we have the JPEGField
now. Sooo I guess this would be a really cool feature to add. Would you mind creating the PR yourself?
@jckw it's not implemented yet, but we have the
JPEGField
now. Sooo I guess this would be a really cool feature to add. Would you mind creating the PR yourself?
@codingjoe I think I should be able to have a look on this and create a PR. I briefly looked at the code and I think I could override the render_variations
method on JPEGField
and rotating the image before calling the parent render_variations
. What do you think?
It would be amazing if a "check for JPEG deal with EXIF tags" could be option on the regular ImageField too!
I assume we could just use whatever validator JPEG fields use to check that JPEGs are being used.
I think it would be a great feature for both fields, but I would start with the JPEGField
and take it from there.
@baxeico yes, be we should not rotate the original image, or at least not save a rotated version. It should remain unmodified. However, we can rotate it in memory before passing it to the render_variations
function. We should also consider how to keep the EXIT information. I believe currently we drop in some cases for smaller variations.
Updated @baxeico's solution with exif_transpose
:
def render_variations_and_autorotate(file_name, variations, storage):
with storage.open(file_name) as f:
with Image.open(f) as image:
transposed_image = ImageOps.exif_transpose(image)
if not getattr(storage, 'file_overwrite', False):
storage.delete(file_name)
with BytesIO() as file_buffer:
transposed_image.save(file_buffer, format=image.format)
f = ContentFile(file_buffer.getvalue())
storage.save(file_name, f)
# Allow default render variations
return True