diff options
Diffstat (limited to 'PIL/SpiderImagePlugin.py')
-rw-r--r-- | PIL/SpiderImagePlugin.py | 294 |
1 files changed, 294 insertions, 0 deletions
diff --git a/PIL/SpiderImagePlugin.py b/PIL/SpiderImagePlugin.py new file mode 100644 index 0000000..e0fd045 --- /dev/null +++ b/PIL/SpiderImagePlugin.py @@ -0,0 +1,294 @@ +# +# The Python Imaging Library. +# +# SPIDER image file handling +# +# History: +# 2004-08-02 Created BB +# 2006-03-02 added save method +# 2006-03-13 added support for stack images +# +# Copyright (c) 2004 by Health Research Inc. (HRI) RENSSELAER, NY 12144. +# Copyright (c) 2004 by William Baxter. +# Copyright (c) 2004 by Secret Labs AB. +# Copyright (c) 2004 by Fredrik Lundh. +# + +## +# Image plugin for the Spider image format. This format is is used +# by the SPIDER software, in processing image data from electron +# microscopy and tomography. +## + +# +# SpiderImagePlugin.py +# +# The Spider image format is used by SPIDER software, in processing +# image data from electron microscopy and tomography. +# +# Spider home page: +# http://www.wadsworth.org/spider_doc/spider/docs/spider.html +# +# Details about the Spider image format: +# http://www.wadsworth.org/spider_doc/spider/docs/image_doc.html +# + +import Image, ImageFile +import os, string, struct, sys + +def isInt(f): + try: + i = int(f) + if f-i == 0: return 1 + else: return 0 + except: + return 0 + +iforms = [1,3,-11,-12,-21,-22] + +# There is no magic number to identify Spider files, so just check a +# series of header locations to see if they have reasonable values. +# Returns no.of bytes in the header, if it is a valid Spider header, +# otherwise returns 0 + +def isSpiderHeader(t): + h = (99,) + t # add 1 value so can use spider header index start=1 + # header values 1,2,5,12,13,22,23 should be integers + for i in [1,2,5,12,13,22,23]: + if not isInt(h[i]): return 0 + # check iform + iform = int(h[5]) + if not iform in iforms: return 0 + # check other header values + labrec = int(h[13]) # no. records in file header + labbyt = int(h[22]) # total no. of bytes in header + lenbyt = int(h[23]) # record length in bytes + #print "labrec = %d, labbyt = %d, lenbyt = %d" % (labrec,labbyt,lenbyt) + if labbyt != (labrec * lenbyt): return 0 + # looks like a valid header + return labbyt + +def isSpiderImage(filename): + fp = open(filename,'rb') + f = fp.read(92) # read 23 * 4 bytes + fp.close() + bigendian = 1 + t = struct.unpack('>23f',f) # try big-endian first + hdrlen = isSpiderHeader(t) + if hdrlen == 0: + bigendian = 0 + t = struct.unpack('<23f',f) # little-endian + hdrlen = isSpiderHeader(t) + return hdrlen + + +class SpiderImageFile(ImageFile.ImageFile): + + format = "SPIDER" + format_description = "Spider 2D image" + + def _open(self): + # check header + n = 27 * 4 # read 27 float values + f = self.fp.read(n) + + try: + self.bigendian = 1 + t = struct.unpack('>27f',f) # try big-endian first + hdrlen = isSpiderHeader(t) + if hdrlen == 0: + self.bigendian = 0 + t = struct.unpack('<27f',f) # little-endian + hdrlen = isSpiderHeader(t) + if hdrlen == 0: + raise SyntaxError, "not a valid Spider file" + except struct.error: + raise SyntaxError, "not a valid Spider file" + + h = (99,) + t # add 1 value : spider header index starts at 1 + iform = int(h[5]) + if iform != 1: + raise SyntaxError, "not a Spider 2D image" + + self.size = int(h[12]), int(h[2]) # size in pixels (width, height) + self.istack = int(h[24]) + self.imgnumber = int(h[27]) + + if self.istack == 0 and self.imgnumber == 0: + # stk=0, img=0: a regular 2D image + offset = hdrlen + self.nimages = 1 + elif self.istack > 0 and self.imgnumber == 0: + # stk>0, img=0: Opening the stack for the first time + self.imgbytes = int(h[12]) * int(h[2]) * 4 + self.hdrlen = hdrlen + self.nimages = int(h[26]) + # Point to the first image in the stack + offset = hdrlen * 2 + self.imgnumber = 1 + elif self.istack == 0 and self.imgnumber > 0: + # stk=0, img>0: an image within the stack + offset = hdrlen + self.stkoffset + self.istack = 2 # So Image knows it's still a stack + else: + raise SyntaxError, "inconsistent stack header values" + + if self.bigendian: + self.rawmode = "F;32BF" + else: + self.rawmode = "F;32F" + self.mode = "F" + + self.tile = [("raw", (0, 0) + self.size, offset, + (self.rawmode, 0, 1))] + self.__fp = self.fp # FIXME: hack + + # 1st image index is zero (although SPIDER imgnumber starts at 1) + def tell(self): + if self.imgnumber < 1: + return 0 + else: + return self.imgnumber - 1 + + def seek(self, frame): + if self.istack == 0: + return + if frame >= self.nimages: + raise EOFError, "attempt to seek past end of file" + self.stkoffset = self.hdrlen + frame * (self.hdrlen + self.imgbytes) + self.fp = self.__fp + self.fp.seek(self.stkoffset) + self._open() + + # returns a byte image after rescaling to 0..255 + def convert2byte(self, depth=255): + (min, max) = self.getextrema() + m = 1 + if max != min: + m = depth / (max-min) + b = -m * min + return self.point(lambda i, m=m, b=b: i * m + b).convert("L") + + # returns a ImageTk.PhotoImage object, after rescaling to 0..255 + def tkPhotoImage(self): + import ImageTk + return ImageTk.PhotoImage(self.convert2byte(), palette=256) + +# -------------------------------------------------------------------- +# Image series + +# given a list of filenames, return a list of images +def loadImageSeries(filelist=None): + " create a list of Image.images for use in montage " + if filelist == None or len(filelist) < 1: + return + + imglist = [] + for img in filelist: + if not os.path.exists(img): + print "unable to find %s" % img + continue + try: + im = Image.open(img).convert2byte() + except: + if not isSpiderImage(img): + print img + " is not a Spider image file" + continue + im.info['filename'] = img + imglist.append(im) + return imglist + +# -------------------------------------------------------------------- +# For saving images in Spider format + +def makeSpiderHeader(im): + nsam,nrow = im.size + lenbyt = nsam * 4 # There are labrec records in the header + labrec = 1024 / lenbyt + if 1024%lenbyt != 0: labrec += 1 + labbyt = labrec * lenbyt + hdr = [] + nvalues = labbyt / 4 + for i in range(nvalues): + hdr.append(0.0) + + if len(hdr) < 23: + return [] + + # NB these are Fortran indices + hdr[1] = 1.0 # nslice (=1 for an image) + hdr[2] = float(nrow) # number of rows per slice + hdr[5] = 1.0 # iform for 2D image + hdr[12] = float(nsam) # number of pixels per line + hdr[13] = float(labrec) # number of records in file header + hdr[22] = float(labbyt) # total number of bytes in header + hdr[23] = float(lenbyt) # record length in bytes + + # adjust for Fortran indexing + hdr = hdr[1:] + hdr.append(0.0) + # pack binary data into a string + hdrstr = [] + for v in hdr: + hdrstr.append(struct.pack('f',v)) + return hdrstr + +def _save(im, fp, filename): + if im.mode[0] != "F": + im = im.convert('F') + + hdr = makeSpiderHeader(im) + if len(hdr) < 256: + raise IOError, "Error creating Spider header" + + # write the SPIDER header + try: + fp = open(filename, 'wb') + except: + raise IOError, "Unable to open %s for writing" % filename + fp.writelines(hdr) + + rawmode = "F;32NF" #32-bit native floating point + ImageFile._save(im, fp, [("raw", (0,0)+im.size, 0, (rawmode,0,1))]) + + fp.close() + +def _save_spider(im, fp, filename): + # get the filename extension and register it with Image + fn, ext = os.path.splitext(filename) + Image.register_extension("SPIDER", ext) + _save(im, fp, filename) + +# -------------------------------------------------------------------- + +Image.register_open("SPIDER", SpiderImageFile) +Image.register_save("SPIDER", _save_spider) + +if __name__ == "__main__": + + if not sys.argv[1:]: + print "Syntax: python SpiderImagePlugin.py Spiderimage [outfile]" + sys.exit() + + filename = sys.argv[1] + if not isSpiderImage(filename): + print "input image must be in Spider format" + sys.exit() + + outfile = "" + if len(sys.argv[1:]) > 1: + outfile = sys.argv[2] + + im = Image.open(filename) + print "image: " + str(im) + print "format: " + str(im.format) + print "size: " + str(im.size) + print "mode: " + str(im.mode) + print "max, min: ", + print im.getextrema() + + if outfile != "": + # perform some image operation + im = im.transpose(Image.FLIP_LEFT_RIGHT) + print "saving a flipped version of %s as %s " % (os.path.basename(filename), outfile) + im.save(outfile, "SPIDER") |