diff --git a/modules/api/api.py b/modules/api/api.py index 4e6560826..5742e6e6e 100644 --- a/modules/api/api.py +++ b/modules/api/api.py @@ -86,6 +86,7 @@ def decode_base64_to_image(encoding): response = requests.get(encoding, timeout=30, headers=headers) try: image = Image.open(BytesIO(response.content)) + image = images.apply_exif_orientation(image) return image except Exception as e: raise HTTPException(status_code=500, detail="Invalid image url") from e @@ -94,6 +95,7 @@ def decode_base64_to_image(encoding): encoding = encoding.split(";")[1].split(",")[1] try: image = Image.open(BytesIO(base64.b64decode(encoding))) + image = images.apply_exif_orientation(image) return image except Exception as e: raise HTTPException(status_code=500, detail="Invalid encoded image") from e diff --git a/modules/images.py b/modules/images.py index b6f2358c3..1728ebc3e 100644 --- a/modules/images.py +++ b/modules/images.py @@ -797,3 +797,52 @@ def flatten(img, bgcolor): return img.convert('RGB') + +# https://www.exiv2.org/tags.html +_EXIF_ORIENT = 274 # exif 'Orientation' tag + +def apply_exif_orientation(image): + """ + Applies the exif orientation correctly. + + This code exists per the bug: + https://github.com/python-pillow/Pillow/issues/3973 + with the function `ImageOps.exif_transpose`. The Pillow source raises errors with + various methods, especially `tobytes` + + Function based on: + https://github.com/wkentaro/labelme/blob/v4.5.4/labelme/utils/image.py#L59 + https://github.com/python-pillow/Pillow/blob/7.1.2/src/PIL/ImageOps.py#L527 + + Args: + image (PIL.Image): a PIL image + + Returns: + (PIL.Image): the PIL image with exif orientation applied, if applicable + """ + if not hasattr(image, "getexif"): + return image + + try: + exif = image.getexif() + except Exception: # https://github.com/facebookresearch/detectron2/issues/1885 + exif = None + + if exif is None: + return image + + orientation = exif.get(_EXIF_ORIENT) + + method = { + 2: Image.FLIP_LEFT_RIGHT, + 3: Image.ROTATE_180, + 4: Image.FLIP_TOP_BOTTOM, + 5: Image.TRANSPOSE, + 6: Image.ROTATE_270, + 7: Image.TRANSVERSE, + 8: Image.ROTATE_90, + }.get(orientation) + + if method is not None: + return image.transpose(method) + return image