Create a multi layer tiff without visible modification in wand/imagemagic/python/js
Question:
I need to create a multi layer tif and when I open in photoshop in need to be as separated layers like in the picture:
I need to do it without photoshop so I though so that I can do with Wand in python:
from wand.image import Image
with Image(filename="./GPD_TEST_Product.png") as img:
img.read(filename="./GPD_TEST_Reflection.png")
img.read(filename="./GPD_TEST_Shadow.png")
img.read(filename="./GPD_TEST_Background.png")
img.save(filename="final.tif")
but when I open it with photoshop I got:
but when I open it with preview on mac I can see the layers…
My other idea is to create a psd with layers and try to convert it to .tif with imagemagic, but still when I open it with photoshop, is merged.
here is the file which I’m working on:
https://drive.google.com/drive/folders/1ioWG2rJ32FVstUYJK8ITuhmGSUr7I_Ps?fbclid=IwAR1HtNsChsr6befuUTPHklqKREtwxCMLk39cRhbeshio_MU0fGU-P35kxlU
Any help will be appreciate.
Answers:
The layer and thumbnail image and metadata of layered TIFF files are stored in the ImageResources (#34377) and ImageSourceData (#37724) TIFF tags. The psdtags Python library can read and write those tags but creating the tags from scratch requires knowledge of the format. Something like this should work (I don’t have Photoshop to verify):
from psdtags import *
from tifffile import imwrite
from imagecodecs import imread
background = imread('GPD_TEST_Background.png')
product = imread('GPD_TEST_Product.png')
reflection = imread('GPD_TEST_Reflection.png')
shadow = imread('GPD_TEST_Shadow.png')
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(
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',
background,
photometric='rgb',
metadata=None,
extratags=[image_source_data.tifftag()],
)
print(TiffImageSourceData.fromtiff('LayeredTiffTest.tif'))
If anyone need generating tiff with layers and thumbnail is not white background but composite image here is my solution:
from psdtags import *
from tifffile import imwrite
from imagecodecs import imread
import numpy as np
from PIL import Image
# backgroundImage = Image.open('GPD_TEST_Background.png')
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
# create background with defined color
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
thumbnail = Image.alpha_composite(backgroundImage, reflectionImage)
thumbnail = Image.alpha_composite(thumbnail, shadowImage)
thumbnail = Image.alpha_composite(thumbnail, productImage)
thumbnail = np.array(thumbnail)
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(
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()],
)
print(TiffImageSourceData.fromtiff('LayeredTiffTest.tif'))
What do you think about it?
I need to create a multi layer tif and when I open in photoshop in need to be as separated layers like in the picture:
I need to do it without photoshop so I though so that I can do with Wand in python:
from wand.image import Image
with Image(filename="./GPD_TEST_Product.png") as img:
img.read(filename="./GPD_TEST_Reflection.png")
img.read(filename="./GPD_TEST_Shadow.png")
img.read(filename="./GPD_TEST_Background.png")
img.save(filename="final.tif")
but when I open it with photoshop I got:
but when I open it with preview on mac I can see the layers…
My other idea is to create a psd with layers and try to convert it to .tif with imagemagic, but still when I open it with photoshop, is merged.
here is the file which I’m working on:
https://drive.google.com/drive/folders/1ioWG2rJ32FVstUYJK8ITuhmGSUr7I_Ps?fbclid=IwAR1HtNsChsr6befuUTPHklqKREtwxCMLk39cRhbeshio_MU0fGU-P35kxlU
Any help will be appreciate.
The layer and thumbnail image and metadata of layered TIFF files are stored in the ImageResources (#34377) and ImageSourceData (#37724) TIFF tags. The psdtags Python library can read and write those tags but creating the tags from scratch requires knowledge of the format. Something like this should work (I don’t have Photoshop to verify):
from psdtags import *
from tifffile import imwrite
from imagecodecs import imread
background = imread('GPD_TEST_Background.png')
product = imread('GPD_TEST_Product.png')
reflection = imread('GPD_TEST_Reflection.png')
shadow = imread('GPD_TEST_Shadow.png')
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(
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',
background,
photometric='rgb',
metadata=None,
extratags=[image_source_data.tifftag()],
)
print(TiffImageSourceData.fromtiff('LayeredTiffTest.tif'))
If anyone need generating tiff with layers and thumbnail is not white background but composite image here is my solution:
from psdtags import *
from tifffile import imwrite
from imagecodecs import imread
import numpy as np
from PIL import Image
# backgroundImage = Image.open('GPD_TEST_Background.png')
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
# create background with defined color
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
thumbnail = Image.alpha_composite(backgroundImage, reflectionImage)
thumbnail = Image.alpha_composite(thumbnail, shadowImage)
thumbnail = Image.alpha_composite(thumbnail, productImage)
thumbnail = np.array(thumbnail)
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(
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()],
)
print(TiffImageSourceData.fromtiff('LayeredTiffTest.tif'))
What do you think about it?