icc_transform vs colourspace for handling of 16-bit images vs 8-bit images #3966
-
Hi, I'm trying to better understand how icc_transform acts on 16-bit images with regards to using the output profile "srgb". What I'm trying to accomplish is taking whatever image the user supplies and standardizing it into a consistent format to work with in numpy, where the only difference in the form is the bit depth. So considering only 8-bit (uchar) vs 16-bit (ushort) images, I have the following code: image_vips = vips.Image.new_from_buffer(image_bytes, "", access='sequential')
untagged_profile_default = "srgb"
if image_vips.get_typeof('icc-profile-data') != 0:
# get and log the profile info using Pillow ImageCms
image_profile = ImageCms.getProfileInfo(io.BytesIO(image_vips.get('icc-profile-data')))
logger.info(f'Image profile is {image_profile}')
elif image_vips.get_typeof('icc-profile-data') == 0 and image_vips.interpretation == 'cmyk':
untagged_profile_default = "cmyk"
image_2_srgb = image_vips.icc_transform("srgb", embedded=True, input_profile=untagged_profile_default)
image_array = image_2_srgb.numpy() My understanding is that for 8-bit images, this works correctly as it will transform to the srgb ICC profile in libvips. What I'm not clear on is if icc_transform applies the srgb profile or a rgb16 profile if the image is 16-bit. This libvips/libvips/colour/icc_transform.c Line 379 in 3adc62b I read this comment (#580 (comment)) and ultimately that would work for me if I got all the 8-bit images as equivalent to VIPS_INTERPRETATION_sRGB (black to white 0 to 255, luminance encoded with gamma 2.4 plus straight line near 0, and 709 primaries for color) and all the 16-bit images as equivalent to VIPS_INTERPRETATION_RGB16 (same as sRGB except linear encoded [gamma = 1, right?] and 0-65535 for black to white). So I changed the code to the following: image_vips = vips.Image.new_from_buffer(image_bytes, "", access='sequential')
bit_depth = image_vips.format
image_profile = None
if bit_depth not in ['uchar', 'ushort']:
raise ValueError(f'Invalid bit depth of {bit_depth}')
if image_vips.get_typeof('icc-profile-data') != 0:
image_profile = ImageCms.getProfileInfo(io.BytesIO(image_vips.get('icc-profile-data')))
logger.info(f'Image profile is {image_profile}')
if bit_depth == 'uchar': # 8-bit
if image_profile is not None:
image_2_numpy = image_vips.icc_transform("srgb", embedded=True, intent="absolute")
else:
image_2_numpy = image_vips.colourspace("srgb")
elif bit_depth == 'ushort': # 16-bit
if image_profile is not None:
image_2_numpy = image_vips.icc_transform("srgb", embedded=True, intent="absolute")
elif image_vips.interpretation == 'rgb16':
image_2_numpy = image_vips
else:
image_2_numpy = image_vips.colourspace("srgb", image_vips.interpretation)
image_array = image_2_numpy.numpy() The two things that have me kind of stumped are the difference between icc_transform and colourspace, specifically as I'm not going to be outputting these images to file at all since I just want them in a consistent RGB colorspace with a known gamma, and what's the colourspace that needs to be applied to the 16-bit images to make them match the RGB16 interpretation (or be the same as RGB16 except with a non-linear gamma, that's fine too)? |
Beta Was this translation helpful? Give feedback.
Replies: 1 comment 4 replies
-
Hello @daniellovera, image_vips = vips.Image.new_from_buffer(image_bytes, "", access='sequential')
untagged_profile_default = "srgb"
if image_vips.get_typeof('icc-profile-data') != 0:
# get and log the profile info using Pillow ImageCms
image_profile = ImageCms.getProfileInfo(io.BytesIO(image_vips.get('icc-profile-data')))
logger.info(f'Image profile is {image_profile}')
elif image_vips.get_typeof('icc-profile-data') == 0 and image_vips.interpretation == 'cmyk':
untagged_profile_default = "cmyk"
image_2_srgb = image_vips.icc_transform("srgb", embedded=True, input_profile=untagged_profile_default)
image_array = image_2_srgb.numpy() I don't think you need to do this, do you? https://github.com/libvips/libvips/blob/master/libvips/colour/icc_transform.c#L670-L750 So just this ought to work: image_vips = vips.Image.new_from_buffer(image_bytes, "", access='sequential')
if image_vips.get_typeof('icc-profile-data') != 0:
# get and log the profile info using Pillow ImageCms
image_profile = ImageCms.getProfileInfo(io.BytesIO(image_vips.get('icc-profile-data')))
logger.info(f'Image profile is {image_profile}')
image_array = image_vips.icc_transform("srgb").numpy() The difficulty you might have is that many user images have incorrect profiles, often a cmyk image with an embedded srgb profile :( libvips will try to fix this, but it might not always make the right choice.
ICC profiles don't care about depth -- the same profile will do 8 or 16 bits. Whatever the input image depth, |
Beta Was this translation helpful? Give feedback.
Hello @daniellovera,
I don't think you need to do this, do …