Python脚本生成TIFF:如何设置300DPI与eciRGB v2色彩模型?
解决TIFF文件的300DPI与eciRGB v2色彩模型设置问题
关键修改点说明
1. 设置300DPI
DPI属于TIFF文件的元数据,直接在imwrite函数中配置即可,无需修改图像像素数据:
- 添加
resolution=(300, 300)指定横向/纵向分辨率 - 添加
resolutionunit=2指定单位为英寸(对应DPI的定义)
2. 配置eciRGB v2色彩模型
TIFF的photometric参数仅定义基础色彩类型(如RGB),无法直接指定eciRGB v2这类特定色彩空间,必须通过嵌入ICC配置文件实现:
- 下载eciRGB v2的ICC文件(可从ECI官方渠道获取)
- 读取ICC文件二进制数据,在
imwrite中通过icc参数传入 - 若psdtags库支持对应枚举值,可同步调整图层元数据中的色彩空间定义
修改后的完整代码
from psdtags import * from tifffile import imwrite from imagecodecs import imread import numpy as np from PIL import Image productImage = Image.open('GPD_TEST_Product.png') reflectionImage = Image.open('GPD_TEST_Reflection.png') shadowImage = Image.open('GPD_TEST_Shadow.png') w, h = productImage.size # 创建白色背景 backgroundImage = Image.new(mode="RGBA", size=(w,h), color=(255, 255, 255,255)) background = np.array(backgroundImage) product = np.array(productImage) reflection = np.array(reflectionImage) shadow = np.array(shadowImage) # 合成缩略图 thumbnail = Image.alpha_composite(backgroundImage, reflectionImage) thumbnail = Image.alpha_composite(thumbnail, shadowImage) thumbnail = Image.alpha_composite(thumbnail, productImage) thumbnail = np.array(thumbnail) # 加载eciRGB v2 ICC配置文件,替换为你的实际文件路径 with open('eciRGB_v2.icc', 'rb') as f: eci_icc_data = f.read() image_source_data = TiffImageSourceData( name='Layered TIFF Test', psdformat=PsdFormat.LE32BIT, layers=PsdLayers( key=PsdKey.LAYER, has_transparency=False, layers=[ PsdLayer( name='Background', rectangle=PsdRectangle(0, 0, *background.shape[:2]), channels=[ PsdChannel( channelid=PsdChannelId.CHANNEL0, compression=PsdCompressionType.RLE, data=background[..., 0], ), PsdChannel( channelid=PsdChannelId.CHANNEL1, compression=PsdCompressionType.RLE, data=background[..., 1], ), PsdChannel( channelid=PsdChannelId.CHANNEL2, compression=PsdCompressionType.RLE, data=background[..., 2], ), ], mask=PsdLayerMask(), opacity=255, blendmode=PsdBlendMode.NORMAL, blending_ranges=(), clipping=PsdClippingType.BASE, flags=PsdLayerFlag.PHOTOSHOP5 | PsdLayerFlag.TRANSPARENCY_PROTECTED, info=[ PsdString(PsdKey.UNICODE_LAYER_NAME, 'Background'), ], ), PsdLayer( name='Reflection', rectangle=PsdRectangle(0, 0, *reflection.shape[:2]), channels=[ PsdChannel( channelid=PsdChannelId.TRANSPARENCY_MASK, compression=PsdCompressionType.RLE, data=reflection[..., 3], ), PsdChannel( channelid=PsdChannelId.CHANNEL0, compression=PsdCompressionType.RLE, data=reflection[..., 0], ), PsdChannel( channelid=PsdChannelId.CHANNEL1, compression=PsdCompressionType.RLE, data=reflection[..., 1], ), PsdChannel( channelid=PsdChannelId.CHANNEL2, compression=PsdCompressionType.RLE, data=reflection[..., 2], ), ], mask=PsdLayerMask(), opacity=255, blendmode=PsdBlendMode.NORMAL, blending_ranges=(), clipping=PsdClippingType.BASE, flags=PsdLayerFlag.PHOTOSHOP5, info=[ PsdString(PsdKey.UNICODE_LAYER_NAME, 'Reflection'), ], ), PsdLayer( name='Shadow', rectangle=PsdRectangle(0, 0, *shadow.shape[:2]), channels=[ PsdChannel( channelid=PsdChannelId.TRANSPARENCY_MASK, compression=PsdCompressionType.RLE, data=shadow[..., 3], ), PsdChannel( channelid=PsdChannelId.CHANNEL0, compression=PsdCompressionType.RLE, data=shadow[..., 0], ), PsdChannel( channelid=PsdChannelId.CHANNEL1, compression=PsdCompressionType.RLE, data=shadow[..., 1], ), PsdChannel( channelid=PsdChannelId.CHANNEL2, compression=PsdCompressionType.RLE, data=shadow[..., 2], ), ], mask=PsdLayerMask(), opacity=255, blendmode=PsdBlendMode.NORMAL, blending_ranges=(), clipping=PsdClippingType.BASE, flags=PsdLayerFlag.PHOTOSHOP5, info=[ PsdString(PsdKey.UNICODE_LAYER_NAME, 'Shadow'), ], ), PsdLayer( name='Product', rectangle=PsdRectangle(0, 0, *product.shape[:2]), channels=[ PsdChannel( channelid=PsdChannelId.TRANSPARENCY_MASK, compression=PsdCompressionType.RLE, data=product[..., 3], ), PsdChannel( channelid=PsdChannelId.CHANNEL0, compression=PsdCompressionType.RLE, data=product[..., 0], ), PsdChannel( channelid=PsdChannelId.CHANNEL1, compression=PsdCompressionType.RLE, data=product[..., 1], ), PsdChannel( channelid=PsdChannelId.CHANNEL2, compression=PsdCompressionType.RLE, data=product[..., 2], ), ], mask=PsdLayerMask(), opacity=255, blendmode=PsdBlendMode.NORMAL, blending_ranges=(), clipping=PsdClippingType.BASE, flags=PsdLayerFlag.PHOTOSHOP5, info=[ PsdString(PsdKey.UNICODE_LAYER_NAME, 'Product'), ], ), ], ), usermask=PsdUserMask( # 若psdtags支持eciRGB色彩空间枚举,可替换此处的RGB colorspace=PsdColorSpaceType.RGB, components=(65535, 0, 0, 0), opacity=50, ), info=[ PsdEmpty(PsdKey.PATTERNS), PsdFilterMask( # 同上,若支持则替换色彩空间 colorspace=PsdColorSpaceType.RGB, components=(65535, 0, 0, 0), opacity=50, ), ], ) imwrite( 'LayeredTiffTest.tif', thumbnail, photometric='rgb', metadata=None, extratags=[image_source_data.tifftag()], # 设置300DPI resolution=(300, 300), resolutionunit=2, # 嵌入eciRGB v2 ICC配置文件 icc=eci_icc_data )
注意事项
- 需替换代码中
eciRGB_v2.icc为你的实际文件路径 - 如果原始图像是sRGB色彩空间,建议先将图像转换为eciRGB v2再合成,否则仅嵌入ICC文件可能导致色彩显示偏差(可使用PIL或colorio库完成色彩转换)
内容的提问来源于stack exchange,提问作者konradns
相关产品推荐
相关产品推荐

