如何使用scikit-image和/或PIL将ProPhoto RGB色彩空间转换为RGB?

3
我有一些在ProPhoto RGB色彩空间中的TIF文件。使用scikit-image的“imload”方法可以很好地导入这些文件。然而,当我尝试使用matplotlib查看图像数据时,会收到以下错误信息:
plt.imshow(myImage)

plt.show()

将RGB数据裁剪到imshow的有效范围内(对于浮点数为[0..1],对于整数为[0..255])。

这些值被裁剪为255,并显示一个完全白色的图像。关于imread读取的图像数据,错误是正确的。这些值看起来像:

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

这些值需要进行缩放才能获得范围在0-255之间的正确RGB值。

当我使用类似IrfanView的软件打开图像文件并重新保存时,色彩空间会被正确处理为RGB(可能是因为IrfanView正在为我转换它)。然而,我需要自动化这个转换过程。

是否可以使用scikit-image或PIL(或其他方法)将ProPhoto RGB色彩空间的TIF文件转换为RGB色彩空间?我在scikit-image的“color”模块文档中https://scikit-image.org/docs/stable/api/skimage.color.html没有看到对该色彩空间的任何提及。除非“ProPhoto RGB”可以用另一个名称表示或与另一种转换方法兼容?

谢谢!

编辑:

用户Abhi25t的答案有助于进展一些来显示这些图像。然而,他们的答案似乎仅解决了16位到8位的转换问题。还存在ProPhoto RGB到sRGB的色彩空间问题。希望这次编辑可以更好地说明这个问题。

原始测试TIF图像的像素值为ProPhoto色彩空间:

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

Photoshop转换为sRGB的文件的像素值:
array([[59771, 59873, 60161],
       [59863, 59965, 60235],
       [59755, 59855, 60119],
       ...,
       [59623, 59489, 59993],
       [59561, 59411, 59925],
       [59463, 59333, 59851]], dtype=uint16)

IRFAN VIEW 转换文件的像素值(似乎已转换为 8 位并翻译为 sRGB 空间):
array([[233, 233, 234],
       [233, 233, 234],
       [232, 233, 234],
       ...,
       [232, 231, 233],
       [232, 231, 233],
       [231, 231, 233]], dtype=uint8)

我在这里添加了一些测试代码和图片: https://drive.google.com/drive/folders/1tcfUY2Kwlz-LAlt6_YXtT8l6vLnCucbM?usp=sharing

该图像应显示为一个色彩检查器目标。 只需绘制“TEST_PROPHOTO_IRFAN_SAVED.tif”的imread结果,您就应该能够正确地看到它。

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()

2
也许你可以通过Dropbox或GoogleDrive分享ProPhoto TIFF,这样大家就可以进行实验了? - Mark Setchell
2个回答

3
您需要执行以下步骤:
  1. 使用 imread 读取图像。
  2. 将图像转换为浮点表示,范围在[0-1]之间,即将其转换为float并除以65535。
  3. 使用RIMM-ROMM RGB解码函数进行解码。
  4. 使用所需的颜色转换矩阵从RIMM-ROMM RGB色域转换为sRGB色域:
# Using CAT02.
[[ 2.0364917242 -0.7375906525 -0.2992598689]
 [-0.2257179791  1.2231765313  0.0027252248]
 [-0.0105451286 -0.1348798497  1.1452101525]]
  1. 使用sRGB反电光传递函数进行编码。
  2. 转换为整数表示,范围在[0-255]之间,即乘以255并转换为整数。

Colour可以直接使用colour.RGB_to_RGB定义来实现3到5。假设数据已经在浮点数范围内,转换如下:

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)

注意:由于ProPhoto RGB的色域比sRGB大,因此在转换过程中可能会生成超出色域范围的颜色,这些颜色可以在第4或第5步后简单地剪裁到[0-1]范围内,或者使用专用算法进行色域映射。

ProPhoto RGB & sRGB


谢谢!这非常接近一个完整的答案。然而,我发现当我实现这个时,值很奇怪。例如,在您的第5步之后,我运行:(img_rgb_convert * 255).astype('int')(即执行您的第6步),这会产生一个包含高达287和低至-462的值的数组。将它们剪切到0和255 似乎可以产生有效的结果(尽管我不确定能否轻易地察觉到任何微妙的颜色差异),但这是正确的方法吗? - baffled
最后,我加载了IrfanView转换的文件,并使用numpy.subtract将RGB值与非剪切(img_rgb_convert * 255)。astype('int')[Step 6 result]进行比较。这种差异可以从-462到+32范围内。与剪辑的img_rgb_convert进行比较,得到的范围为-23到+24。这似乎是个问题,除非问题出在IrfanView的转换上? - baffled
1
这将生成一个数组,其中包含我的测试文件中高达287和低至-462的值。这并不意外,因为ProPhoto RGB比sRGB具有更宽的色域,您可能会得到超出色域的颜色。作为额外的步骤,在4或5之后,您可以夹紧颜色,使其在0-1范围内。 - Kel Solaar
再次感谢。那很有道理!您对IrfanView转换和剪裁的“img_rgb_convert”结果之间的差异有何看法? - baffled
1
我无法确定,因为没有查看IrfanView代码。正如您所看到的,如果不小心处理,转换可能会出现很多问题!例如,它们可能没有执行色彩适应,而这是必需的,因为光源不同。 - Kel Solaar
非常感谢您的出色回答和帮助! - baffled

1
你需要在这里将16位分辨率(灰度)转换为8位。
myImage = myImage * 255/65535

然后 matplotlib 就可以显示了。

如有需要,请将您的浮点数转换为整数。

myImage = myImage.astype(int)

我认为255/65535应该是(255/65535)。这个帮助了,然而,一个颜色空间转换问题仍然存在。我已经尝试澄清我的问题。 - baffled

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接