diff --git a/src/datasets/features/image.py b/src/datasets/features/image.py index 7555e160a2d..8d573bb1021 100644 --- a/src/datasets/features/image.py +++ b/src/datasets/features/image.py @@ -154,6 +154,7 @@ def decode_example(self, value: dict, token_per_repo_id=None) -> "PIL.Image.Imag if config.PIL_AVAILABLE: import PIL.Image + import PIL.ImageOps else: raise ImportError("To support decoding images, please install 'Pillow'.") @@ -186,6 +187,8 @@ def decode_example(self, value: dict, token_per_repo_id=None) -> "PIL.Image.Imag else: image = PIL.Image.open(BytesIO(bytes_)) image.load() # to avoid "Too many open files" errors + if image.getexif().get(PIL.Image.ExifTags.Base.Orientation) is not None: + image = PIL.ImageOps.exif_transpose(image) if self.mode and self.mode != image.mode: image = image.convert(self.mode) return image diff --git a/tests/features/test_image.py b/tests/features/test_image.py index 9a6f1f2946e..35e62f7d2bf 100644 --- a/tests/features/test_image.py +++ b/tests/features/test_image.py @@ -1,6 +1,7 @@ import os import tarfile import warnings +from io import BytesIO import numpy as np import pandas as pd @@ -90,6 +91,24 @@ def test_image_decode_example(shared_datadir): Image(decode=False).decode_example(image_path) +@require_pil +def test_image_decode_example_with_exif_orientation_tag(shared_datadir): + import PIL.Image + + image_path = str(shared_datadir / "test_image_rgb.jpg") + buffer = BytesIO() + exif = PIL.Image.Exif() + exif[PIL.Image.ExifTags.Base.Orientation] = 8 # rotate the image for 90° + PIL.Image.open(image_path).save(buffer, format="JPEG", exif=exif.tobytes()) + image = Image() + + decoded_example = image.decode_example({"path": None, "bytes": buffer.getvalue()}) + + assert isinstance(decoded_example, PIL.Image.Image) + assert decoded_example.size == (480, 640) # rotated + assert decoded_example.mode == "RGB" + + @require_pil def test_image_change_mode(shared_datadir): import PIL.Image