1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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",
30 257:"NumberOfRows",
31 258:"BitsPerSample",
32 259:"Compression",
33 262:"PhotometricInterpretation",
34 270:"ImageDescription",
35 273:"StripOffsets",
36 278:"RowsPerStrip",
37 279:"StripByteCounts",
38 305:"Software",
39 306:"Date",
40 320:"Colormap",
41 339:"SampleFormat",
42 }
43
44
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"),
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
85 SAMPLE_FORMAT_UINT = 1
86 SAMPLE_FORMAT_INT = 2
87 SAMPLE_FORMAT_FLOAT = 3
88 SAMPLE_FORMAT_VOID = 4
89 SAMPLE_FORMAT_COMPLEXINT = 5
90 SAMPLE_FORMAT_COMPLEXIEEEFP = 6
91
92
93
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"):
107 fd = filename
108 self._access = None
109 else:
110
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
119 if fd is None:
120 fd = self.fd
121 else:
122 self.fd = fd
123
124 fd.seek(0)
125 order = fd.read(2).decode()
126 if len(order):
127 if order == "II":
128
129 fileOrder = "little"
130 self._structChar = '<'
131 elif order == "MM":
132
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
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
170
171 newFile = open(fileName, 'rb')
172 else:
173 newFile = open(fileName, self._access)
174 self.fd = newFile
175
177 if self._access is None:
178
179 if DEBUG:
180 print("Not closing not owned file")
181 return
182
183 if not self.fd.closed:
184 self.fd.close()
185
190
195
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
228 if DEBUG:
229 print("Number of images found = %d" % nImages)
230 return nImages
231
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
275
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
303
306
313
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
321 self.__makeSureFileIsOpen()
322 tagIDList, fieldTypeList, nValuesList, valueOffsetList = self._parseImageFileDirectory(nImage)
323
324
325 nColumns = valueOffsetList[tagIDList.index(TAG_NUMBER_OF_COLUMNS)]
326 nRows = valueOffsetList[tagIDList.index(TAG_NUMBER_OF_ROWS)]
327
328
329 idx = tagIDList.index(TAG_BITS_PER_SAMPLE)
330 nBits = valueOffsetList[idx]
331 if nValuesList[idx] != 1:
332
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
354 if TAG_SAMPLE_FORMAT in tagIDList:
355 sampleFormat = valueOffsetList[tagIDList.index(TAG_SAMPLE_FORMAT)]
356 else:
357
358 sampleFormat = SAMPLE_FORMAT_VOID
359
360
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
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
449 nBits = 32
450 colormap = None
451 sampleFormat = SAMPLE_FORMAT_FLOAT
452 interpretation = 1
453
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
468 info["rowsPerStrip"] = rowsPerStrip
469 info["stripByteCounts"] = stripByteCounts
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
482 if sys.version < '3.0':
483 descriptionString = imageDescription
484 else:
485 descriptionString = str(imageDescription.decode())
486
487
488 items = descriptionString.split('=')
489 for i in range(int(len(items) / 2)):
490 key = "%s" % items[i * 2]
491
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
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
537 if DEBUG:
538 print("Using PackBits compression")
539
540 interpretation = info["photometricInterpretation"]
541 if interpretation == 2:
542
543 pass
544
545 elif interpretation == 3:
546
547 pass
548
549 elif interpretation > 2:
550
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
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"]
623 rowsPerStrip = info["rowsPerStrip"]
624 stripByteCounts = info["stripByteCounts"]
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
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
653 fd.seek(stripOffsets[i])
654
655 nBytes = stripByteCounts[i]
656 if compression_type == 32773:
657 try:
658 bufferBytes = bytes()
659 except:
660
661 bufferBytes = ""
662
663 readBytes = 0
664
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
671
672
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
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
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
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
727 if self._forceMonoOutput:
728
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
746
747
748 self.__makeSureFileIsOpen()
749 fd = self.fd
750
751 if not len(image0.shape):
752 raise ValueError("Empty image")
753 if len(image0.shape) == 1:
754
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
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
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
786 self._initInternalVariables(fd)
787 st = self._structChar
788
789
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
809 fd.seek(0, os.SEEK_END)
810
811
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
820 outputIFD = self._getOutputIFD(image, description=description,
821 software=software,
822 date=date)
823
824
825 fd.write(outputIFD)
826
827
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
838 if fd is None:
839 fd = self.fd
840 if sys.byteorder == "little":
841 order = "II"
842
843 fileOrder = "little"
844 self._structChar = '<'
845 else:
846 order = "MM"
847
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
868
869
870
871
872
873
874
875
876
877
878
879
880
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
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
934 compression = 1
935
936
937 interpretation = 1
938
939
940 if imageDescription is not None:
941 descriptionLength = len(imageDescription)
942 else:
943 descriptionLength = 0
944
945
946
947
948 self.fd.seek(0, os.SEEK_END)
949 endOfFile = self.fd.tell()
950 if endOfFile == 0:
951
952 endOfFile = 8
953
954
955 if ALLOW_MULTIPLE_STRIPS:
956
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
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
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
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
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
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