How do you convert ProPhoto RGB colorspace to RGB with scikit-image and/or PIL?

Question:

I have TIF files that are in the ProPhoto RGB colorspace. These are imported fine with scikit-image’s "imload" method. However, when I try to use matplotlib to view the image data I receive the error:

plt.imshow(myImage)

plt.show()

Clipping input data to the valid range for imshow with RGB data ([0..1] for floats or [0..255] for integers).

The values are clipped to 255 and a totally white image is displayed. The error is correct about the image data as read by imread. The values look like:

myImage[0]
array([[60061, 60135, 60673],
   [59907, 59983, 60533],
   [59931, 60007, 60557],
   ...,
   [60649, 60801, 61147],
   [60581, 60743, 61091],
   [60647, 60797, 61143]], dtype=uint16)

These values obviously need to be scaled to obtain proper RGB values that range between 0-255.

When I open the image file in something like IrfanView and re-save it, the colorspace is handled properly as RGB (probably because IrfanView is converting it for me). However, I need to automate that conversion.

Is it possible to convert TIFs using the ProPhoto RGB colorspace to the RGB colorspace using either scikit-image or PIL (or something else)? I did not see any mention of that colorspace in the docs for the "color" module of scikit-image at https://scikit-image.org/docs/stable/api/skimage.color.html. Unless "ProPhoto RGB" can be known by another name or compatible with another conversion method?

Thank you!

EDIT:

User Abhi25t’s answer helped make some progress towards displaying these images. However, their answer appears to address only the 16-bit to 8-bit conversion. There is still a colorspace issue of ProPhoto RGB to sRGB. Hopefully this edit demonstrates this better.

The pixel values for the raw test TIF image in ProPhoto colorspace:

 array([[58465, 58479, 58785],
       [58575, 58591, 58879],
       [58441, 58457, 58739],
       ...,
       [58185, 58045, 58549],
       [58101, 57951, 58463],
       [57993, 57853, 58371]], dtype=uint16)

The pixel values for the Photoshop sRGB converted file:

array([[59771, 59873, 60161],
       [59863, 59965, 60235],
       [59755, 59855, 60119],
       ...,
       [59623, 59489, 59993],
       [59561, 59411, 59925],
       [59463, 59333, 59851]], dtype=uint16)

The pixel values for the IRFAN VIEW converted file (it appears to have converted to 8-bit AND translated to sRGB space):

array([[233, 233, 234],
       [233, 233, 234],
       [232, 233, 234],
       ...,
       [232, 231, 233],
       [232, 231, 233],
       [231, 231, 233]], dtype=uint8)

I have added some test code and images here:
https://drive.google.com/drive/folders/1tcfUY2Kwlz-LAlt6_YXtT8l6vLnCucbM?usp=sharing

The image should display as a colorchecker target. You should be able to see it properly just plotting the imread result of "TEST_PROPHOTO_IRFAN_SAVED.tif".

from skimage import io
import numpy as np
import matplotlib.pyplot as plt

# TEST_PROPHOTO.tif : RAW TIF FILE
# TEST_PROPHOTO_IRFAN_SAVED.tif : RAW TIF, OPENED IN IRFANVIEW AND RESAVED AS TIF
# TEST_SRGB_PHOTOSHOP_CONVERTED.tif : RAW TIF, OPENED IN PHOTOSHOP, PROFILE CONVERTED TO sRGB, RESAVED AS TIF

img_path = '/path/to/image/myImage.tif'

img_rgb = io.imread(img_path)
plt.imshow(img_rgb)
plt.show()

# VIEW IMAGE CONTENTS
img_rgb[0] # you can see these values are 16-bit;
# however, colorspace (actual RGB values) differs between the ProPhoto test file and the Photoshop converted sRGB file

# STACKOVERFLOW USER Abhi25t's METHOD TO CONVERT FROM 16-bit color [0-65535] to 8-bit color [0-255]:
img_rgb_convert = img_rgb * (255/65535)
img_rgb_convert = img_rgb_convert.astype(int)
plt.imshow(img_rgb_convert)
plt.show()
Asked By: baffled

||

Answers:

You need to convert 16-bit resolution (of grayscale) to 8-bit here.

myImage = myImage * 255/65535

And then matplotlib will be able to display.

Convert your floats to int if required.

myImage = myImage.astype(int)
Answered By: Abhi25t

You would need to perform the following steps:

  1. Read the image with imread.
  2. Convert to floating-point representation in range [0-1], i.e. convert to float and divide by 65535.
  3. Decode with the RIMM-ROMM RGB decoding function.
  4. Convert from RIMM-ROMM RGB gamut to sRGB gamut using the required colour conversion matrix:
# Using CAT02.
[[ 2.0364917242 -0.7375906525 -0.2992598689]
 [-0.2257179791  1.2231765313  0.0027252248]
 [-0.0105451286 -0.1348798497  1.1452101525]]
  1. Encode with the sRGB inverse Electro-Optical Transfer Function.
  2. Convert to integer-representation in range [0-255], i.e. multiply by 255 and convert to integer.

Colour can do 3 to 5 directly using the colour.RGB_to_RGB definition. Assuming the data is in floating-point range already, the conversion would be like to that:

img_rgb_convert = colour.RGB_to_RGB(
    img_rgb,
    colour.RGB_COLOURSPACES['ProPhoto RGB'],
    colour.RGB_COLOURSPACES['sRGB'],
    chromatic_adaptation_transform='CAT02',
    apply_cctf_decoding=True,
    apply_cctf_encoding=True)

Note: Because ProPhoto RGB has a larger gamut than that of sRGB, it is possible that the conversion generates out-of-gamut colours, those can be clipped in the range [0-1] for simplicity after step 4 or 5 or gamut-mapped using a dedicated algorithm.

ProPhoto RGB & sRGB

Answered By: Kel Solaar