Package fabio :: Module tifimage
[hide private]
[frames] | no frames]

Source Code for Module fabio.tifimage

  1  #!/usr/bin/env python 
  2  #-*- coding: utf8 -*- 
  3  """ 
  4  FabIO class for dealing with TIFF images.  
  5  In facts wraps TiffIO from V. Armando Solé (available in PyMca) or falls back to PIL 
  6   
  7  Authors: 
  8  ........ 
  9  * Henning O. Sorensen & Erik Knudsen: 
 10    Center for Fundamental Research: Metal Structures in Four Dimensions; 
 11    Risoe National Laboratory; 
 12    Frederiksborgvej 399; 
 13    DK-4000 Roskilde; 
 14    email:erik.knudsen@risoe.dk 
 15  * Jérôme Kieffer: 
 16    European Synchrotron Radiation Facility; 
 17    Grenoble (France) 
 18      
 19  License: GPLv3+         
 20  """ 
 21   
 22  __authors__ = ["Jérôme Kieffer", "Henning O. Sorensen", "Erik Knudsen"] 
 23  __date__ = "11/07/2011" 
 24  __license__ = "GPLv3+" 
 25  __copyright__ = "ESRF, Grenoble & Risoe National Laboratory" 
 26  __status__ = "stable" 
 27  import time, logging, struct 
 28  logger = logging.getLogger("tifimage") 
 29  try: 
 30      import Image 
 31  except ImportError: 
 32      logger.warning("PIL is not installed ... trying to do without") 
 33      Image = None 
 34  import numpy 
 35  from fabioimage import fabioimage 
 36  from TiffIO     import TiffIO 
 37   
 38  PIL_TO_NUMPY = { "I;16": numpy.uint16, 
 39                     "F": numpy.float32, 
 40                     "1": numpy.bool, 
 41                     "I": numpy.int32, 
 42                     "L": numpy.uint8, 
 43                      } 
 44   
 45  LITTLE_ENDIAN = 1234 
 46  BIG_ENDIAN = 3412 
 47   
 48  TYPES = {0:'invalid', 1:'byte', 2:'ascii', 3:'short', 4:'long', 5:'rational', 6:'sbyte', 7:'undefined', 8:'sshort', 9:'slong', 10:'srational', 11:'float', 12:'double'} 
 49   
 50  TYPESIZES = {0:0, 1:1, 2:1, 3:2, 4:4, 5:8, 6:1, 7:1, 8:2, 9:4, 10:8, 11:4, 12:8} 
 51   
 52  baseline_tiff_tags = { 
 53    256:'ImageWidth', 
 54    257:'ImageLength', 
 55    306:'DateTime', 
 56    315:'Artist', 
 57    258:'BitsPerSample', 
 58    265:'CellLength', 
 59    264:'CellWidth', 
 60    259:'Compression', 
 61   
 62    262:'PhotometricInterpretation', 
 63    296:'ResolutionUnit', 
 64    282:'XResolution', 
 65    283:'YResolution', 
 66   
 67    278:'RowsPerStrip', 
 68    273:'StripOffset', 
 69    279:'StripByteCounts', 
 70   
 71    270:'ImageDescription', 
 72    271:'Make', 
 73    272:'Model', 
 74    320:'ColorMap', 
 75    305:'Software', 
 76    339:'SampleFormat', 
 77    33432:'Copyright' 
 78    } 
 79   
