Pillow/OpenCV粘贴图像到画布后颜色偏移,如何添加ICC profile解决?
问题描述
这是原始图像:
这是我用Pillow打开并显示后的图像:
原始图像为JPEG格式,我尝试将格式改为TIFF、PSD均无效,也试过将图像转换为RGB、RGBA、CMYK模式,未见改善。
如果对该图像直接截图后再用Pillow打开,颜色可以正常保留。
之后我尝试换用OpenCV处理,也出现了完全相同的问题,显示结果和上述异常图片一致。
正如@HansHirse在我前一个问题的评论中提到的内容提醒的那样,我验证后确认直接打开图像后立即保存时颜色可以正常保留。哪怕不调用ICC profile,直接打开保存也会输出完全一致的图像。
相关测试代码:
from PIL import Image i = Image.open("/Users/shashwataryal/Downloads/Tom Ford Rodeo.jpeg") # 直接打开后立即保存,颜色可以正常保留 i.save("/Users/shashwataryal/Downloads/1.jpeg") # 但如果新建图像后粘贴原始图像,就会出现颜色过饱和的问题,和之前显示异常的结果一致 layer = Image.new('RGB', i.size, "#faf8f8") layer.show() layer.paste(i) layer.show() layer.save('/Users/shashwataryal/Downloads/1.jpeg',icc_profile=i.info.get('icc_profile')) layer.save('/Users/shashwataryal/Downloads/2.jpeg',icc_profile=i.info.get('icc_profile'))
我的需求是将该图添加到Pillow创建的空白新图像上,后续可能还会在旁添加其他图像。目前有两个疑问:
- 我找不到给新图像添加ICC profile的方法
- 如果我把其他图像粘贴到这个Pillow创建的新图像上,ICC profile会对其他图像产生影响吗?
之前我的问题被标记为相关问题的重复,但该问题仅涉及打开后立即保存图像的场景,我在该场景下无任何问题,我的问题出现在将图像粘贴到大尺寸空白画布后再保存的环节。
解决方案
你遇到的问题核心是原始图像携带的是广色域ICC配置文件,Pillow新建的空白画布默认使用sRGB色域,直接粘贴时没有做色域转换,导致颜色值映射错误。你直接保存原始图像时只是原样携带了ICC配置文件,所以显示正常;但粘贴到新画布后,即使保存时传入原始ICC,画布本身的颜色值是按sRGB生成的,套上广色域ICC后就会出现过饱和的问题。
具体解决步骤:
- 先读取原始图像的ICC配置文件,将原始图像的颜色值转换为sRGB色域的数值,再进行粘贴操作。可以用Pillow的
ImageCms模块完成色域转换:
import io from PIL import Image, ImageCms # 打开原始图像 img = Image.open("/Users/shashwataryal/Downloads/Tom Ford Rodeo.jpeg") # 获取原始ICC配置 orig_icc = img.info.get("icc_profile") if orig_icc: # 构建原始色域和目标sRGB色域的转换路径 src_profile = ImageCms.ImageCmsProfile(io.BytesIO(orig_icc)) dst_profile = ImageCms.createProfile("sRGB") # 转换图像颜色值到sRGB img_srgb = ImageCms.profileToProfile(img, src_profile, dst_profile, outputMode="RGB") else: img_srgb = img.convert("RGB") # 新建空白画布,默认就是sRGB色域 layer = Image.new('RGB', img.size, "#faf8f8") # 粘贴已经转换为sRGB的图像 layer.paste(img_srgb) # 保存时不需要额外指定ICC,默认sRGB所有显示设备都能正确识别 layer.save('/Users/shashwataryal/Downloads/output.jpeg')
如果运行时提示找不到ImageCms模块,需要先安装对应依赖,macOS可以用brew install littlecms2后重装Pillow即可。
- 关于你提到的两个疑问:
- 给新图像添加ICC配置文件不需要在新建时操作,只需要在最终
save的时候传入icc_profile参数即可,你之前的写法是对的。但要注意:如果传入的是广色域ICC,整个画布所有内容都会按照这个ICC来解析,所以必须确保画布上所有图像的颜色值都已经提前转换到对应ICC的色域,不然其他图像也会出现颜色异常。 - 如果你要在同一个画布上放多个不同ICC的图像,建议统一把所有图像都转换为sRGB色域后再粘贴,最终保存时也用sRGB的ICC,兼容性最好,不会出现不同图像颜色偏移的问题。
内容的提问来源于stack exchange,提问作者Shashwat Aryal
相关产品推荐
相关产品推荐

