# 
 | 
# 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, 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") 
 |