fix(python): honor EXIF orientation for bytes and PIL images (#755)

This commit is contained in:
LI authored and GitHub committed 2026-10-13 08:06:01 +08:00
1 parent 0700743fc8
commit 7cfd3d9ef7
2 files changed
+62 -3

No files matched your search

+4 -3
View File
@@ -81,14 +81,15 @@ class LoadImage:
return img
if isinstance(img, bytes):
img = self.img_to_ndarray(Image.open(BytesIO(img)))
img = self.exif_transpose(Image.open(BytesIO(img)))
img = self.img_to_ndarray(img)
return img
if isinstance(img, np.ndarray):
return img
if isinstance(img, Image.Image):
return self.img_to_ndarray(img)
return self.img_to_ndarray(self.exif_transpose(img))
if isinstance(img, MemoryImage):
try:
@@ -116,7 +117,7 @@ class LoadImage:
if img_corrected is None:
return img
return img_corrected
except Exception as e:
except Exception:
return img
def img_to_ndarray(self, img: Image.Image) -> np.ndarray:
+58
View File
@@ -0,0 +1,58 @@
from pathlib import Path
import numpy as np
import pytest
from PIL import Image
from rapidocr.utils.load_image import LoadImage
@pytest.mark.parametrize("input_type", ["bytes", "pil"])
@pytest.mark.parametrize("orientation", [1, 3, 6, 8])
def test_exif_input_types_match_path(tmp_path, input_type, orientation):
pixels = np.arange(18, dtype=np.uint8).reshape(2, 3, 3)
image = Image.fromarray(pixels)
exif = image.getexif()
exif[274] = orientation
path = tmp_path / "oriented.png"
image.save(path, exif=exif)
loader = LoadImage()
rotations = {1: 0, 3: 2, 6: 3, 8: 1}
expected = np.rot90(pixels, rotations[orientation])[..., ::-1]
np.testing.assert_array_equal(loader(path), expected)
if input_type == "bytes":
actual = loader(path.read_bytes())
else:
with Image.open(path) as pil_image:
original_pixels = np.array(pil_image)
actual = loader(pil_image)
assert pil_image.getexif()[274] == orientation
np.testing.assert_array_equal(np.array(pil_image), original_pixels)
np.testing.assert_array_equal(actual, expected)
@pytest.mark.parametrize("input_type", ["bytes", "pil"])
def test_exif_existing_jpeg_matches_path(input_type):
path = Path(__file__).parent / "test_files" / "img_exif_orientation.jpg"
loader = LoadImage()
expected = loader(path)
if input_type == "bytes":
actual = loader(path.read_bytes())
else:
with Image.open(path) as image:
actual = loader(image)
np.testing.assert_array_equal(actual, expected)
@pytest.mark.parametrize("input_type", ["bytes", "pil"])
def test_input_without_exif_preserves_pixels(tmp_path, input_type):
pixels = np.arange(18, dtype=np.uint8).reshape(2, 3, 3)
image = Image.fromarray(pixels)
path = tmp_path / "unoriented.png"
image.save(path)
loader = LoadImage()
if input_type == "bytes":
actual = loader(path.read_bytes())
else:
actual = loader(image)
np.testing.assert_array_equal(actual, pixels[..., ::-1])