Merge pull request #22 from alexwlchan/fix-srgbify-rotation-bug
- ID
45b10d5- date
2024-03-28 07:14:32+00:00- author
Alex Chan <alex@alexwlchan.net>- parents
3607dbb,f9b6c1e- message
Merge pull request #22 from alexwlchan/fix-srgbify-rotation-bug Make sure srgbify doesn't rotate images when it strips out EXIF- changed files
3 files, 41 additions, 9 deletions
Changed files
images/examples/taylorswift.jpg (0) → images/examples/taylorswift.jpg (3939779)
diff --git a/images/examples/taylorswift.jpg b/images/examples/taylorswift.jpg
new file mode 100644
index 0000000..b3885f6
Binary files /dev/null and b/images/examples/taylorswift.jpg differ
images/srgbify.py (1846) → images/srgbify.py (2475)
diff --git a/images/srgbify.py b/images/srgbify.py
index 503d016..08f164c 100755
--- a/images/srgbify.py
+++ b/images/srgbify.py
@@ -6,13 +6,20 @@ This is particularly useful for screenshots on macOS, which are taken
with the display's colour profile (e.g. Display LCD or Display P3), but
which I want to convert to sRGB for converting on the web.
+Note: this is a potentially destructive script. Don't run this on images
+you care about if you don't have a backup!
+
+* It overwrites the original image file.
+* It strips out EXIF metadata.
+
Based on https://github.com/python-pillow/Pillow/issues/1662
"""
import io
import sys
+import typing
-from PIL import Image, ImageCms
+from PIL import Image, ImageCms, ImageOps
from PIL.ImageCms import PyCMSError
from pillow_heif import register_heif_opener
@@ -20,7 +27,7 @@ from pillow_heif import register_heif_opener
register_heif_opener()
-def convert_image_to_srgb(im: Image) -> Image:
+def convert_image_to_srgb(im: Image) -> typing.Union[Image, None]:
"""
Convert an image to sRGB and return a new Image instance.
"""
@@ -28,10 +35,19 @@ def convert_image_to_srgb(im: Image) -> Image:
# If this image doesn't have a colour profile, we're done.
if icc_profile is None:
- return im
+ return None
+
+ # If the image has an EXIF orientation tag, it will be stripped out
+ # upon saving (like all EXIF metadata).
+ #
+ # To avoid any weird rotation issues, bake the rotation into the image.
+ # See https://github.com/python-pillow/Pillow/issues/4703#issuecomment-645219973
+ # or the associated test.
+ #
+ # See https://github.com/alexwlchan/scripts/issues/21
+ im = ImageOps.exif_transpose(im)
# Otherwise, convert the image to an sRGB colour profile and return that.
-
try:
return ImageCms.profileToProfile(
im,
@@ -39,7 +55,6 @@ def convert_image_to_srgb(im: Image) -> Image:
outputProfile=ImageCms.createProfile("sRGB"),
)
except PyCMSError as err:
- print(err.args[0].args)
if (
im.mode == "L"
and b"GRAYXYZ" in icc_profile
@@ -65,7 +80,7 @@ if __name__ == "__main__":
im = Image.open(path)
out_im = convert_image_to_srgb(im)
- if out_im != im:
+ if out_im is not None:
out_im.save(path)
print(path)
images/test_srgbify.py (1762) → images/test_srgbify.py (2378)
diff --git a/images/test_srgbify.py b/images/test_srgbify.py
index aed3d10..a4172d2 100644
--- a/images/test_srgbify.py
+++ b/images/test_srgbify.py
@@ -1,4 +1,5 @@
import filecmp
+import pathlib
from PIL import Image
@@ -13,9 +14,7 @@ def test_it_ignores_an_image_which_is_already_srgb():
"""
im = Image.open("images/examples/Shoes-sRGB.jpg")
- new_im = convert_image_to_srgb(im)
-
- assert new_im is im
+ assert convert_image_to_srgb(im) is None
def test_it_converts_a_display_p3_image_to_srgb(tmp_path):
@@ -65,3 +64,21 @@ def test_it_converts_images_with_a_grey_profile():
new_im = convert_image_to_srgb(im)
assert new_im.mode == "RGB"
+
+
+def test_it_preserves_rotation_from_exif_orientation(tmp_path: pathlib.Path):
+ """
+ This is based on a photo exported from my Apple Photos Library
+ which was rotated by 90 degrees upon transformation.
+
+ This was caused by an image with an EXIF orientation tag that was
+ being stripped on save. I found a solution in the Pillow issue tracker.
+
+ See https://github.com/alexwlchan/scripts/issues/21
+ See https://github.com/python-pillow/Pillow/issues/4703
+ """
+ im = Image.open("images/examples/taylorswift.jpg")
+
+ new_im = convert_image_to_srgb(im)
+
+ assert new_im.size == (3024, 4032)