Merge pull request #19 from alexwlchan/better-srgbify
- ID
9ad1652- date
2024-03-18 07:43:33+00:00- author
Alex Chan <alex@alexwlchan.net>- parents
6116a53,87e69ed- message
Merge pull request #19 from alexwlchan/better-srgbify Add tests for `srgbify`; fix a bug with greyscale images- changed files
Changed files
.github/workflows/test.yml (1214) → .github/workflows/test.yml (1018)
diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml
index bbe8dae..b72bfaa 100644
--- a/.github/workflows/test.yml
+++ b/.github/workflows/test.yml
@@ -38,9 +38,4 @@ jobs:
- name: Run tests
run: |
- py.test aws/test_s3tree.py
- py.test flickr/test_fluser_lookup.py
- py.test text/test_fix_twitter_thread.py
- py.test textexpander/test_get_mastodon_text.py
- py.test web/test_save_ao3_links.py
- py.test web/test_save_youtube_videos.py
+ find . -name 'test_*.py' -not -path './.venv/*' | sort | xargs pytest
images/examples/3925727501_6aa7c94c10_w.jpg (0) → images/examples/3925727501_6aa7c94c10_w.jpg (36732)
diff --git a/images/examples/3925727501_6aa7c94c10_w.jpg b/images/examples/3925727501_6aa7c94c10_w.jpg
new file mode 100644
index 0000000..232f760
Binary files /dev/null and b/images/examples/3925727501_6aa7c94c10_w.jpg differ
images/examples/Iceland-P3.jpg (0) → images/examples/Iceland-P3.jpg (102415)
diff --git a/images/examples/Iceland-P3.jpg b/images/examples/Iceland-P3.jpg
new file mode 100644
index 0000000..fe9ebea
Binary files /dev/null and b/images/examples/Iceland-P3.jpg differ
images/examples/Iceland-sRGB-expected.jpg (0) → images/examples/Iceland-sRGB-expected.jpg (30481)
diff --git a/images/examples/Iceland-sRGB-expected.jpg b/images/examples/Iceland-sRGB-expected.jpg
new file mode 100644
index 0000000..ea0e17c
Binary files /dev/null and b/images/examples/Iceland-sRGB-expected.jpg differ
images/examples/Shoes-sRGB.jpg (0) → images/examples/Shoes-sRGB.jpg (451785)
diff --git a/images/examples/Shoes-sRGB.jpg b/images/examples/Shoes-sRGB.jpg
new file mode 100644
index 0000000..9a6515f
Binary files /dev/null and b/images/examples/Shoes-sRGB.jpg differ
images/examples/screenshot-with-transparency.png (0) → images/examples/screenshot-with-transparency.png (361453)
diff --git a/images/examples/screenshot-with-transparency.png b/images/examples/screenshot-with-transparency.png
new file mode 100644
index 0000000..afb6a57
Binary files /dev/null and b/images/examples/screenshot-with-transparency.png differ
images/srgbify.py (1264) → images/srgbify.py (1846)
diff --git a/images/srgbify.py b/images/srgbify.py
index 1bbcb31..503d016 100755
--- a/images/srgbify.py
+++ b/images/srgbify.py
@@ -9,41 +9,63 @@ which I want to convert to sRGB for converting on the web.
Based on https://github.com/python-pillow/Pillow/issues/1662
"""
+import io
import sys
-import tempfile
from PIL import Image, ImageCms
+from PIL.ImageCms import PyCMSError
from pillow_heif import register_heif_opener
register_heif_opener()
-if __name__ == "__main__":
- if len(sys.argv) == 1:
- sys.exit(f"Usage: {__file__} <PATH...>")
+def convert_image_to_srgb(im: Image) -> Image:
+ """
+ Convert an image to sRGB and return a new Image instance.
+ """
+ icc_profile = im.info.get("icc_profile")
- for path in sys.argv[1:]:
- in_img = Image.open(path)
+ # If this image doesn't have a colour profile, we're done.
+ if icc_profile is None:
+ return im
- srgb_profile = ImageCms.createProfile("sRGB")
+ # Otherwise, convert the image to an sRGB colour profile and return that.
- icc_profile = in_img.info.get("icc_profile")
+ try:
+ return ImageCms.profileToProfile(
+ im,
+ inputProfile=io.BytesIO(icc_profile),
+ outputProfile=ImageCms.createProfile("sRGB"),
+ )
+ except PyCMSError as err:
+ print(err.args[0].args)
+ if (
+ im.mode == "L"
+ and b"GRAYXYZ" in icc_profile
+ and err.args[0].args == ("cannot build transform",)
+ ):
+ return ImageCms.profileToProfile(
+ im,
+ inputProfile=io.BytesIO(icc_profile),
+ outputProfile=ImageCms.createProfile("sRGB"),
+ outputMode="RGB",
+ )
+ else:
+ raise
+
+ return out_im
- # Handle the case where this image doesn't have
- # a colour profile
- if icc_profile is None:
- print(path)
- continue
- _, icc_profile_path = tempfile.mkstemp(suffix=".icc")
+if __name__ == "__main__":
+ if len(sys.argv) == 1:
+ sys.exit(f"Usage: {__file__} <PATH...>")
- with open(icc_profile_path, "wb") as out_file:
- out_file.write(icc_profile)
+ for path in sys.argv[1:]:
+ im = Image.open(path)
+ out_im = convert_image_to_srgb(im)
- out_img = ImageCms.profileToProfile(
- in_img, inputProfile=icc_profile_path, outputProfile=srgb_profile
- )
+ if out_im != im:
+ out_im.save(path)
- out_img.save(path)
print(path)
images/test_srgbify.py (0) → images/test_srgbify.py (1762)
diff --git a/images/test_srgbify.py b/images/test_srgbify.py
new file mode 100644
index 0000000..aed3d10
--- /dev/null
+++ b/images/test_srgbify.py
@@ -0,0 +1,67 @@
+import filecmp
+
+from PIL import Image
+
+from srgbify import convert_image_to_srgb
+
+
+def test_it_ignores_an_image_which_is_already_srgb():
+ """
+ If an image is already in sRGB, we don't need to do anything.
+
+ This image was downloaded from https://webkit.org/blog-files/color-gamut/
+ """
+ im = Image.open("images/examples/Shoes-sRGB.jpg")
+
+ new_im = convert_image_to_srgb(im)
+
+ assert new_im is im
+
+
+def test_it_converts_a_display_p3_image_to_srgb(tmp_path):
+ """
+ If an image is in Display P3, it gets converted to sRGB.
+
+ This image was downloaded from https://webkit.org/blog-files/color-gamut/
+ """
+ im = Image.open("images/examples/Iceland-P3.jpg")
+
+ new_im = convert_image_to_srgb(im)
+
+ new_im.save(tmp_path / "Iceland-sRGB-actual.jpg")
+
+ assert filecmp.cmp(
+ tmp_path / "Iceland-sRGB-actual.jpg",
+ "images/examples/Iceland-sRGB-expected.jpg",
+ shallow=False,
+ )
+
+
+def test_it_preserves_transparency_in_png_images():
+ """
+ If an image is a PNG with transparency, that gets preserved when converting
+ to sRGB.
+
+ This image is a screenshot from my own computer.
+ """
+ im = Image.open("images/examples/screenshot-with-transparency.png")
+
+ new_im = convert_image_to_srgb(im)
+
+ assert new_im.mode == "RGBA"
+
+ # The top left-hand corner should be transparent
+ assert new_im.getpixel((0, 0)) == (0, 0, 0, 0)
+
+
+def test_it_converts_images_with_a_grey_profile():
+ """
+ If an image has a grey colour profile, that gets converted to sRGB.
+
+ This image is from https://www.flickr.com/photos/lselibrary/3925727501/
+ """
+ im = Image.open("images/examples/3925727501_6aa7c94c10_w.jpg")
+
+ new_im = convert_image_to_srgb(im)
+
+ assert new_im.mode == "RGB"