fabio.TiffIO

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

# Source Code for Module fabio.TiffIO

       1  #/*##########################################################################
       2  # Copyright (C) 2012 European Synchrotron Radiation Facility
       3  #
       4  # This file is part of the PyMca X-ray Fluorescence Toolkit developed at
       5  # the ESRF by the Software group.
       6  #
       7  # This file is free software; you can redistribute it and/or modify it
       8  # under the terms of the GNU Lesser General Public License as published by the Free
       9  # Software Foundation; either version 2 of the License, or (at your option)
      10  # any later version.
      11  #
      12  # PyMca is distributed in the hope that it will be useful, but WITHOUT ANY
      13  # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
      14  # FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more
      15  # details.
      16  #
      17  #############################################################################*/
      18  __author__ = "V.A. Sole - ESRF Data Analysis"
      19  __revision__ = 1501
      20
      21  import sys
      22  import os
      23  import struct
      24  import numpy
      25
      26  DEBUG = 0
      27  ALLOW_MULTIPLE_STRIPS = False
      28
      29  TAG_ID = { 256:"NumberOfColumns", # S or L ImageWidth
      30              257:"NumberOfRows", # S or L ImageHeight
      31              258:"BitsPerSample", # S Number of bits per component
      32              259:"Compression", # SHORT (1 - NoCompression, ...
      33              262:"PhotometricInterpretation", # SHORT (0 - WhiteIsZero, 1 -BlackIsZero, 2 - RGB, 3 - Palette color
      34              270:"ImageDescription", # ASCII
      35              273:"StripOffsets", # S or L, for each strip, the byte offset of the strip
      36              278:"RowsPerStrip", # S or L, number of rows in each back may be not for the last
      37              279:"StripByteCounts", # S or L, The number of bytes in the strip AFTER any compression
      38              305:"Software", # ASCII
      39              306:"Date", # ASCII
      40              320:"Colormap", # Colormap of Palette-color Images
      41              339:"SampleFormat", # SHORT Interpretation of data in each pixel
      42              }
      43
      44  #TILES ARE TO BE SUPPORTED TOO ...
      45  TAG_NUMBER_OF_COLUMNS = 256
      46  TAG_NUMBER_OF_ROWS = 257
      47  TAG_BITS_PER_SAMPLE = 258
      48  TAG_PHOTOMETRIC_INTERPRETATION = 262
      49  TAG_COMPRESSION = 259
      50  TAG_IMAGE_DESCRIPTION = 270
      51  TAG_STRIP_OFFSETS = 273
      52  TAG_ROWS_PER_STRIP = 278
      53  TAG_STRIP_BYTE_COUNTS = 279
      54  TAG_SOFTWARE = 305
      55  TAG_DATE = 306
      56  TAG_COLORMAP = 320
      57  TAG_SAMPLE_FORMAT = 339
      58
      59  FIELD_TYPE = {1:('BYTE', "B"),
      60                 2:('ASCII', "s"), #string ending with binary zero
      61                 3:('SHORT', "H"),
      62                 4:('LONG', "I"),
      63                 5:('RATIONAL', "II"),
      64                 6:('SBYTE', "b"),
      65                 7:('UNDEFINED', "B"),
      66                 8:('SSHORT', "h"),
      67                 9:('SLONG', "i"),
      68                 10:('SRATIONAL', "ii"),
      69                 11:('FLOAT', "f"),
      70                 12:('DOUBLE', "d")}
      71
      72  FIELD_TYPE_OUT = { 'B':   1,
      73                     's':   2,
      74                     'H':   3,
      75                     'I':   4,
      76                     'II':  5,
      77                     'b':   6,
      78                     'h':   8,
      79                     'i':   9,
      80                     'ii': 10,
      81                     'f':  11,
      82                     'd':  12}
      83
      84  #sample formats (http://www.awaresystems.be/imaging/tiff/tiffflags/sampleformat.html)
      85  SAMPLE_FORMAT_UINT = 1
      86  SAMPLE_FORMAT_INT = 2
      87  SAMPLE_FORMAT_FLOAT = 3   #floating point
      88  SAMPLE_FORMAT_VOID = 4   #undefined data, usually assumed UINT
      89  SAMPLE_FORMAT_COMPLEXINT = 5
      90  SAMPLE_FORMAT_COMPLEXIEEEFP = 6
      91
      92
      93
      94 -class TiffIO(object):
      95 -    def __init__(self, filename, mode=None, cache_length=20, mono_output=False):
      96          if mode is None:
      97              mode = 'rb'
      98          if 'b' not in mode:
      99              mode = mode + 'b'
     100          if 'a' in mode.lower():
     101              raise IOError("Mode %s makes no sense on TIFF files. Consider 'rb+'" % mode)
     102          if ('w' in mode):
     103              if '+' not in mode:
     104                  mode += '+'
     105          #if isinstance(filename, file): #does not work in python 3
     106          if hasattr(filename, "seek"):
     107              fd = filename
     108              self._access = None
     109          else:
     110              #the b is needed for windows and python 3
     111              fd = open(filename, mode)
     112              self._access = mode
     113
     114          self._initInternalVariables(fd)
     115          self._maxImageCacheLength = cache_length
     116          self._forceMonoOutput = mono_output
     117
     118 -    def _initInternalVariables(self, fd=None):
     119          if fd is None:
     120              fd = self.fd
     121          else:
     122              self.fd = fd
     123          # read the order
     124          fd.seek(0)
     125          order = fd.read(2).decode()
     126          if len(order):
     127              if order == "II":
     128                  #intel, little endian
     129                  fileOrder = "little"
     130                  self._structChar = '<'
     131              elif order == "MM":
     132                  #motorola, high endian
     133                  fileOrder = "big"
     134                  self._structChar = '>'
     135              else:
     136                  raise IOError("File is not a Mar CCD file, nor a TIFF file")
     137              a = fd.read(2)
     138              fortyTwo = struct.unpack(self._structChar + "H", a)[0]
     139              if fortyTwo != 42:
     140                  raise IOError("Invalid TIFF version %d" % fortyTwo)
     141              else:
     142                  if DEBUG:
     143                      print("VALID TIFF VERSION")
     144              if sys.byteorder != fileOrder:
     145                  swap = True
     146              else:
     147                  swap = False
     148          else:
     149              if sys.byteorder == "little":
     150                  self._structChar = '<'
     151              else:
     152                  self._structChar = '>'
     153              swap = False
     154          self._swap = swap
     155          self._IFD = []
     156          self._imageDataCacheIndex = []
     157          self._imageDataCache = []
     158          self._imageInfoCacheIndex = []
     159          self._imageInfoCache = []
     160          self.getImageFileDirectories(fd)
     161
     162 -    def __makeSureFileIsOpen(self):
     163          if not self.fd.closed:
     164              return
     165          if DEBUG:
     166              print("Reopening closed file")
     167          fileName = self.fd.name
     168          if self._access is None:
     169              #we do not own the file
     170              #open in read mode
     171              newFile = open(fileName, 'rb')
     172          else:
     173              newFile = open(fileName, self._access)
     174          self.fd = newFile
     175
     176 -    def __makeSureFileIsClosed(self):
     177          if self._access is None:
     178              #we do not own the file
     179              if DEBUG:
     180                  print("Not closing not owned file")
     181              return
     182
     183          if not self.fd.closed:
     184              self.fd.close()
     185
     186 -    def getNumberOfImages(self):
     187          #update for the case someone has done anything?
     188          self._updateIFD()
     189          return len(self._IFD)
     190
     191 -    def _updateIFD(self):
     192          self.__makeSureFileIsOpen()
     193          self.getImageFileDirectories()
     194          self.__makeSureFileIsClosed()
     195
     196 -    def getImageFileDirectories(self, fd=None):
     197          if fd is None:
     198              fd = self.fd
     199          else:
     200              self.fd = fd
     201          st = self._structChar
     202          fd.seek(4)
     203          self._IFD = []
     204          nImages = 0
     205          fmt = st + 'I'
     206          inStr = fd.read(struct.calcsize(fmt))
     207          if not len(inStr):
     208              offsetToIFD = 0
     209          else:
     210              offsetToIFD = struct.unpack(fmt, inStr)[0]
     211          if DEBUG:
     212              print("Offset to first IFD = %d" % offsetToIFD)
     213          while offsetToIFD != 0:
     214              self._IFD.append(offsetToIFD)
     215              nImages += 1
     216              fd.seek(offsetToIFD)
     217              fmt = st + 'H'
     218              numberOfDirectoryEntries = struct.unpack(fmt, fd.read(struct.calcsize(fmt)))[0]
     219              if DEBUG:
     220                  print("Number of directory entries = %d" % numberOfDirectoryEntries)
     221
     222              fmt = st + 'I'
     223              fd.seek(offsetToIFD + 2 + 12 * numberOfDirectoryEntries)
     224              offsetToIFD = struct.unpack(fmt, fd.read(struct.calcsize(fmt)))[0]
     225              if DEBUG:
     226                  print("Next Offset to IFD = %d" % offsetToIFD)
     227              #offsetToIFD = 0
     228          if DEBUG:
     229              print("Number of images found = %d" % nImages)
     230          return nImages
     231
     232 -    def _parseImageFileDirectory(self, nImage):
     233          offsetToIFD = self._IFD[nImage]
     234          st = self._structChar
     235          fd = self.fd
     236          fd.seek(offsetToIFD)
     237          fmt = st + 'H'
     238          numberOfDirectoryEntries = struct.unpack(fmt, fd.read(struct.calcsize(fmt)))[0]
     239          if DEBUG:
     240              print("Number of directory entries = %d" % numberOfDirectoryEntries)
     241
     242          fmt = st + 'HHI4s'
     243          tagIDList = []
     244          fieldTypeList = []
     245          nValuesList = []
     246          valueOffsetList = []
     247          for i in range(numberOfDirectoryEntries):
     248              tagID, fieldType, nValues, valueOffset = struct.unpack(fmt, fd.read(12))
     249              tagIDList.append(tagID)
     250              fieldTypeList.append(fieldType)
     251              nValuesList.append(nValues)
     252              if nValues == 1:
     253                  ftype, vfmt = FIELD_TYPE[fieldType]
     254                  if ftype not in ['ASCII', 'RATIONAL', 'SRATIONAL']:
     255                      vfmt = st + vfmt
     256                      actualValue = struct.unpack(vfmt, valueOffset[0: struct.calcsize(vfmt)])[0]
     257                      valueOffsetList.append(actualValue)
     258                  else:
     259                      valueOffsetList.append(valueOffset)
     260              elif (nValues < 5) and (fieldType == 2):
     261                  ftype, vfmt = FIELD_TYPE[fieldType]
     262                  vfmt = st + "%d%s" % (nValues, vfmt)
     263                  actualValue = struct.unpack(vfmt, valueOffset[0: struct.calcsize(vfmt)])[0]
     264                  valueOffsetList.append(actualValue)
     265              else:
     266                  valueOffsetList.append(valueOffset)
     267              if DEBUG:
     268                  if tagID in TAG_ID:
     269                      print("tagID = %s" % TAG_ID[tagID])
     270                  else:
     271                      print("tagID        = %d" % tagID)
     272                  print("fieldType    = %s" % FIELD_TYPE[fieldType][0])
     273                  print("nValues      = %d" % nValues)
     274                  #if nValues == 1:
     275                  #    print("valueOffset =  %s" % valueOffset)
     276          return tagIDList, fieldTypeList, nValuesList, valueOffsetList
     277
     278
     279
     280 -    def _readIFDEntry(self, tag, tagIDList, fieldTypeList, nValuesList, valueOffsetList):
     281          fd = self.fd
     282          st = self._structChar
     283          idx = tagIDList.index(tag)
     284          nValues = nValuesList[idx]
     285          output = []
     286          ftype, vfmt = FIELD_TYPE[fieldTypeList[idx]]
     287          vfmt = st + "%d%s" % (nValues, vfmt)
     288          requestedBytes = struct.calcsize(vfmt)
     289          if nValues == 1:
     290              output.append(valueOffsetList[idx])
     291          elif requestedBytes < 5:
     292              output.append(valueOffsetList[idx])
     293          else:
     294              offset = fd.seek(struct.unpack(st + "I", valueOffsetList[idx])[0])
     295              output = struct.unpack(vfmt, fd.read(requestedBytes))
     296          return output
     297
     298 -    def getData(self, nImage, **kw):
     299          if nImage >= len(self._IFD):
     300              #update prior to raise an index error error
     301              self._updateIFD()
     302          return self._readImage(nImage, **kw)
     303
     304 -    def getImage(self, nImage):
     305          return self.getData(nImage)
     306
     307 -    def getInfo(self, nImage, **kw):
     308          if nImage >= len(self._IFD):
     309              #update prior to raise an index error error
     310              self._updateIFD()
     311          current = self._IFD[nImage]
     312          return self._readInfo(nImage)
     313
     314 -    def _readInfo(self, nImage, close=True):
     315          if nImage in self._imageInfoCacheIndex:
     316              if DEBUG:
     317                  print("Reading info from cache")
     318              return self._imageInfoCache[self._imageInfoCacheIndex.index(nImage)]
     319
     320          #read the header
     321          self.__makeSureFileIsOpen()
     322          tagIDList, fieldTypeList, nValuesList, valueOffsetList = self._parseImageFileDirectory(nImage)
     323
     324          #rows and columns
     325          nColumns = valueOffsetList[tagIDList.index(TAG_NUMBER_OF_COLUMNS)]
     326          nRows = valueOffsetList[tagIDList.index(TAG_NUMBER_OF_ROWS)]
     327
     328          #bits per sample
     329          idx = tagIDList.index(TAG_BITS_PER_SAMPLE)
     330          nBits = valueOffsetList[idx]
     331          if nValuesList[idx] != 1:
     332              #this happens with RGB and friends, nBits is not a single value
     333              nBits = self._readIFDEntry(TAG_BITS_PER_SAMPLE,
     334                                            tagIDList, fieldTypeList, nValuesList, valueOffsetList)
     335
     336
     337          if TAG_COLORMAP in tagIDList:
     338              idx = tagIDList.index(TAG_COLORMAP)
     339              tmpColormap = self._readIFDEntry(TAG_COLORMAP,
     340                                            tagIDList, fieldTypeList, nValuesList, valueOffsetList)
     341              if max(tmpColormap) > 255:
     342                  tmpColormap = numpy.array(tmpColormap, dtype=numpy.uint16)
     343                  tmpColormap = (tmpColormap / 256.).astype(numpy.uint8)
     344              else:
     345                  tmpColormap = numpy.array(tmpColormap, dtype=numpy.uint8)
     346              tmpColormap.shape = 3, -1
     347              colormap = numpy.zeros((tmpColormap.shape[-1], 3), tmpColormap.dtype)
     348              colormap[:, :] = tmpColormap.T
     349              tmpColormap = None
     350          else:
     351              colormap = None
     352
     353          #sample format
     354          if TAG_SAMPLE_FORMAT in tagIDList:
     355              sampleFormat = valueOffsetList[tagIDList.index(TAG_SAMPLE_FORMAT)]
     356          else:
     357              #set to unknown
     358              sampleFormat = SAMPLE_FORMAT_VOID
     359
     360          # compression
     361          compression = False
     362          compression_type = 1
     363          if TAG_COMPRESSION in tagIDList:
     364              compression_type = valueOffsetList[tagIDList.index(TAG_COMPRESSION)]
     365              if compression_type == 1:
     366                  compression = False
     367              else:
     368                  compression = True
     369
     370          #photometric interpretation
     371          interpretation = 1
     372          if TAG_PHOTOMETRIC_INTERPRETATION in tagIDList:
     373              interpretation = valueOffsetList[tagIDList.index(TAG_PHOTOMETRIC_INTERPRETATION)]
     374          else:
     375              print("WARNING: Non standard TIFF. Photometric interpretation TAG missing")
     376          helpString = ""
     377          if sys.version > '2.6':
     378              helpString = eval('b""')
     379
     380          if TAG_IMAGE_DESCRIPTION in tagIDList:
     381              imageDescription = self._readIFDEntry(TAG_IMAGE_DESCRIPTION,
     382                      tagIDList, fieldTypeList, nValuesList, valueOffsetList)
     383              if type(imageDescription) in [type([1]), type((1,))]:
     384                  imageDescription = helpString.join(imageDescription)
     385          else:
     386              imageDescription = "%d/%d" % (nImage + 1, len(self._IFD))
     387
     388          if sys.version < '3.0':
     389              defaultSoftware = "Unknown Software"
     390          else:
     391              defaultSoftware = bytes("Unknown Software",
     392                                      encoding='utf-8')
     393          if TAG_SOFTWARE in tagIDList:
     394              software = self._readIFDEntry(TAG_SOFTWARE,
     395                      tagIDList, fieldTypeList, nValuesList, valueOffsetList)
     396              if type(software) in [type([1]), type((1,))]:
     397                  software = helpString.join(software)
     398          else:
     399              software = defaultSoftware
     400
     401          if software == defaultSoftware:
     402              try:
     403                  if sys.version < '3.0':
     404                      if imageDescription.upper().startswith("IMAGEJ"):
     405                          software = imageDescription.split("=")[0]
     406                  else:
     407                      tmpString = imageDescription.decode()
     408                      if tmpString.upper().startswith("IMAGEJ"):
     409                          software = bytes(tmpString.split("=")[0],
     410                                           encoding='utf-8')
     411              except:
     412                  pass
     413
     414          if TAG_DATE in tagIDList:
     415              date = self._readIFDEntry(TAG_DATE,
     416                      tagIDList, fieldTypeList, nValuesList, valueOffsetList)
     417              if type(date) in [type([1]), type((1,))]:
     418                  date = helpString.join(date)
     419          else:
     420              date = "Unknown Date"
     421
     422          stripOffsets = self._readIFDEntry(TAG_STRIP_OFFSETS,
     423                          tagIDList, fieldTypeList, nValuesList, valueOffsetList)
     424          if TAG_ROWS_PER_STRIP in tagIDList:
     425              rowsPerStrip = self._readIFDEntry(TAG_ROWS_PER_STRIP,
     426                          tagIDList, fieldTypeList, nValuesList, valueOffsetList)[0]
     427          else:
     428              rowsPerStrip = nRows
     429              print("WARNING: Non standard TIFF. Rows per strip TAG missing")
     430
     431          if TAG_STRIP_BYTE_COUNTS in tagIDList:
     432              stripByteCounts = self._readIFDEntry(TAG_STRIP_BYTE_COUNTS,
     433                          tagIDList, fieldTypeList, nValuesList, valueOffsetList)
     434          else:
     435              print("WARNING: Non standard TIFF. Strip byte counts TAG missing")
     436              if hasattr(nBits, 'index'):
     437                  expectedSum = 0
     438                  for n in nBits:
     439                      expectedSum += int(nRows * nColumns * n / 8)
     440              else:
     441                  expectedSum = int(nRows * nColumns * nBits / 8)
     442              stripByteCounts = [expectedSum]
     443
     444          if close:
     445              self.__makeSureFileIsClosed()
     446
     447          if self._forceMonoOutput and (interpretation > 1):
     448              #color image but asked monochrome output
     449              nBits = 32
     450              colormap = None
     451              sampleFormat = SAMPLE_FORMAT_FLOAT
     452              interpretation = 1
     453              #we cannot rely on any cache in this case
     454              useInfoCache = False
     455              if DEBUG:
     456                  print("FORCED MONO")
     457          else:
     458              useInfoCache = True
     459
     460          info = {}
     461          info["nRows"] = nRows
     462          info["nColumns"] = nColumns
     463          info["nBits"] = nBits
     464          info["compression"] = compression
     465          info["compression_type"] = compression_type
     466          info["imageDescription"] = imageDescription
     467          info["stripOffsets"] = stripOffsets #This contains the file offsets to the data positions
     468          info["rowsPerStrip"] = rowsPerStrip
     469          info["stripByteCounts"] = stripByteCounts #bytes in strip since I do not support compression
     470          info["software"] = software
     471          info["date"] = date
     472          info["colormap"] = colormap
     473          info["sampleFormat"] = sampleFormat
     474          info["photometricInterpretation"] = interpretation
     475          infoDict = {}
     476          if sys.version < '3.0':
     477              testString = 'PyMca'
     478          else:
     479              testString = eval('b"PyMca"')
     480          if software.startswith(testString):
     481              #str to make sure python 2.x sees it as string and not unicode
     482              if sys.version < '3.0':
     483                  descriptionString = imageDescription
     484              else:
     485                  descriptionString = str(imageDescription.decode())
     486              #interpret the image description in terms of supplied
     487              #information at writing time
     488              items = descriptionString.split('=')
     489              for i in range(int(len(items) / 2)):
     490                  key = "%s" % items[i * 2]
     491                  #get rid of the \n at the end of the value
     492                  value = "%s" % items[i * 2 + 1][:-1]
     493                  infoDict[key] = value
     494          info['info'] = infoDict
     495
     496          if (self._maxImageCacheLength > 0) and useInfoCache:
     497              self._imageInfoCacheIndex.insert(0, nImage)
     498              self._imageInfoCache.insert(0, info)
     499              if len(self._imageInfoCacheIndex) > self._maxImageCacheLength:
     500                  self._imageInfoCacheIndex = self._imageInfoCacheIndex[:self._maxImageCacheLength]
     501                  self._imageInfoCache = self._imageInfoCache[:self._maxImageCacheLength]
     502          return info
     503
     504 -    def _readImage(self, nImage, **kw):
     505          if DEBUG:
     506              print("Reading image %d" % nImage)
     507          if 'close' in kw:
     508              close = kw['close']
     509          else:
     510              close = True
     511          rowMin = kw.get('rowMin', None)
     512          rowMax = kw.get('rowMax', None)
     513          if nImage in self._imageDataCacheIndex:
     514              if DEBUG:
     515                  print("Reading image data from cache")
     516              return self._imageDataCache[self._imageDataCacheIndex.index(nImage)]
     517
     518          self.__makeSureFileIsOpen()
     519          if self._forceMonoOutput:
     520              oldMono = True
     521          else:
     522              oldMono = False
     523          try:
     524              self._forceMonoOutput = False
     525              info = self._readInfo(nImage, close=False)
     526              self._forceMonoOutput = oldMono
     527          except:
     528              self._forceMonoOutput = oldMono
     529              raise
     530          compression = info['compression']
     531          compression_type = info['compression_type']
     532          if compression:
     533              if compression_type != 32773:
     534                  raise IOError("Compressed TIFF images not supported except packbits")
     535              else:
     536                  #PackBits compression
     537                  if DEBUG:
     538                      print("Using PackBits compression")
     539
     540          interpretation = info["photometricInterpretation"]
     541          if interpretation == 2:
     542              #RGB
     543              pass
     544              #raise IOError("RGB Image. Only grayscale images supported")
     545          elif interpretation == 3:
     546              #Palette Color Image
     547              pass
     548              #raise IOError("Palette-color Image. Only grayscale images supported")
     549          elif interpretation > 2:
     550              #Palette Color Image
     551              raise IOError("Only grayscale images supported")
     552
     553          nRows = info["nRows"]
     554          nColumns = info["nColumns"]
     555          nBits = info["nBits"]
     556          colormap = info["colormap"]
     557          sampleFormat = info["sampleFormat"]
     558
     559          if rowMin is None:
     560              rowMin = 0
     561
     562          if rowMax is None:
     563              rowMax = nRows - 1
     564
     565          if rowMin < 0:
     566              rowMin = nRows - rowMin
     567
     568          if rowMax < 0:
     569              rowMax = nRows - rowMax
     570
     571          if rowMax < rowMin:
     572              txt = "Max Row smaller than Min Row. Reverse selection not supported"
     573              raise NotImplemented(txt)
     574
     575          if rowMin >= nRows:
     576              raise IndexError("Image only has %d rows" % nRows)
     577
     578          if rowMax >= nRows:
     579              raise IndexError("Image only has %d rows" % nRows)
     580
     581          if sampleFormat == SAMPLE_FORMAT_FLOAT:
     582              if nBits == 32:
     583                  dtype = numpy.float32
     584              elif nBits == 64:
     585                  dtype = numpy.float64
     586              else:
     587                  raise ValueError("Unsupported number of bits for a float: %d" % nBits)
     588          elif sampleFormat in [SAMPLE_FORMAT_UINT, SAMPLE_FORMAT_VOID]:
     589              if nBits in [8, (8, 8, 8), [8, 8, 8]]:
     590                  dtype = numpy.uint8
     591              elif nBits in [16, (16, 16, 16), [16, 16, 16]]:
     592                  dtype = numpy.uint16
     593              elif nBits in [32, (32, 32, 32), [32, 32, 32]]:
     594                  dtype = numpy.uint32
     595              elif nBits in [64, (64, 64, 64), [64, 64, 64]]:
     596                  dtype = numpy.uint64
     597              else:
     598                  raise ValueError("Unsupported number of bits for unsigned int: %s" % (nBits,))
     599          elif sampleFormat == SAMPLE_FORMAT_INT:
     600              if nBits in [8, (8, 8, 8), [8, 8, 8]]:
     601                  dtype = numpy.int8
     602              elif nBits in [16, (16, 16, 16), [16, 16, 16]]:
     603                  dtype = numpy.int16
     604              elif nBits in [32, (32, 32, 32), [32, 32, 32]]:
     605                  dtype = numpy.int32
     606              elif nBits in [64, (64, 64, 64), [64, 64, 64]]:
     607                  dtype = numpy.int64
     608              else:
     609                  raise ValueError("Unsupported number of bits for signed int: %s" % (nBits,))
     610          else:
     611              raise ValueError("Unsupported combination. Bits = %s  Format = %d" % (nBits, sampleFormat))
     612          if hasattr(nBits, 'index'):
     613              image = numpy.zeros((nRows, nColumns, len(nBits)), dtype=dtype)
     614          elif colormap is not None:
     615              #should I use colormap dtype?
     616              image = numpy.zeros((nRows, nColumns, 3), dtype=dtype)
     617          else:
     618              image = numpy.zeros((nRows, nColumns), dtype=dtype)
     619
     620          fd = self.fd
     621          st = self._structChar
     622          stripOffsets = info["stripOffsets"] #This contains the file offsets to the data positions
     623          rowsPerStrip = info["rowsPerStrip"]
     624          stripByteCounts = info["stripByteCounts"] #bytes in strip since I do not support compression
     625
     626          rowStart = 0
     627          if len(stripOffsets) == 1:
     628              bytesPerRow = int(stripByteCounts[0] / rowsPerStrip)
     629              fd.seek(stripOffsets[0] + rowMin * bytesPerRow)
     630              nBytes = (rowMax - rowMin + 1) * bytesPerRow
     631              if self._swap:
     632                  readout = numpy.fromstring(fd.read(nBytes), dtype).byteswap()
     633              else:
     634                  readout = numpy.fromstring(fd.read(nBytes), dtype)
     635              if hasattr(nBits, 'index'):
     636                  readout.shape = -1, nColumns, len(nBits)
     637              elif info['colormap'] is not None:
     638                  readout = colormap[readout]
     639              else:
     640                  readout.shape = -1, nColumns
     641              image[rowMin:rowMax + 1, :] = readout
     642          else:
     643              for i in range(len(stripOffsets)):
     644                  #the amount of rows
     645                  nRowsToRead = rowsPerStrip
     646                  rowEnd = int(min(rowStart + nRowsToRead, nRows))
     647                  if rowEnd < rowMin:
     648                      rowStart += nRowsToRead
     649                      continue
     650                  if (rowStart > rowMax):
     651                      break
     652                  #we are in position
     653                  fd.seek(stripOffsets[i])
     654                  #the amount of bytes to read
     655                  nBytes = stripByteCounts[i]
     656                  if compression_type == 32773:
     657                      try:
     658                          bufferBytes = bytes()
     659                      except:
     660                          #python 2.5 ...
     661                          bufferBytes = ""
     662                      #packBits
     663                      readBytes = 0
     664                      #intermediate buffer
     665                      tmpBuffer = fd.read(nBytes)
     666                      while readBytes < nBytes:
     667                          n = struct.unpack('b', tmpBuffer[readBytes:(readBytes + 1)])[0]
     668                          readBytes += 1
     669                          if n >= 0:
     670                              #should I prevent reading more than the
     671                              #length of the chain? Let's python raise
     672                              #the exception...
     673                              bufferBytes += tmpBuffer[readBytes:\
     674                                                        readBytes + (n + 1)]
     675                              readBytes += (n + 1)
     676                          elif n > -128:
     677                              bufferBytes += (-n + 1) * tmpBuffer[readBytes:(readBytes + 1)]
     678                              readBytes += 1
     679                          else:
     680                              #if read -128 ignore the byte
     681                              continue
     682                      if self._swap:
     683                          readout = numpy.fromstring(bufferBytes, dtype).byteswap()
     684                      else:
     685                          readout = numpy.fromstring(bufferBytes, dtype)
     686                      if hasattr(nBits, 'index'):
     687                          readout.shape = -1, nColumns, len(nBits)
     688                      elif info['colormap'] is not None:
     689                          readout = colormap[readout]
     690                          readout.shape = -1, nColumns, 3
     691                      else:
     692                          readout.shape = -1, nColumns
     693                      image[rowStart:rowEnd, :] = readout
     694                  else:
     695                      if 1:
     696                          #use numpy
     697                          if self._swap:
     698                              readout = numpy.fromstring(fd.read(nBytes), dtype).byteswap()
     699                          else:
     700                              readout = numpy.fromstring(fd.read(nBytes), dtype)
     701                          if hasattr(nBits, 'index'):
     702                              readout.shape = -1, nColumns, len(nBits)
     703                          elif colormap is not None:
     704                              readout = colormap[readout]
     705                              readout.shape = -1, nColumns, 3
     706                          else:
     707                              readout.shape = -1, nColumns
     708                          image[rowStart:rowEnd, :] = readout
     709                      else:
     710                          #using struct
     711                          readout = numpy.array(struct.unpack(st + "%df" % int(nBytes / 4), fd.read(nBytes)),
     712                                                dtype=dtype)
     713                          if hasattr(nBits, 'index'):
     714                              readout.shape = -1, nColumns, len(nBits)
     715                          elif colormap is not None:
     716                              readout = colormap[readout]
     717                              readout.shape = -1, nColumns, 3
     718                          else:
     719                              readout.shape = -1, nColumns
     720                          image[rowStart:rowEnd, :] = readout
     721                  rowStart += nRowsToRead
     722          if close:
     723              self.__makeSureFileIsClosed()
     724
     725          if len(image.shape) == 3:
     726              #color image
     727              if self._forceMonoOutput:
     728                  #color image, convert to monochrome
     729                  image = (image[:, :, 0] * 0.114 + \
     730                           image[:, :, 1] * 0.587 + \
     731                           image[:, :, 2] * 0.299).astype(numpy.float32)
     732
     733          if (rowMin == 0) and (rowMax == (nRows - 1)):
     734              self._imageDataCacheIndex.insert(0, nImage)
     735              self._imageDataCache.insert(0, image)
     736              if len(self._imageDataCacheIndex) > self._maxImageCacheLength:
     737                  self._imageDataCacheIndex = self._imageDataCacheIndex[:self._maxImageCacheLength]
     738                  self._imageDataCache = self._imageDataCache[:self._maxImageCacheLength]
     739
     740          return image
     741
     742 -    def writeImage(self, image0, info=None, software=None, date=None):
     743          if software is None:
     744              software = 'PyMca.TiffIO'
     745          #if date is None:
     746          #    date = time.ctime()
     747
     748          self.__makeSureFileIsOpen()
     749          fd = self.fd
     750          #prior to do anything, perform some tests
     751          if not len(image0.shape):
     752              raise ValueError("Empty image")
     753          if len(image0.shape) == 1:
     754              #get a different view
     755              image = image0[:]
     756              image.shape = 1, -1
     757          else:
     758              image = image0
     759
     760          if image.dtype == numpy.float64:
     761              image = image.astype(numpy.float32)
     762          fd.seek(0)
     763          mode = fd.mode
     764          name = fd.name
     765          if 'w' in mode:
     766              #we have to overwrite the file
     767              self.__makeSureFileIsClosed()
     768              fd = None
     769              if os.path.exists(name):
     770                  os.remove(name)
     771              fd = open(name, mode='wb+')
     772              self._initEmptyFile(fd)
     773          self.fd = fd
     774
     775          #read the file size
     776          self.__makeSureFileIsOpen()
     777          fd = self.fd
     778          fd.seek(0, os.SEEK_END)
     779          endOfFile = fd.tell()
     780          if fd.tell() == 0:
     781              self._initEmptyFile(fd)
     782              fd.seek(0, os.SEEK_END)
     783              endOfFile = fd.tell()
     784
     785          #init internal variables
     786          self._initInternalVariables(fd)
     787          st = self._structChar
     788
     789          #get the image file directories
     790          nImages = self.getImageFileDirectories()
     791          if DEBUG:
     792              print("File contains %d images" % nImages)
     793          if nImages == 0:
     794              fd.seek(4)
     795              fmt = st + 'I'
     796              fd.write(struct.pack(fmt, endOfFile))
     797          else:
     798              fd.seek(self._IFD[-1])
     799              fmt = st + 'H'
     800              numberOfDirectoryEntries = struct.unpack(fmt, fd.read(struct.calcsize(fmt)))[0]
     801              fmt = st + 'I'
     802              pos = self._IFD[-1] + 2 + 12 * numberOfDirectoryEntries
     803              fd.seek(pos)
     804              fmt = st + 'I'
     805              fd.write(struct.pack(fmt, endOfFile))
     806          fd.flush()
     807
     808          #and we can write at the end of the file, find out the file length
     809          fd.seek(0, os.SEEK_END)
     810
     811          #get the description information from the input information
     812          if info is None:
     813              description = info
     814          else:
     815              description = "%s" % ""
     816              for key in info.keys():
     817                  description += "%s=%s\n" % (key, info[key])
     818
     819          #get the image file directory
     820          outputIFD = self._getOutputIFD(image, description=description,
     821                                                software=software,
     822                                                date=date)
     823
     824          #write the new IFD
     825          fd.write(outputIFD)
     826
     827          #write the image
     828          if self._swap:
     829              fd.write(image.byteswap().tostring())
     830          else:
     831              fd.write(image.tostring())
     832
     833          fd.flush()
     834          self.fd = fd
     835          self.__makeSureFileIsClosed()
     836
     837 -    def _initEmptyFile(self, fd=None):
     838          if fd is None:
     839              fd = self.fd
     840          if sys.byteorder == "little":
     841              order = "II"
     842              #intel, little endian
     843              fileOrder = "little"
     844              self._structChar = '<'
     845          else:
     846              order = "MM"
     847              #motorola, high endian
     848              fileOrder = "big"
     849              self._structChar = '>'
     850          st = self._structChar
     851          if fileOrder == sys.byteorder:
     852              self._swap = False
     853          else:
     854              self._swap = True
     855          fd.seek(0)
     856          if sys.version < '3.0':
     857              fd.write(struct.pack(st + '2s', order))
     858              fd.write(struct.pack(st + 'H', 42))
     859              fd.write(struct.pack(st + 'I', 0))
     860          else:
     861              fd.write(struct.pack(st + '2s', bytes(order, 'utf-8')))
     862              fd.write(struct.pack(st + 'H', 42))
     863              fd.write(struct.pack(st + 'I', 0))
     864          fd.flush()
     865
     866 -    def _getOutputIFD(self, image, description=None, software=None, date=None):
     867          #the tags have to be in order
     868          #the very minimum is
     869          #256:"NumberOfColumns",           # S or L ImageWidth
     870          #257:"NumberOfRows",              # S or L ImageHeight
     871          #258:"BitsPerSample",             # S Number of bits per component
     872          #259:"Compression",               # SHORT (1 - NoCompression, ...
     873          #262:"PhotometricInterpretation", # SHORT (0 - WhiteIsZero, 1 -BlackIsZero, 2 - RGB, 3 - Palette color
     874          #270:"ImageDescription",          # ASCII
     875          #273:"StripOffsets",              # S or L, for each strip, the byte offset of the strip
     876          #278:"RowsPerStrip",              # S or L, number of rows in each back may be not for the last
     877          #279:"StripByteCounts",           # S or L, The number of bytes in the strip AFTER any compression
     878          #305:"Software",                  # ASCII
     879          #306:"Date",                      # ASCII
     880          #339:"SampleFormat",              # SHORT Interpretation of data in each pixel
     881
     882          nDirectoryEntries = 9
     883          imageDescription = None
     884          if description is not None:
     885              descriptionLength = len(description)
     886              while descriptionLength < 4:
     887                  description = description + " "
     888                  descriptionLength = len(description)
     889              if sys.version >= '3.0':
     890                  description = bytes(description, 'utf-8')
     891              elif type(description) != type(""):
     892                  try:
     893                      description = description.decode('utf-8')
     894                  except UnicodeDecodeError:
     895                      try:
     896                          description = description.decode('latin-1')
     897                      except UnicodeDecodeError:
     898                          description = "%s" % description
     899                  if sys.version > '2.6':
     900                      description = description.encode('utf-8', errors="ignore")
     901                  description = "%s" % description
     902              descriptionLength = len(description)
     903              imageDescription = struct.pack("%ds" % descriptionLength, description)
     904              nDirectoryEntries += 1
     905
     906          #software
     907          if software is not None:
     908              softwareLength = len(software)
     909              while softwareLength < 4:
     910                  software = software + " "
     911                  softwareLength = len(software)
     912              if sys.version >= '3.0':
     913                  software = bytes(software, 'utf-8')
     914              softwarePackedString = struct.pack("%ds" % softwareLength, software)
     915              nDirectoryEntries += 1
     916          else:
     917              softwareLength = 0
     918
     919          if date is not None:
     920              dateLength = len(date)
     921              if sys.version >= '3.0':
     922                  date = bytes(date, 'utf-8')
     923              datePackedString = struct.pack("%ds" % dateLength, date)
     924              dateLength = len(datePackedString)
     925              nDirectoryEntries += 1
     926          else:
     927              dateLength = 0
     928
     929          nRows, nColumns = image.shape
     930          dtype = image.dtype
     931          bitsPerSample = int(dtype.str[-1]) * 8
     932
     933          #only uncompressed data
     934          compression = 1
     935
     936          #interpretation, black is zero
     937          interpretation = 1
     938
     939          #image description
     940          if imageDescription is not None:
     941              descriptionLength = len(imageDescription)
     942          else:
     943              descriptionLength = 0
     944
     945          #strip offsets
     946          #we are putting them after the directory and the directory is
     947          #at the end of the file
     948          self.fd.seek(0, os.SEEK_END)
     949          endOfFile = self.fd.tell()
     950          if endOfFile == 0:
     951              #empty file
     952              endOfFile = 8
     953
     954          #rows per strip
     955          if ALLOW_MULTIPLE_STRIPS:
     956              #try to segment the image in several pieces
     957              if not (nRows % 4):
     958                  rowsPerStrip = int(nRows / 4)
     959              elif not (nRows % 10):
     960                  rowsPerStrip = int(nRows / 10)
     961              elif not (nRows % 8):
     962                  rowsPerStrip = int(nRows / 8)
     963              elif not (nRows % 4):
     964                  rowsPerStrip = int(nRows / 4)
     965              elif not (nRows % 2):
     966                  rowsPerStrip = int(nRows / 2)
     967              else:
     968                  rowsPerStrip = nRows
     969          else:
     970              rowsPerStrip = nRows
     971
     972          #stripByteCounts
     973          stripByteCounts = int(nColumns * rowsPerStrip * bitsPerSample / 8)
     974
     975          if descriptionLength > 4:
     976              stripOffsets0 = endOfFile + dateLength + descriptionLength + \
     977                          2 + 12 * nDirectoryEntries + 4
     978          else:
     979              stripOffsets0 = endOfFile + dateLength + \
     980                          2 + 12 * nDirectoryEntries + 4
     981
     982          if softwareLength > 4:
     983              stripOffsets0 += softwareLength
     984
     985          stripOffsets = [stripOffsets0]
     986          stripOffsetsLength = 0
     987          stripOffsetsString = None
     988
     989          st = self._structChar
     990
     991          if rowsPerStrip != nRows:
     992              nStripOffsets = int(nRows / rowsPerStrip)
     993              fmt = st + 'I'
     994              stripOffsetsLength = struct.calcsize(fmt) * nStripOffsets
     995              stripOffsets0 += stripOffsetsLength
     996              #the length for the stripByteCounts will be the same
     997              stripOffsets0 += stripOffsetsLength
     998              stripOffsets = []
     999              for i in range(nStripOffsets):
    1000                  value = stripOffsets0 + i * stripByteCounts
    1001                  stripOffsets.append(value)
    1002                  if i == 0:
    1003                      stripOffsetsString = struct.pack(fmt, value)
    1004                      stripByteCountsString = struct.pack(fmt, stripByteCounts)
    1005                  else:
    1006                      stripOffsetsString += struct.pack(fmt, value)
    1007                      stripByteCountsString += struct.pack(fmt, stripByteCounts)
    1008
    1009          if DEBUG:
    1010              print("IMAGE WILL START AT %d" % stripOffsets[0])
    1011
    1012          #sample format
    1013          if dtype in [numpy.float32, numpy.float64] or\
    1014             dtype.str[-2] == 'f':
    1015              sampleFormat = SAMPLE_FORMAT_FLOAT
    1016          elif dtype in [numpy.uint8, numpy.uint16, numpy.uint32, numpy.uint64]:
    1017              sampleFormat = SAMPLE_FORMAT_UINT
    1018          elif dtype in [numpy.int8, numpy.int16, numpy.int32, numpy.int64]:
    1019              sampleFormat = SAMPLE_FORMAT_INT
    1020          else:
    1021              raise ValueError("Unsupported data type %s" % dtype)
    1022
    1023          info = {}
    1024          info["nColumns"] = nColumns
    1025          info["nRows"] = nRows
    1026          info["nBits"] = bitsPerSample
    1027          info["compression"] = compression
    1028          info["photometricInterpretation"] = interpretation
    1029          info["stripOffsets"] = stripOffsets
    1030          info["rowsPerStrip"] = rowsPerStrip
    1031          info["stripByteCounts"] = stripByteCounts
    1032          info["date"] = date
    1033          info["sampleFormat"] = sampleFormat
    1034
    1035          outputIFD = ""
    1036          if sys.version > '2.6':
    1037              outputIFD = eval('b""')
    1038
    1039          fmt = st + "H"
    1040          outputIFD += struct.pack(fmt, nDirectoryEntries)
    1041
    1042          fmt = st + "HHII"
    1043          outputIFD += struct.pack(fmt, TAG_NUMBER_OF_COLUMNS,
    1044                                           FIELD_TYPE_OUT['I'],
    1045                                           1,
    1046                                           info["nColumns"])
    1047          outputIFD += struct.pack(fmt, TAG_NUMBER_OF_ROWS,
    1048                                           FIELD_TYPE_OUT['I'],
    1049                                           1,
    1050                                           info["nRows"])
    1051
    1052          fmt = st + 'HHIHH'
    1053          outputIFD += struct.pack(fmt, TAG_BITS_PER_SAMPLE,
    1054                                           FIELD_TYPE_OUT['H'],
    1055                                           1,
    1056                                           info["nBits"], 0)
    1057          fmt = st + 'HHIHH'
    1058          outputIFD += struct.pack(fmt, TAG_COMPRESSION,
    1059                                           FIELD_TYPE_OUT['H'],
    1060                                           1,
    1061                                           info["compression"], 0)
    1062          fmt = st + 'HHIHH'
    1063          outputIFD += struct.pack(fmt, TAG_PHOTOMETRIC_INTERPRETATION,
    1064                                           FIELD_TYPE_OUT['H'],
    1065                                           1,
    1066                                           info["photometricInterpretation"], 0)
    1067
    1068          if imageDescription is not None:
    1069              descriptionLength = len(imageDescription)
    1070              if descriptionLength > 4:
    1071                  fmt = st + 'HHII'
    1072                  outputIFD += struct.pack(fmt, TAG_IMAGE_DESCRIPTION,
    1073                                           FIELD_TYPE_OUT['s'],
    1074                                           descriptionLength,
    1075                                           info["stripOffsets"][0] - \
    1076                                           2 * stripOffsetsLength - \
    1077                                           descriptionLength)
    1078              else:
    1079                  #it has to have length 4
    1080                  fmt = st + 'HHI%ds' % descriptionLength
    1081                  outputIFD += struct.pack(fmt, TAG_IMAGE_DESCRIPTION,
    1082                                           FIELD_TYPE_OUT['s'],
    1083                                           descriptionLength,
    1084                                           description)
    1085
    1086          if len(stripOffsets) == 1:
    1087              fmt = st + 'HHII'
    1088              outputIFD += struct.pack(fmt, TAG_STRIP_OFFSETS,
    1089                                               FIELD_TYPE_OUT['I'],
    1090                                               1,
    1091                                               info["stripOffsets"][0])
    1092          else:
    1093              fmt = st + 'HHII'
    1094              outputIFD += struct.pack(fmt, TAG_STRIP_OFFSETS,
    1095                                               FIELD_TYPE_OUT['I'],
    1096                                               len(stripOffsets),
    1097                      info["stripOffsets"][0] - 2 * stripOffsetsLength)
    1098
    1099          fmt = st + 'HHII'
    1100          outputIFD += struct.pack(fmt, TAG_ROWS_PER_STRIP,
    1101                                           FIELD_TYPE_OUT['I'],
    1102                                           1,
    1103                                           info["rowsPerStrip"])
    1104
    1105          if len(stripOffsets) == 1:
    1106              fmt = st + 'HHII'
    1107              outputIFD += struct.pack(fmt, TAG_STRIP_BYTE_COUNTS,
    1108                                               FIELD_TYPE_OUT['I'],
    1109                                               1,
    1110                                               info["stripByteCounts"])
    1111          else:
    1112              fmt = st + 'HHII'
    1113              outputIFD += struct.pack(fmt, TAG_STRIP_BYTE_COUNTS,
    1114                                               FIELD_TYPE_OUT['I'],
    1115                                               len(stripOffsets),
    1116                      info["stripOffsets"][0] - stripOffsetsLength)
    1117
    1118          if software is not None:
    1119              if softwareLength > 4:
    1120                  fmt = st + 'HHII'
    1121                  outputIFD += struct.pack(fmt, TAG_SOFTWARE,
    1122                                           FIELD_TYPE_OUT['s'],
    1123                                           softwareLength,
    1124                                           info["stripOffsets"][0] - \
    1125                                           2 * stripOffsetsLength - \
    1126                              descriptionLength - softwareLength - dateLength)
    1127              else:
    1128                  #it has to have length 4
    1129                  fmt = st + 'HHI%ds' % softwareLength
    1130                  outputIFD += struct.pack(fmt, TAG_SOFTWARE,
    1131                                           FIELD_TYPE_OUT['s'],
    1132                                           softwareLength,
    1133                                           softwarePackedString)
    1134
    1135          if date is not None:
    1136              fmt = st + 'HHII'
    1137              outputIFD += struct.pack(fmt, TAG_DATE,
    1138                                        FIELD_TYPE_OUT['s'],
    1139                                        dateLength,
    1140                                        info["stripOffsets"][0] - \
    1141                                           2 * stripOffsetsLength - \
    1142                                        descriptionLength - dateLength)
    1143
    1144          fmt = st + 'HHIHH'
    1145          outputIFD += struct.pack(fmt, TAG_SAMPLE_FORMAT,
    1146                                           FIELD_TYPE_OUT['H'],
    1147                                           1,
    1148                                           info["sampleFormat"], 0)
    1149          fmt = st + 'I'
    1150          outputIFD += struct.pack(fmt, 0)
    1151
    1152          if softwareLength > 4:
    1153              outputIFD += softwarePackedString
    1154
    1155          if date is not None:
    1156              outputIFD += datePackedString
    1157
    1158          if imageDescription is not None:
    1159              if descriptionLength > 4:
    1160                  outputIFD += imageDescription
    1161
    1162          if stripOffsetsString is not None:
    1163              outputIFD += stripOffsetsString
    1164              outputIFD += stripByteCountsString
    1165
    1166          return outputIFD
    1167
    1168
    1169  if __name__ == "__main__":
    1170      filename = sys.argv[1]
    1171      dtype = numpy.uint16
    1172      if not os.path.exists(filename):
    1173          print("Testing file creation")
    1174          tif = TiffIO(filename, mode='wb+')
    1175          data = numpy.arange(10000).astype(dtype)
    1176          data.shape = 100, 100
    1177          tif.writeImage(data, info={'Title':'1st'})
    1178          tif = None
    1179          if os.path.exists(filename):
    1180              print("Testing image appending")
    1181              tif = TiffIO(filename, mode='rb+')
    1182              tif.writeImage((data * 2).astype(dtype), info={'Title':'2nd'})
    1183              tif = None
    1184      tif = TiffIO(filename)
    1185      print("Number of images = %d" % tif.getNumberOfImages())
    1186      for i in range(tif.getNumberOfImages()):
    1187          info = tif.getInfo(i)
    1188          for key in info:
    1189              if key not in ["colormap"]:
    1190                  print("%s = %s" % (key, info[key]))
    1191              elif info['colormap'] is not None:
    1192                  print("RED   %s = %s" % (key, info[key][0:10, 0]))
    1193                  print("GREEN %s = %s" % (key, info[key][0:10, 1]))
    1194                  print("BLUE  %s = %s" % (key, info[key][0:10, 2]))
    1195          data = tif.getImage(i)[0, 0:10]
    1196          print("data [0, 0:10] = ", data)
    1197