diff --git a/modules/api/api.py b/modules/api/api.py index b3d74e513a3..3108247ce7d 100644 --- a/modules/api/api.py +++ b/modules/api/api.py @@ -74,6 +74,55 @@ def verify_url(url): return True +# 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 + def decode_base64_to_image(encoding): if encoding.startswith("http://") or encoding.startswith("https://"): if not opts.api_enable_requests: @@ -86,6 +135,7 @@ def decode_base64_to_image(encoding): response = requests.get(encoding, timeout=30, headers=headers) try: image = Image.open(BytesIO(response.content)) + image = _apply_exif_orientation(image) return image except Exception as e: raise HTTPException(status_code=500, detail="Invalid image url") from e @@ -94,6 +144,7 @@ def decode_base64_to_image(encoding): encoding = encoding.split(";")[1].split(",")[1] try: image = Image.open(BytesIO(base64.b64decode(encoding))) + image = _apply_exif_orientation(image) return image except Exception as e: raise HTTPException(status_code=500, detail="Invalid encoded image") from e