[Image-SIG] Accessing Photoshop layers in PIL?
Fredrik Lundh
fredrik@pythonware.com
Sat, 20 Oct 2001 17:43:02 +0200
This is a multi-part message in MIME format.
------=_NextPart_000_006D_01C1598E.AACE7610
Content-Type: text/plain;
charset="iso-8859-1"
Content-Transfer-Encoding: 7bit
Hamish Lawson wrote:
> Is it possible to access the individual layers of a Photoshop image in
> PIL? By 'layers' I mean in Photoshop's sense - a stack of images that
> overlay each other like acetates to produce the composite image -
> rather than RGB or CYMK channels.
PIL 1.1.2 only loads the first channel. I've attached a
rewritten PsdImagePlugin which allows you to use the
seek/tell methods to select which channel to load:
im = Image.open("spam.psd")
im.seek(2) # get third channel
layer3 = im.copy()
I've tested this code on exactly one sample; let me know
if it works for your images.
</F>
------=_NextPart_000_006D_01C1598E.AACE7610
Content-Type: application/octet-stream;
name="PsdImagePlugin.py"
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
filename="PsdImagePlugin.py"
#
# The Python Imaging Library
# $Id: //modules/pil/PIL/PsdImagePlugin.py#3 $
#
# Adobe PSD 2.5/3.0 file handling
#
# History:
# 1995-09-01 fl Created
# 1997-01-03 fl Read most PSD images
# 1997-01-18 fl Fixed P and CMYK support
# 2001-10-20 fl Added seek/tell support (for channels)
#
# Copyright (c) 1997-2001 by Secret Labs AB.
# Copyright (c) 1995-2001 by Fredrik Lundh
#
# See the README file for information on usage and redistribution.
#
__version__ = "0.4"
import string
import Image, ImageFile, ImagePalette
MODES = {
0: "1",
1: "L",
2: "P",
3: "RGB",
4: "CMYK",
7: "L",
8: "L",
9: "LAB"
}
#
# helpers
def i16(c):
return ord(c[1]) + (ord(c[0])<<8)
def i32(c):
return ord(c[3]) + (ord(c[2])<<8) + (ord(c[1])<<16) + (ord(c[0])<<24)
# --------------------------------------------------------------------.
# read PSD images
def _accept(prefix):
return prefix[:4] == "8BPS"
class PsdImageFile(ImageFile.ImageFile):
format = "PSD"
format_description = "Adobe Photoshop"
def _open(self):
read = self.fp.read
#
# header
s = read(26)
if s[:4] != "8BPS" or i16(s[4:]) != 1:
raise SyntaxError, "not a PSD file"
bits, channels = i16(s[22:]), i16(s[12:])
# FIXME: check number of bits
self.mode = MODES[i16(s[24:])]
self.size = i32(s[18:]), i32(s[14:])
#
# color mode data
size = i32(read(4))
if size:
data = read(size)
if self.mode == "P" and size == 768:
self.palette = ImagePalette.raw("RGB;L", data)
#
# image resources
self.resources = []
size = i32(read(4))
if size:
# load resources
end = self.fp.tell() + size
while self.fp.tell() < end:
signature = read(4)
id = i16(read(2))
name = read(ord(read(1)))
if not (len(name) & 1):
read(1) # padding
data = read(i32(read(4)))
if (len(data) & 1):
read(1) # padding
self.resources.append((id, name, data))
#
# layer and mask information
size = i32(read(4))
if size:
self.fp.seek(size, 1) # ignored, for now
#
# image descriptor
self.channels = []
compression = i16(read(2))
if compression == 0:
#
# raw compression
offset = self.fp.tell()
for channel in range(channels):
tile = []
for layer in self.mode:
if self.mode == "CMYK":
layer = layer + ";I"
tile.append(("raw", (0,0)+self.size, offset, layer))
offset = offset + self.size[0]*self.size[1]
self.channels.append(tile)
elif compression == 1:
#
# packbits compression
i = 0
bytecount = read(channels * self.size[1] * 2)
offset = self.fp.tell()
for channel in range(channels):
tile = []
for layer in self.mode:
if self.mode == "CMYK":
layer = layer + ";I"
tile.append(
("packbits", (0,0)+self.size, offset, layer)
)
for y in range(self.size[1]):
offset = offset + i16(bytecount[i:i+2])
i = i + 2
self.channels.append(tile)
# keep the file open
self.fp2 = self.fp
self.seek(0)
def seek(self, channel):
try:
self.fp = self.fp2
self.tile = self.channels[channel]
self.frame = channel
except:
raise EOFError, "no such channel"
def tell(self, channel):
return self.frame
# --------------------------------------------------------------------
# registry
Image.register_open("PSD", PsdImageFile, _accept)
Image.register_extension("PSD", ".psd")
------=_NextPart_000_006D_01C1598E.AACE7610--