3A script that converts images to an sRGB colour profile.
5This is particularly useful for screenshots on macOS, which are taken
6with the display's colour profile (e.g. Display LCD or Display P3), but
7which I want to convert to sRGB for converting on the web.
9Note: this is a potentially destructive script. Don't run this on images
10you care about if you don't have a backup!
12* It overwrites the original image file.
13* It strips out EXIF metadata.
15Based on https://github.com/python-pillow/Pillow/issues/1662
22from PIL import Image, ImageCms, ImageOps
23from PIL.ImageCms import PyCMSError
26def get_profile_description(icc_profile):
27 f = io.BytesIO(icc_profile)
28 prf = ImageCms.ImageCmsProfile(f)
29 return prf.profile.profile_description
32def convert_image_to_srgb(im: Image) -> typing.Union[Image, None]:
34 Convert an image to sRGB and return a new Image instance.
36 icc_profile = im.info.get("icc_profile")
38 # If this image doesn't have a colour profile, we're done.
39 if icc_profile is None:
42 # If the image has an EXIF orientation tag, it will be stripped out
43 # upon saving (like all EXIF metadata).
45 # To avoid any weird rotation issues, bake the rotation into the image.
46 # See https://github.com/python-pillow/Pillow/issues/4703#issuecomment-645219973
47 # or the associated test.
49 # See https://github.com/alexwlchan/scripts/issues/21
50 im = ImageOps.exif_transpose(im)
52 # Otherwise, convert the image to an sRGB colour profile and return that.
54 return ImageCms.profileToProfile(
56 inputProfile=io.BytesIO(icc_profile),
57 outputProfile=ImageCms.createProfile("sRGB"),
59 except PyCMSError as err:
60 profile_name = get_profile_description(icc_profile)
62 is_gray_xyz = im.mode == "L" and b"GRAYXYZ" in icc_profile
63 is_cmyk = im.mode == "CMYK" and profile_name == "U.S. Web Coated (SWOP) v2"
65 if err.args[0].args == ("cannot build transform",) and (is_gray_xyz or is_cmyk):
66 return ImageCms.profileToProfile(
68 inputProfile=io.BytesIO(icc_profile),
69 outputProfile=ImageCms.createProfile("sRGB"),
78if __name__ == "__main__":
79 if len(sys.argv) == 1:
80 sys.exit(f"Usage: {__file__} <PATH...>")
82 for path in sys.argv[1:]:
84 out_im = convert_image_to_srgb(im)
86 if out_im is not None: