fabio.tifimage

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