12

I want to batch convert images from various modes such as RGBa, CMYK etc to sRGB using PIL in Python.

However, I am not sure and can't find it anywhere whether

image.convert("RGB") 

converts the image to sRGB or Adobe RGB. My intuition says that it converts to sRGB.

If it converts to sRGB and I pass in Adobe RGB image to the convert function, would it successfully convert to sRGB?

Sabih
  • 325
  • 1
  • 3
  • 12

1 Answers1

25

By default PIL is agnostic about color spaces (like sRGB and Adobe RGB).

As you might know, RGB is simply three 8-bit values for red, green and blue. CMYK is four 8-bit values. When you use Image.convert('RGB') it just converts each pixel to the triple 8-bit value. So it fundamentally changes the mode of how image is represented and stored.

sRGB and Adobe RGB, on the other hand, are just RGB color spaces. Basically, these are rules for how to render those three values on your monitor, printer or any other physical output device. Such rules can be added to JPEG files in form of ICC profiles by such software as Photoshop, for example. But PIL normally will not attach any ICC profile when you just use Image.save.

In order to check if your image embeds ICC profile, you can look for key 'icc_profile' in dictionary image.info like this image.info.get('icc_profile', None).

In order to save JPEG images preserving ICC profile with PIL you could use something like this:

img.save('image.jpg',
         format = 'JPEG',
         quality = 100,
         icc_profile = img.info.get('icc_profile',''))

In order to convert any RGB JPEG image from its color space to sRGB, you could use this function:

import io
from PIL import Image
from PIL import ImageCms

def convert_to_srgb(img):
    '''Convert PIL image to sRGB color space (if possible)'''
    icc = img.info.get('icc_profile', '')
    if icc:
        io_handle = io.BytesIO(icc)     # virtual file
        src_profile = ImageCms.ImageCmsProfile(io_handle)
        dst_profile = ImageCms.createProfile('sRGB')
        img = ImageCms.profileToProfile(img, src_profile, dst_profile)
    return img

So your code for conversion to single RGB color space might look like:

img = Image.open('image_AdobeRGB.jpg')
img_conv = convert_to_srgb(img)
if img.info.get('icc_profile', '') != img_conv.info.get('icc_profile', ''):
    # ICC profile was changed -> save converted file
    img_conv.save('image_sRGB.jpg',
                  format = 'JPEG',
                  quality = 100,
                  icc_profile = img_conv.info.get('icc_profile',''))

NOTE: you might want to use slightly lower quality than 100.

Example input (Adobe RGB JPEG):

Adobe RGB JPG

Example output (sRGB JPEG):

sRGB JPG

Andriy Makukha
  • 7,580
  • 1
  • 38
  • 49
  • What about the matrix transformation that needs to be applied to convert from sRGB to Adobe RGB as mentioned here: https://stackoverflow.com/questions/40017741/mathematical-conversion-srgb-and-adobergb?rq=1 – Sabih Jun 01 '18 at 08:46
  • 1
    @SabihHasan, in my example such math is done internally by `ImageCms.profileToProfile`. This is needed when you want to make two images with different color spaces to look the same (or very similar) on screen. Like my sample input and sample output. Since color spaces are different, underlying RGB bytes need to be different to make that happen. So this math just computes what the RGB bytes need to be for the picture to look the same in different color space. (On the contrary, if two images have same RGB bytes, but have different color spaces, they will look different on screen.) – Andriy Makukha Jun 01 '18 at 09:31