fix image orientation
This commit is contained in:
+4
-2
@@ -6,7 +6,7 @@ import subprocess
|
|||||||
from tempfile import TemporaryDirectory
|
from tempfile import TemporaryDirectory
|
||||||
|
|
||||||
from fastapi import HTTPException, UploadFile
|
from fastapi import HTTPException, UploadFile
|
||||||
from PIL import Image, UnidentifiedImageError
|
from PIL import Image, ImageOps, UnidentifiedImageError
|
||||||
|
|
||||||
try:
|
try:
|
||||||
from pillow_heif import register_heif_opener
|
from pillow_heif import register_heif_opener
|
||||||
@@ -59,7 +59,9 @@ def process_upload(file: UploadFile | None) -> ProcessedImage | None:
|
|||||||
|
|
||||||
|
|
||||||
def _prepare_image(source_image: Image.Image) -> ProcessedImage:
|
def _prepare_image(source_image: Image.Image) -> ProcessedImage:
|
||||||
prepared = _strip_metadata_and_convert(source_image)
|
# Normalize orientation from EXIF before resizing or stripping metadata.
|
||||||
|
prepared = ImageOps.exif_transpose(source_image)
|
||||||
|
prepared = _strip_metadata_and_convert(prepared)
|
||||||
prepared.thumbnail((MAX_IMAGE_SIDE, MAX_IMAGE_SIDE))
|
prepared.thumbnail((MAX_IMAGE_SIDE, MAX_IMAGE_SIDE))
|
||||||
|
|
||||||
output = BytesIO()
|
output = BytesIO()
|
||||||
|
|||||||
@@ -63,6 +63,19 @@ def make_image_upload(
|
|||||||
return (filename, output.getvalue(), "image/png")
|
return (filename, output.getvalue(), "image/png")
|
||||||
|
|
||||||
|
|
||||||
|
def make_oriented_jpeg_upload(
|
||||||
|
filename="portrait.jpg",
|
||||||
|
size=(1600, 900),
|
||||||
|
orientation=6,
|
||||||
|
):
|
||||||
|
image = Image.new("RGB", size, (20, 120, 220))
|
||||||
|
exif = Image.Exif()
|
||||||
|
exif[274] = orientation
|
||||||
|
output = BytesIO()
|
||||||
|
image.save(output, format="JPEG", exif=exif)
|
||||||
|
return (filename, output.getvalue(), "image/jpeg")
|
||||||
|
|
||||||
|
|
||||||
def read_jpeg_size(image_bytes):
|
def read_jpeg_size(image_bytes):
|
||||||
with Image.open(BytesIO(image_bytes)) as image:
|
with Image.open(BytesIO(image_bytes)) as image:
|
||||||
return image.format, image.size
|
return image.format, image.size
|
||||||
@@ -357,6 +370,21 @@ def test_can_upload_image_for_box_and_process_it(client, db_session):
|
|||||||
assert image_size == (1600, 800)
|
assert image_size == (1600, 800)
|
||||||
|
|
||||||
|
|
||||||
|
def test_image_pipeline_applies_exif_orientation_before_saving(client, db_session):
|
||||||
|
response = create_box(client, name="Portrait Box", image=make_oriented_jpeg_upload())
|
||||||
|
|
||||||
|
assert response.status_code == 303
|
||||||
|
|
||||||
|
box = db_session.query(Box).filter_by(name="Portrait Box").one()
|
||||||
|
assert box.image_blob is not None
|
||||||
|
assert box.image_width == 900
|
||||||
|
assert box.image_height == 1600
|
||||||
|
|
||||||
|
image_format, image_size = read_jpeg_size(box.image_blob)
|
||||||
|
assert image_format == "JPEG"
|
||||||
|
assert image_size == (900, 1600)
|
||||||
|
|
||||||
|
|
||||||
def test_can_upload_image_for_item(client, db_session):
|
def test_can_upload_image_for_item(client, db_session):
|
||||||
box = Box(name="Main Box")
|
box = Box(name="Main Box")
|
||||||
db_session.add(box)
|
db_session.add(box)
|
||||||
|
|||||||
Reference in New Issue
Block a user