~ubuntu-branches/ubuntu/vivid/pymca/vivid-proposed

« back to all changes in this revision

Viewing changes to PyMca/TiffIO.py

  • Committer: Package Import Robot
  • Author(s): Picca Frédéric-Emmanuel
  • Date: 2014-10-01 21:38:53 UTC
  • mfrom: (1.1.7)
  • Revision ID: package-import@ubuntu.com-20141001213853-b6b199r0ty8smcxd
Tags: 4.7.4+dfsg-1
* Imported Upstream version 4.7.4+dfsg
* debian/patches
  - 0003-forwaded-upstream-allow-to-build-with-the-system-qhu.patch
    (an equivalent patch was applyed by upstream)

Show diffs side-by-side

added added

removed removed

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