80 -class tifimage(fabioimage):
81 """ 82 Images in TIF format 83 Wraps TiffIO 84 """ 85 _need_a_seek_to_read = True 86
87 - def __init__(self, *args, **kwds):
88 """ Tifimage constructor adds an nbits member attribute """ 89 self.nbits = None 90 fabioimage.__init__(self, *args, **kwds) 91 self.lib = None
92
93 - def _readheader(self, infile):
94 """ 95 Try to read Tiff images header... 96 """ 97 # try: 98 # self.header = { "filename" : infile.name } 99 # except AttributeError: 100 # self.header = {} 101 # 102 # t = Tiff_header(infile.read()) 103 # self.header = t.header 104 # try: 105 # self.dim1 = int(self.header['ImageWidth']) 106 # self.dim2 = int(self.header['ImageLength']) 107 # except (KeyError): 108 # logger.warning("image dimensions could not be determined from header tags, trying to go on anyway") 109 # read the first 32 bytes to determine size 110 header = numpy.fromstring(infile.read(64), numpy.uint16) 111 self.dim1 = int(header[9]) 112 self.dim2 = int(header[15]) 113 # nbits is not a fabioimage attribute... 114 self.nbits = int(header[21]) # number of bits
115
116 - def read(self, fname, frame=None):
117 """ 118 Wrapper for TiffIO. 119 """ 120 infile = self._open(fname, "rb") 121 self._readheader(infile) 122 infile.seek(0) 123 self.lib = None 124 try: 125 tiffIO = TiffIO(infile) 126 if tiffIO.getNumberOfImages() > 0: 127 #No support for now of multi-frame tiff images 128 self.data = tiffIO.getImage(0) 129 self.header = tiffIO.getInfo(0) 130 except Exception, error: 131 logger.warning("Unable to read %s with TiffIO due to %s, trying PIL" % (fname, error)) 132 else: 133 if self.data.ndim == 2: 134 self.dim2, self.dim1 = self.data.shape 135 elif self.data.ndim == 3: 136 self.dim2, self.dim1, ncol = self.data.shape 137 logger.warning("Third dimension is the color") 138 else: 139 logger.warning("dataset has %s dimensions (%s), check for errors !!!!", self.data.ndim, self.data.shape) 140 self.lib = "TiffIO" 141 142 if (self.lib is None): 143 if Image: 144 try: 145 infile.seek(0) 146 self.pilimage = Image.open(infile) 147 except Exception: 148 logger.error("Error in opening %s with PIL" % fname) 149 self.lib = None 150 infile.seek(0) 151 else: 152 self.lib = "PIL" 153 self.dim1, self.dim2 = self.pilimage.size 154 if self.pilimage.mode in PIL_TO_NUMPY: 155 self.data = numpy.fromstring(self.pilimage.tostring(), PIL_TO_NUMPY[self.pilimage.mode]) 156 else: #probably RGB or RGBA images: rely on PIL to convert it to greyscale float. 157 self.data = numpy.fromstring(self.pilimage.convert("F").tostring(), numpy.float32) 158 self.data.shape = (self.dim2, self.dim1) 159 else: 160 logger.error("Error in opening %s: no tiff reader managed to read the file.", fname) 161 self.lib = None 162 infile.seek(0) 163 164 self.bpp = len(numpy.zeros(1, dtype=self.data.dtype).tostring()) 165 self.bytecode = self.data.dtype.name 166 self.resetvals() 167 return self
168
169 - def write(self, fname):
170 """ 171 Overrides the fabioimage.write method and provides a simple TIFF image writer. 172 @param fname: name of the file to save the image to 173 @tag_type fname: string or unicode (file?)... 174 """ 175 tiffIO = TiffIO(fname, mode="w") 176 tiffIO.writeImage(self.data, info=self.header, software="fabio.tifimage", date=time.ctime())
177 178 179 180 #define a couple of helper classes here:
181 -class Tiff_header(object):
182 - def __init__(self, string):
183 if string[:4] == "II\x2a\x00": 184 self.byteorder = LITTLE_ENDIAN 185 elif string[:4] == 'MM\x00\x2a': 186 self.byteorder = BIG_ENDIAN 187 else: 188 logger.warning("Warning: This does not appear to be a tiff file") 189 #the next two bytes contains the offset of the oth IFD 190 offset_first_ifd = struct.unpack_from("h", string[4:])[0] 191 self.ifd = [Image_File_Directory()] 192 offset_next = self.ifd[0].unpack(string, offset_first_ifd) 193 while (offset_next != 0): 194 self.ifd.append(Image_File_Directory()) 195 offset_next = self.ifd[-1].unpack(string, offset_next) 196 197 self.header = {} 198 #read the values of the header items into a dictionary 199 for entry in self.ifd[0].entries: 200 if entry.tag in baseline_tiff_tags.keys(): 201 self.header[baseline_tiff_tags[entry.tag]] = entry.val 202 else: 203 self.header[entry.tag] = entry.val
204
205 -class Image_File_Directory(object):
206 - def __init__(self, instring=None, offset= -1):
207 self.entries = [] 208 self.offset = offset 209 self.count = None
210
211 - def unpack(self, instring, offset= -1):
212 if (offset == -1): offset = self.offset 213 214 strInput = instring[offset:] 215 self.count = struct.unpack_from("H", strInput[:2])[0] 216 #0th IFD contains count-1 entries (count includes the adress of the next IFD) 217 for i in range(self.count - 1): 218 e = Image_File_Directory_entry().unpack(strInput[2 + 12 * (i + 1):]) 219 if (e != None): 220 self.entries.append(e) 221 #extract data associated with tags 222 for e in self.entries: 223 if (e.val == None): 224 e.extract_data(instring) 225 #do we have some more ifds in this file 226 offset_next = struct.unpack_from("L", instring[offset + 2 + self.count * 12:])[0] 227 return offset_next
228
229 -class Image_File_Directory_entry(object):
230 - def __init__(self, tag=0, tag_type=0, count=0, offset=0):
231 self.tag = tag 232 self.tag_type = tag_type 233 self.count = count 234 self.val_offset = offset 235 self.val = None
236
237 - def unpack(self, strInput):
238 idfentry = strInput[:12] 239 ################################################################################ 240 # # TOFIX: How is it possible that HHL (2+2+4 bytes has a size of ) 241 ################################################################################ 242 (tag, tag_type, count) = struct.unpack_from("HHL", idfentry) 243 self.tag = tag 244 self.count = count 245 self.tag_type = tag_type 246 self.val = None 247 if (count <= 0): 248 logger.warning("Tag # %s has an invalid count: %s. Tag is ignored" % (tag, count)) 249 return 250 if(count * TYPESIZES[tag_type] <= 4): 251 self.val_offset = 8 252 self.extract_data(idfentry) 253 self.val_offset = None 254 else: 255 self.val_offset = struct.unpack_from("L", idfentry[8:])[0] 256 return self
257
258 - def extract_data(self, full_string):
259 tag_type = self.tag_type 260 if (TYPES[tag_type] == 'byte'): 261 self.val = struct.unpack_from("B", full_string[self.val_offset:])[0] 262 elif (TYPES[tag_type] == 'short'): 263 self.val = struct.unpack_from("H", full_string[self.val_offset:])[0] 264 elif (TYPES[tag_type] == 'long'): 265 self.val = struct.unpack_from("L", full_string[self.val_offset:])[0] 266 elif (TYPES[tag_type] == 'sbyte'): 267 self.val = struct.unpack_from("b", full_string[self.val_offset:])[0] 268 elif (TYPES[tag_type] == 'sshort'): 269 self.val = struct.unpack_from("h", full_string[self.val_offset:])[0] 270 elif (TYPES[tag_type] == 'slong'): 271 self.val = struct.unpack_from("l", full_string[self.val_offset:])[0] 272 elif (TYPES[tag_type] == 'ascii'): 273 self.val = full_string[self.val_offset:self.val_offset + max(self.count, 4)] 274 elif (TYPES[tag_type] == 'rational'): 275 if self.val_offset != None: 276 (num, den) = struct.unpack_from("LL", full_string[self.val_offset:]) 277 print self.val_offset 278 self.val = float(num) / den 279 elif (TYPES[tag_type] == 'srational'): 280 if self.val_offset != None: 281 (num, den) = struct.unpack_from("ll", full_string[self.val_offset:]) 282 self.val = float(num) / den, 283 elif (TYPES[tag_type] == 'float'): 284 self.val = struct.unpack_from("f", full_string[self.val_offset:])[0] 285 elif (TYPES[tag_type] == 'double'): 286 if self.val_offset != None: 287 self.val = struct.unpack_from("d", full_string[self.val_offset:])[0] 288 else: 289 logger.warning("unrecognized type of strInputentry self: %s tag: %s type: %s TYPE: %s" % (self, baseline_tiff_tags[self.tag], self.tag_type, TYPES[tag_type]))
290