Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Rotate variations or keep exif info for JPEG #66

Open
codingjoe opened this issue Nov 11, 2015 · 10 comments
Open

Rotate variations or keep exif info for JPEG #66

codingjoe opened this issue Nov 11, 2015 · 10 comments

Comments

@codingjoe
Copy link
Owner

http://stackoverflow.com/a/11543365/649951

@baxeico
Copy link

baxeico commented Feb 5, 2016

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

@josefnorlin
Copy link

@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),
})

@josefnorlin
Copy link

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?

@codingjoe
Copy link
Owner Author

@baxeico you will need to import pil.Image ;)

@jckw
Copy link

jckw commented Sep 30, 2019

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.

@codingjoe
Copy link
Owner Author

@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?

@baxeico
Copy link

baxeico commented Oct 2, 2019

@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?

@jckw
Copy link

jckw commented Oct 2, 2019

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.

@codingjoe
Copy link
Owner Author

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.

@IgorCode
Copy link

IgorCode commented Feb 22, 2021

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

5 participants