1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34 """Read and write image data from and to TIFF files.
35
36 Image and meta-data can be read from TIFF, BigTIFF, OME-TIFF, STK, LSM, NIH,
37 ImageJ, MicroManager, FluoView, SEQ and GEL files.
38 Only a subset of the TIFF specification is supported, mainly uncompressed
39 and losslessly compressed 2**(0 to 6) bit integer, 16, 32 and 64-bit float,
40 grayscale and RGB(A) images, which are commonly used in bio-scientific imaging.
41 Specifically, reading JPEG/CCITT compressed image data or EXIF/IPTC/GPS/XMP
42 meta-data is not implemented. Only primary info records are read for STK,
43 FluoView, MicroManager, and NIH image formats.
44
45 TIFF, the Tagged Image File Format, is under the control of Adobe Systems.
46 BigTIFF allows for files greater than 4 GB. STK, LSM, FluoView, SEQ, GEL,
47 and OME-TIFF, are custom extensions defined by MetaMorph, Carl Zeiss
48 MicroImaging, Olympus, Media Cybernetics, Molecular Dynamics, and the Open
49 Microscopy Environment consortium respectively.
50
51 For command line usage run ``python tifffile.py --help``
52
53 :Author:
54 `Christoph Gohlke <http://www.lfd.uci.edu/~gohlke/>`_
55
56 :Organization:
57 Laboratory for Fluorescence Dynamics, University of California, Irvine
58
59 :Version: 2013.11.03
60
61 Requirements
62 ------------
63 * `CPython 2.7 or 3.3 <http://www.python.org>`_
64 * `Numpy 1.7 <http://www.numpy.org>`_
65 * `Matplotlib 1.3 <http://www.matplotlib.org>`_ (optional for plotting)
66 * `Tifffile.c 2013.01.18 <http://www.lfd.uci.edu/~gohlke/>`_
67 (recommended for faster decoding of PackBits and LZW encoded strings)
68
69 Notes
70 -----
71 The API is not stable yet and might change between revisions.
72
73 Tested on little-endian platforms only.
74
75 Other Python packages and modules for reading bio-scientific TIFF files:
76 * `Imread <http://luispedro.org/software/imread>`_
77 * `PyLibTiff <http://code.google.com/p/pylibtiff>`_
78 * `SimpleITK <http://www.simpleitk.org>`_
79 * `PyLSM <https://launchpad.net/pylsm>`_
80 * `PyMca.TiffIO.py <http://pymca.sourceforge.net/>`_
81 * `BioImageXD.Readers <http://www.bioimagexd.net/>`_
82 * `Cellcognition.io <http://cellcognition.org/>`_
83 * `CellProfiler.bioformats <http://www.cellprofiler.org/>`_
84
85 Acknowledgements
86 ----------------
87 * Egor Zindy, University of Manchester, for cz_lsm_scan_info specifics.
88 * Wim Lewis for a bug fix and some read_cz_lsm functions.
89 * Hadrien Mary for help on reading MicroManager files.
90
91 References
92 ----------
93 (1) TIFF 6.0 Specification and Supplements. Adobe Systems Incorporated.
94 http://partners.adobe.com/public/developer/tiff/
95 (2) TIFF File Format FAQ. http://www.awaresystems.be/imaging/tiff/faq.html
96 (3) MetaMorph Stack (STK) Image File Format.
97 http://support.meta.moleculardevices.com/docs/t10243.pdf
98 (4) File Format Description - LSM 5xx Release 2.0.
99 http://ibb.gsf.de/homepage/karsten.rodenacker/IDL/Lsmfile.doc
100 (5) BioFormats. http://www.loci.wisc.edu/ome/formats.html
101 (6) The OME-TIFF format.
102 http://www.openmicroscopy.org/site/support/file-formats/ome-tiff
103 (7) TiffDecoder.java
104 http://rsbweb.nih.gov/ij/developer/source/ij/io/TiffDecoder.java.html
105 (8) UltraQuant(r) Version 6.0 for Windows Start-Up Guide.
106 http://www.ultralum.com/images%20ultralum/pdf/UQStart%20Up%20Guide.pdf
107 (9) Micro-Manager File Formats.
108 http://www.micro-manager.org/wiki/Micro-Manager_File_Formats
109
110 Examples
111 --------
112 >>> data = numpy.random.rand(301, 219)
113 >>> imsave('temp.tif', data)
114 >>> image = imread('temp.tif')
115 >>> assert numpy.all(image == data)
116
117 >>> tif = TiffFile('test.tif')
118 >>> images = tif.asarray()
119 >>> image0 = tif[0].asarray()
120 >>> for page in tif:
121 ... for tag in page.tags.values():
122 ... t = tag.name, tag.value
123 ... image = page.asarray()
124 ... if page.is_rgb: pass
125 ... if page.is_palette:
126 ... t = page.color_map
127 ... if page.is_stk:
128 ... t = page.mm_uic_tags.number_planes
129 ... if page.is_lsm:
130 ... t = page.cz_lsm_info
131 >>> tif.close()
132
133 """
134
135 from __future__ import division, print_function
136
137 import sys
138 import os
139 import re
140 import glob
141 import math
142 import zlib
143 import time
144 import json
145 import struct
146 import warnings
147 import datetime
148 import collections
149 from fractions import Fraction
150 from xml.etree import cElementTree as ElementTree
151
152 import numpy
153
154 __version__ = '2013.11.03'
155 __docformat__ = 'restructuredtext en'
156 __all__ = ['imsave', 'imread', 'imshow', 'TiffFile', 'TiffSequence']
157
158
159 -def imsave(filename, data, photometric=None, planarconfig=None,
160 resolution=None, description=None, software='tifffile.py',
161 byteorder=None, bigtiff=False, compress=0, extratags=()):
162 """Write image data to TIFF file.
163
164 Image data are written in one stripe per plane.
165 Dimensions larger than 2 or 3 (depending on photometric mode and
166 planar configuration) are flattened and saved as separate pages.
167 The 'sample_format' and 'bits_per_sample' TIFF tags are derived from
168 the data type.
169
170 Parameters
171 ----------
172 filename : str
173 Name of file to write.
174 data : array_like
175 Input image. The last dimensions are assumed to be image height,
176 width, and samples.
177 photometric : {'minisblack', 'miniswhite', 'rgb'}
178 The color space of the image data.
179 By default this setting is inferred from the data shape.
180 planarconfig : {'contig', 'planar'}
181 Specifies if samples are stored contiguous or in separate planes.
182 By default this setting is inferred from the data shape.
183 'contig': last dimension contains samples.
184 'planar': third last dimension contains samples.
185 resolution : (float, float) or ((int, int), (int, int))
186 X and Y resolution in dots per inch as float or rational numbers.
187 description : str
188 The subject of the image. Saved with the first page only.
189 software : str
190 Name of the software used to create the image.
191 Saved with the first page only.
192 byteorder : {'<', '>'}
193 The endianness of the data in the file.
194 By default this is the system's native byte order.
195 bigtiff : bool
196 If True, the BigTIFF format is used.
197 By default the standard TIFF format is used for data less than 2000 MB.
198 compress : int
199 Values from 0 to 9 controlling the level of zlib compression.
200 If 0, data are written uncompressed (default).
201 extratags: sequence of tuples
202 Additional tags as [(code, dtype, count, value, writeonce)].
203 code : int
204 The TIFF tag Id.
205 dtype : str
206 Data type of items in `value` in Python struct format.
207 One of B, s, H, I, 2I, b, h, i, f, d, Q, or q.
208 count : int
209 Number of data values. Not used for string values.
210 value : sequence
211 `Count` values compatible with `dtype`.
212 writeonce : bool
213 If True, the tag is written to the first page only.
214
215 Examples
216 --------
217 >>> data = numpy.ones((2, 5, 3, 301, 219), 'float32') * 0.5
218 >>> imsave('temp.tif', data, compress=6)
219
220 >>> data = numpy.ones((5, 301, 219, 3), 'uint8') + 127
221 >>> value = u'{"shape": %s}' % str(list(data.shape))
222 >>> imsave('temp.tif', data, extratags=[(270, 's', 0, value, True)])
223
224 """
225 assert(photometric in (None, 'minisblack', 'miniswhite', 'rgb'))
226 assert(planarconfig in (None, 'contig', 'planar'))
227 assert(byteorder in (None, '<', '>'))
228 assert(0 <= compress <= 9)
229
230 if byteorder is None:
231 byteorder = '<' if sys.byteorder == 'little' else '>'
232
233 data = numpy.asarray(data, dtype=byteorder+data.dtype.char, order='C')
234 data_shape = shape = data.shape
235 data = numpy.atleast_2d(data)
236
237 if not bigtiff and data.size * data.dtype.itemsize < 2000*2**20:
238 bigtiff = False
239 offset_size = 4
240 tag_size = 12
241 numtag_format = 'H'
242 offset_format = 'I'
243 val_format = '4s'
244 else:
245 bigtiff = True
246 offset_size = 8
247 tag_size = 20
248 numtag_format = 'Q'
249 offset_format = 'Q'
250 val_format = '8s'
251
252
253 samplesperpixel = 1
254 extrasamples = 0
255 if photometric is None:
256 if data.ndim > 2 and (shape[-3] in (3, 4) or shape[-1] in (3, 4)):
257 photometric = 'rgb'
258 else:
259 photometric = 'minisblack'
260 if photometric == 'rgb':
261 if len(shape) < 3:
262 raise ValueError("not a RGB(A) image")
263 if planarconfig is None:
264 planarconfig = 'planar' if shape[-3] in (3, 4) else 'contig'
265 if planarconfig == 'contig':
266 if shape[-1] not in (3, 4):
267 raise ValueError("not a contiguous RGB(A) image")
268 data = data.reshape((-1, 1) + shape[-3:])
269 samplesperpixel = shape[-1]
270 else:
271 if shape[-3] not in (3, 4):
272 raise ValueError("not a planar RGB(A) image")
273 data = data.reshape((-1, ) + shape[-3:] + (1, ))
274 samplesperpixel = shape[-3]
275 if samplesperpixel == 4:
276 extrasamples = 1
277 elif planarconfig and len(shape) > 2:
278 if planarconfig == 'contig':
279 data = data.reshape((-1, 1) + shape[-3:])
280 samplesperpixel = shape[-1]
281 else:
282 data = data.reshape((-1, ) + shape[-3:] + (1, ))
283 samplesperpixel = shape[-3]
284 extrasamples = samplesperpixel - 1
285 else:
286 planarconfig = None
287
288 while len(shape) > 2 and shape[-1] == 1:
289 shape = shape[:-1]
290 data = data.reshape((-1, 1) + shape[-2:] + (1, ))
291
292 shape = data.shape
293
294 bytestr = bytes if sys.version[0] == '2' else (
295 lambda x: bytes(x, 'utf-8') if isinstance(x, str) else x)
296 tifftypes = {'B': 1, 's': 2, 'H': 3, 'I': 4, '2I': 5, 'b': 6,
297 'h': 8, 'i': 9, 'f': 11, 'd': 12, 'Q': 16, 'q': 17}
298 tifftags = {
299 'new_subfile_type': 254, 'subfile_type': 255,
300 'image_width': 256, 'image_length': 257, 'bits_per_sample': 258,
301 'compression': 259, 'photometric': 262, 'fill_order': 266,
302 'document_name': 269, 'image_description': 270, 'strip_offsets': 273,
303 'orientation': 274, 'samples_per_pixel': 277, 'rows_per_strip': 278,
304 'strip_byte_counts': 279, 'x_resolution': 282, 'y_resolution': 283,
305 'planar_configuration': 284, 'page_name': 285, 'resolution_unit': 296,
306 'software': 305, 'datetime': 306, 'predictor': 317, 'color_map': 320,
307 'extra_samples': 338, 'sample_format': 339}
308 tags = []
309
310 def pack(fmt, *val):
311 return struct.pack(byteorder+fmt, *val)
312
313 def addtag(code, dtype, count, value, writeonce=False):
314
315
316 code = tifftags[code] if code in tifftags else int(code)
317 tifftype = tifftypes[dtype]
318 rawcount = count
319 if dtype not in tifftypes:
320 raise ValueError("unknown dtype %s" % dtype)
321 if dtype == 's':
322 value = bytestr(value) + b'\0'
323 count = rawcount = len(value)
324 value = (value, )
325 if len(dtype) > 1:
326 count *= int(dtype[:-1])
327 dtype = dtype[-1]
328 ifdentry = [pack('HH', code, tifftype),
329 pack(offset_format, rawcount)]
330 ifdvalue = None
331 if count == 1:
332 if isinstance(value, (tuple, list)):
333 value = value[0]
334 ifdentry.append(pack(val_format, pack(dtype, value)))
335 elif struct.calcsize(dtype) * count <= offset_size:
336 ifdentry.append(pack(val_format, pack(str(count)+dtype, *value)))
337 else:
338 ifdentry.append(pack(offset_format, 0))
339 ifdvalue = pack(str(count)+dtype, *value)
340 tags.append((code, b''.join(ifdentry), ifdvalue, writeonce))
341
342 def rational(arg, max_denominator=1000000):
343
344 try:
345 f = Fraction.from_float(arg)
346 except TypeError:
347 f = Fraction(arg[0], arg[1])
348 f = f.limit_denominator(max_denominator)
349 return f.numerator, f.denominator
350
351 if software:
352 addtag('software', 's', 0, software, writeonce=True)
353 if description:
354 addtag('image_description', 's', 0, description, writeonce=True)
355 elif shape != data_shape:
356 addtag('image_description', 's', 0,
357 "shape=(%s)" % (",".join('%i' % i for i in data_shape)),
358 writeonce=True)
359 addtag('datetime', 's', 0,
360 datetime.datetime.now().strftime("%Y:%m:%d %H:%M:%S"),
361 writeonce=True)
362 addtag('compression', 'H', 1, 32946 if compress else 1)
363 addtag('orientation', 'H', 1, 1)
364 addtag('image_width', 'I', 1, shape[-2])
365 addtag('image_length', 'I', 1, shape[-3])
366 addtag('new_subfile_type', 'I', 1, 0 if shape[0] == 1 else 2)
367 addtag('sample_format', 'H', 1,
368 {'u': 1, 'i': 2, 'f': 3, 'c': 6}[data.dtype.kind])
369 addtag('photometric', 'H', 1,
370 {'miniswhite': 0, 'minisblack': 1, 'rgb': 2}[photometric])
371 addtag('samples_per_pixel', 'H', 1, samplesperpixel)
372 if planarconfig:
373 addtag('planar_configuration', 'H', 1, 1 if planarconfig=='contig'
374 else 2)
375 addtag('bits_per_sample', 'H', samplesperpixel,
376 (data.dtype.itemsize * 8, ) * samplesperpixel)
377 else:
378 addtag('bits_per_sample', 'H', 1, data.dtype.itemsize * 8)
379 if extrasamples:
380 if photometric == 'rgb':
381 addtag('extra_samples', 'H', 1, 1)
382 else:
383 addtag('extra_samples', 'H', extrasamples, (0, ) * extrasamples)
384 if resolution:
385 addtag('x_resolution', '2I', 1, rational(resolution[0]))
386 addtag('y_resolution', '2I', 1, rational(resolution[1]))
387 addtag('resolution_unit', 'H', 1, 2)
388 addtag('rows_per_strip', 'I', 1, shape[-3])
389
390
391 strip_byte_counts = (data[0, 0].size * data.dtype.itemsize, ) * shape[1]
392 addtag('strip_byte_counts', offset_format, shape[1], strip_byte_counts)
393 addtag('strip_offsets', offset_format, shape[1], (0, ) * shape[1])
394
395
396 for t in extratags:
397 addtag(*t)
398
399
400 tags = sorted(tags, key=lambda x: x[0])
401
402 with open(filename, 'wb') as fh:
403 seek = fh.seek
404 tell = fh.tell
405
406 def write(arg, *args):
407 fh.write(pack(arg, *args) if args else arg)
408
409 write({'<': b'II', '>': b'MM'}[byteorder])
410 if bigtiff:
411 write('HHH', 43, 8, 0)
412 else:
413 write('H', 42)
414 ifd_offset = tell()
415 write(offset_format, 0)
416
417 for pageindex in range(shape[0]):
418
419 pos = tell()
420 seek(ifd_offset)
421 write(offset_format, pos)
422 seek(pos)
423
424
425 write(numtag_format, len(tags))
426 tag_offset = tell()
427 write(b''.join(t[1] for t in tags))
428 ifd_offset = tell()
429 write(offset_format, 0)
430
431
432 for tagindex, tag in enumerate(tags):
433 if tag[2]:
434 pos = tell()
435 seek(tag_offset + tagindex*tag_size + offset_size + 4)
436 write(offset_format, pos)
437 seek(pos)
438 if tag[0] == 273:
439 strip_offsets_offset = pos
440 elif tag[0] == 279:
441 strip_byte_counts_offset = pos
442 write(tag[2])
443
444
445 data_offset = tell()
446 if compress:
447 strip_byte_counts = []
448 for plane in data[pageindex]:
449 plane = zlib.compress(plane, compress)
450 strip_byte_counts.append(len(plane))
451 fh.write(plane)
452 else:
453
454 data[pageindex].tofile(fh)
455 fh.flush()
456
457
458 pos = tell()
459 for tagindex, tag in enumerate(tags):
460 if tag[0] == 273:
461 if tag[2]:
462 seek(strip_offsets_offset)
463 strip_offset = data_offset
464 for size in strip_byte_counts:
465 write(offset_format, strip_offset)
466 strip_offset += size
467 else:
468 seek(tag_offset + tagindex*tag_size + offset_size + 4)
469 write(offset_format, data_offset)
470 elif tag[0] == 279:
471 if compress:
472 if tag[2]:
473 seek(strip_byte_counts_offset)
474 for size in strip_byte_counts:
475 write(offset_format, size)
476 else:
477 seek(tag_offset + tagindex*tag_size +
478 offset_size + 4)
479 write(offset_format, strip_byte_counts[0])
480 break
481 seek(pos)
482 fh.flush()
483
484 if pageindex == 0:
485 tags = [t for t in tags if not t[-1]]
486
487
488 -def imread(files, *args, **kwargs):
489 """Return image data from TIFF file(s) as numpy array.
490
491 The first image series is returned if no arguments are provided.
492
493 Parameters
494 ----------
495 files : str or list
496 File name, glob pattern, or list of file names.
497 key : int, slice, or sequence of page indices
498 Defines which pages to return as array.
499 series : int
500 Defines which series of pages in file to return as array.
501 multifile : bool
502 If True (default), OME-TIFF data may include pages from multiple files.
503 pattern : str
504 Regular expression pattern that matches axes names and indices in
505 file names.
506
507 Examples
508 --------
509 >>> im = imread('test.tif', 0)
510 >>> im.shape
511 (256, 256, 4)
512 >>> ims = imread(['test.tif', 'test.tif'])
513 >>> ims.shape
514 (2, 256, 256, 4)
515
516 """
517 kwargs_file = {}
518 if 'multifile' in kwargs:
519 kwargs_file['multifile'] = kwargs['multifile']
520 del kwargs['multifile']
521 else:
522 kwargs_file['multifile'] = True
523 kwargs_seq = {}
524 if 'pattern' in kwargs:
525 kwargs_seq['pattern'] = kwargs['pattern']
526 del kwargs['pattern']
527
528 if isinstance(files, basestring) and any(i in files for i in '?*'):
529 files = glob.glob(files)
530 if not files:
531 raise ValueError('no files found')
532 if len(files) == 1:
533 files = files[0]
534
535 if isinstance(files, basestring):
536 with TiffFile(files, **kwargs_file) as tif:
537 return tif.asarray(*args, **kwargs)
538 else:
539 with TiffSequence(files, **kwargs_seq) as imseq:
540 return imseq.asarray(*args, **kwargs)
541
544 """Lazy object attribute whose value is computed on first access."""
545 __slots__ = ('func', )
546
549
550 - def __get__(self, instance, owner):
551 if instance is None:
552 return self
553 value = self.func(instance)
554 if value is NotImplemented:
555 return getattr(super(owner, instance), self.func.__name__)
556 setattr(instance, self.func.__name__, value)
557 return value
558
561 """Read image and meta-data from TIFF, STK, LSM, and FluoView files.
562
563 TiffFile instances must be closed using the close method, which is
564 automatically called when using the 'with' statement.
565
566 Attributes
567 ----------
568 pages : list
569 All TIFF pages in file.
570 series : list of Records(shape, dtype, axes, TiffPages)
571 TIFF pages with compatible shapes and types.
572 micromanager_metadata: dict
573 Extra MicroManager non-TIFF metadata in the file, if exists.
574
575 All attributes are read-only.
576
577 Examples
578 --------
579 >>> tif = TiffFile('test.tif')
580 ... try:
581 ... images = tif.asarray()
582 ... except Exception as e:
583 ... print(e)
584 ... finally:
585 ... tif.close()
586
587 """
588 - def __init__(self, arg, name=None, multifile=False):
589 """Initialize instance from file.
590
591 Parameters
592 ----------
593 arg : str or open file
594 Name of file or open file object.
595 The file objects are closed in TiffFile.close().
596 name : str
597 Human readable label of open file.
598 multifile : bool
599 If True, series may include pages from multiple files.
600
601 """
602 if isinstance(arg, basestring):
603 filename = os.path.abspath(arg)
604 self._fh = open(filename, 'rb')
605 else:
606 filename = str(name)
607 self._fh = arg
608
609 self._fh.seek(0, 2)
610 self._fsize = self._fh.tell()
611 self._fh.seek(0)
612 self.fname = os.path.basename(filename)
613 self.fpath = os.path.dirname(filename)
614 self._tiffs = {self.fname: self}
615 self.offset_size = None
616 self.pages = []
617 self._multifile = bool(multifile)
618 try:
619 self._fromfile()
620 except Exception:
621 self._fh.close()
622 raise
623
625 """Close open file handle(s)."""
626 for tif in self._tiffs.values():
627 if tif._fh:
628 tif._fh.close()
629 tif._fh = None
630 self._tiffs = {}
631
633 """Read TIFF header and all page records from file."""
634 self._fh.seek(0)
635 try:
636 self.byteorder = {b'II': '<', b'MM': '>'}[self._fh.read(2)]
637 except KeyError:
638 raise ValueError("not a valid TIFF file")
639 version = struct.unpack(self.byteorder+'H', self._fh.read(2))[0]
640 if version == 43:
641 self.offset_size, zero = struct.unpack(self.byteorder+'HH',
642 self._fh.read(4))
643 if zero or self.offset_size != 8:
644 raise ValueError("not a valid BigTIFF file")
645 elif version == 42:
646 self.offset_size = 4
647 else:
648 raise ValueError("not a TIFF file")
649 self.pages = []
650 while True:
651 try:
652 page = TiffPage(self)
653 self.pages.append(page)
654 except StopIteration:
655 break
656 if not self.pages:
657 raise ValueError("empty TIFF file")
658
659 if self.is_micromanager:
660
661 self.micromanager_metadata = read_micromanager_metadata(self._fh)
662
663 @lazyattr
665 """Return series of TiffPage with compatible shape and properties."""
666 series = []
667 if self.is_ome:
668 series = self._omeseries()
669 elif self.is_fluoview:
670 dims = {b'X': 'X', b'Y': 'Y', b'Z': 'Z', b'T': 'T',
671 b'WAVELENGTH': 'C', b'TIME': 'T', b'XY': 'R',
672 b'EVENT': 'V', b'EXPOSURE': 'L'}
673 mmhd = list(reversed(self.pages[0].mm_header.dimensions))
674 series = [Record(
675 axes=''.join(dims.get(i[0].strip().upper(), 'Q')
676 for i in mmhd if i[1] > 1),
677 shape=tuple(int(i[1]) for i in mmhd if i[1] > 1),
678 pages=self.pages, dtype=numpy.dtype(self.pages[0].dtype))]
679 elif self.is_lsm:
680 lsmi = self.pages[0].cz_lsm_info
681 axes = CZ_SCAN_TYPES[lsmi.scan_type]
682 if self.pages[0].is_rgb:
683 axes = axes.replace('C', '').replace('XY', 'XYC')
684 axes = axes[::-1]
685 shape = [getattr(lsmi, CZ_DIMENSIONS[i]) for i in axes]
686 pages = [p for p in self.pages if not p.is_reduced]
687 series = [Record(axes=axes, shape=shape, pages=pages,
688 dtype=numpy.dtype(pages[0].dtype))]
689 if len(pages) != len(self.pages):
690 pages = [p for p in self.pages if p.is_reduced]
691 cp = 1
692 i = 0
693 while cp < len(pages) and i < len(shape)-2:
694 cp *= shape[i]
695 i += 1
696 shape = shape[:i] + list(pages[0].shape)
697 axes = axes[:i] + 'CYX'
698 series.append(Record(axes=axes, shape=shape, pages=pages,
699 dtype=numpy.dtype(pages[0].dtype)))
700 elif self.is_imagej:
701 shape = []
702 axes = []
703 ij = self.pages[0].imagej_tags
704 if 'frames' in ij:
705 shape.append(ij['frames'])
706 axes.append('T')
707 if 'slices' in ij:
708 shape.append(ij['slices'])
709 axes.append('Z')
710 if 'channels' in ij and not self.is_rgb:
711 shape.append(ij['channels'])
712 axes.append('C')
713 remain = len(self.pages) // (numpy.prod(shape) if shape else 1)
714 if remain > 1:
715 shape.append(remain)
716 axes.append('I')
717 shape.extend(self.pages[0].shape)
718 axes.extend(self.pages[0].axes)
719 axes = ''.join(axes)
720 series = [Record(pages=self.pages, shape=shape, axes=axes,
721 dtype=numpy.dtype(self.pages[0].dtype))]
722 elif self.is_nih:
723 series = [Record(pages=self.pages,
724 shape=(len(self.pages),) + self.pages[0].shape,
725 axes='I' + self.pages[0].axes,
726 dtype=numpy.dtype(self.pages[0].dtype))]
727 elif self.pages[0].is_shaped:
728 shape = self.pages[0].tags['image_description'].value[7:-1]
729 shape = tuple(int(i) for i in shape.split(b','))
730 series = [Record(pages=self.pages, shape=shape,
731 axes='Q' * len(shape),
732 dtype=numpy.dtype(self.pages[0].dtype))]
733
734 if not series:
735 shapes = []
736 pages = {}
737 for page in self.pages:
738 if not page.shape:
739 continue
740 shape = page.shape + (page.axes,
741 page.compression in TIFF_DECOMPESSORS)
742 if not shape in pages:
743 shapes.append(shape)
744 pages[shape] = [page]
745 else:
746 pages[shape].append(page)
747 series = [Record(pages=pages[s],
748 axes=(('I' + s[-2])
749 if len(pages[s]) > 1 else s[-2]),
750 dtype=numpy.dtype(pages[s][0].dtype),
751 shape=((len(pages[s]), ) + s[:-2]
752 if len(pages[s]) > 1 else s[:-2]))
753 for s in shapes]
754 return series
755
756 - def asarray(self, key=None, series=None, memmap=False):
757 """Return image data of multiple TIFF pages as numpy array.
758
759 By default the first image series is returned.
760
761 Parameters
762 ----------
763 key : int, slice, or sequence of page indices
764 Defines which pages to return as array.
765 series : int
766 Defines which series of pages to return as array.
767 memmap : bool
768 If True, use numpy.memmap to read arrays from file if possible.
769
770 """
771 if key is None and series is None:
772 series = 0
773 if series is not None:
774 pages = self.series[series].pages
775 else:
776 pages = self.pages
777
778 if key is None:
779 pass
780 elif isinstance(key, int):
781 pages = [pages[key]]
782 elif isinstance(key, slice):
783 pages = pages[key]
784 elif isinstance(key, collections.Iterable):
785 pages = [pages[k] for k in key]
786 else:
787 raise TypeError("key must be an int, slice, or sequence")
788
789 if len(pages) == 1:
790 return pages[0].asarray(memmap=memmap)
791 elif self.is_nih:
792 result = numpy.vstack(
793 p.asarray(colormapped=False, squeeze=False, memmap=memmap)
794 for p in pages)
795 if pages[0].is_palette:
796 result = numpy.take(pages[0].color_map, result, axis=1)
797 result = numpy.swapaxes(result, 0, 1)
798 else:
799 if self.is_ome and any(p is None for p in pages):
800 firstpage = next(p for p in pages if p)
801 nopage = numpy.zeros_like(firstpage.asarray(memmap=memmap))
802 result = numpy.vstack((p.asarray(memmap=memmap) if p else nopage)
803 for p in pages)
804 if key is None:
805 try:
806 result.shape = self.series[series].shape
807 except ValueError:
808 warnings.warn("failed to reshape %s to %s" % (
809 result.shape, self.series[series].shape))
810 result.shape = (-1,) + pages[0].shape
811 else:
812 result.shape = (-1,) + pages[0].shape
813 return result
814
816 """Return image series in OME-TIFF file(s)."""
817 root = ElementTree.XML(self.pages[0].tags['image_description'].value)
818 uuid = root.attrib.get('UUID', None)
819 self._tiffs = {uuid: self}
820 modulo = {}
821 result = []
822 for element in root:
823 if element.tag.endswith('BinaryOnly'):
824 warnings.warn("not an OME-TIFF master file")
825 break
826 if element.tag.endswith('StructuredAnnotations'):
827 for annot in element:
828 if not annot.attrib.get('Namespace',
829 '').endswith('modulo'):
830 continue
831 for value in annot:
832 for modul in value:
833 for along in modul:
834 if not along.tag[:-1].endswith('Along'):
835 continue
836 axis = along.tag[-1]
837 newaxis = along.attrib.get('Type', 'other')
838 newaxis = AXES_LABELS[newaxis]
839 if 'Start' in along.attrib:
840 labels = range(
841 int(along.attrib['Start']),
842 int(along.attrib['End']) + 1,
843 int(along.attrib.get('Step', 1)))
844 else:
845 labels = [label.text for label in along
846 if label.tag.endswith('Label')]
847 modulo[axis] = (newaxis, labels)
848 if not element.tag.endswith('Image'):
849 continue
850 for pixels in element:
851 if not pixels.tag.endswith('Pixels'):
852 continue
853 atr = pixels.attrib
854 axes = "".join(reversed(atr['DimensionOrder']))
855 shape = list(int(atr['Size'+ax]) for ax in axes)
856 size = numpy.prod(shape[:-2])
857 ifds = [None] * size
858 for data in pixels:
859 if not data.tag.endswith('TiffData'):
860 continue
861 atr = data.attrib
862 ifd = int(atr.get('IFD', 0))
863 num = int(atr.get('NumPlanes', 1 if 'IFD' in atr else 0))
864 num = int(atr.get('PlaneCount', num))
865 idx = [int(atr.get('First'+ax, 0)) for ax in axes[:-2]]
866 idx = numpy.ravel_multi_index(idx, shape[:-2])
867 for uuid in data:
868 if uuid.tag.endswith('UUID'):
869 if uuid.text not in self._tiffs:
870 if not self._multifile:
871
872 return []
873 fn = uuid.attrib['FileName']
874 try:
875 tf = TiffFile(os.path.join(self.fpath, fn))
876 except (IOError, ValueError):
877 warnings.warn("failed to read %s" % fn)
878 break
879 self._tiffs[uuid.text] = tf
880 pages = self._tiffs[uuid.text].pages
881 try:
882 for i in range(num if num else len(pages)):
883 ifds[idx + i] = pages[ifd + i]
884 except IndexError:
885 warnings.warn("ome-xml: index out of range")
886 break
887 else:
888 pages = self.pages
889 try:
890 for i in range(num if num else len(pages)):
891 ifds[idx + i] = pages[ifd + i]
892 except IndexError:
893 warnings.warn("ome-xml: index out of range")
894 result.append(Record(axes=axes, shape=shape, pages=ifds,
895 dtype=numpy.dtype(ifds[0].dtype)))
896
897 for record in result:
898 for axis, (newaxis, labels) in modulo.items():
899 i = record.axes.index(axis)
900 size = len(labels)
901 if record.shape[i] == size:
902 record.axes = record.axes.replace(axis, newaxis, 1)
903 else:
904 record.shape[i] //= size
905 record.shape.insert(i+1, size)
906 record.axes = record.axes.replace(axis, axis+newaxis, 1)
907
908 return result
909
911 """Return number of image pages in file."""
912 return len(self.pages)
913
915 """Return specified page."""
916 return self.pages[key]
917
919 """Return iterator over pages."""
920 return iter(self.pages)
921
923 """Return string containing information about file."""
924 result = [
925 self.fname.capitalize(),
926 format_size(self._fsize),
927 {'<': 'little endian', '>': 'big endian'}[self.byteorder]]
928 if self.is_bigtiff:
929 result.append("bigtiff")
930 if len(self.pages) > 1:
931 result.append("%i pages" % len(self.pages))
932 if len(self.series) > 1:
933 result.append("%i series" % len(self.series))
934 if len(self._tiffs) > 1:
935 result.append("%i files" % (len(self._tiffs)))
936 return ", ".join(result)
937
940
941 - def __exit__(self, exc_type, exc_value, traceback):
943
944 @lazyattr
946 try:
947 return os.fstat(self._fh.fileno())
948 except Exception:
949 return None
950
951 @lazyattr
953 return self.offset_size != 4
954
955 @lazyattr
957 return all(p.is_rgb for p in self.pages)
958
959 @lazyattr
962
963 @lazyattr
965 return any(p.is_mdgel for p in self.pages)
966
967 @lazyattr
970
971 @lazyattr
973 return all(p.is_stk for p in self.pages)
974
975 @lazyattr
977 return self.pages[0].is_lsm
978
979 @lazyattr
982
983 @lazyattr
986
987 @lazyattr
989 return self.pages[0].is_nih
990
991 @lazyattr
994
995 @lazyattr
997 return self.pages[0].is_ome
998
999
1000 -class TiffPage(object):
1001 """A TIFF image file directory (IFD).
1002
1003 Attributes
1004 ----------
1005 index : int
1006 Index of page in file.
1007 dtype : str {TIFF_SAMPLE_DTYPES}
1008 Data type of image, colormapped if applicable.
1009 shape : tuple
1010 Dimensions of the image array in TIFF page,
1011 colormapped and with one alpha channel if applicable.
1012 axes : str
1013 Axes label codes:
1014 'X' width, 'Y' height, 'S' sample, 'P' plane, 'I' image series,
1015 'Z' depth, 'C' color|em-wavelength|channel, 'E' ex-wavelength|lambda,
1016 'T' time, 'R' region|tile, 'A' angle, 'F' phase, 'H' lifetime,
1017 'L' exposure, 'V' event, 'Q' unknown, '_' missing
1018 tags : TiffTags
1019 Dictionary of tags in page.
1020 Tag values are also directly accessible as attributes.
1021 color_map : numpy array
1022 Color look up table, if exists.
1023 mm_uic_tags: Record(dict)
1024 Consolidated MetaMorph mm_uic# tags, if exists.
1025 cz_lsm_scan_info: Record(dict)
1026 LSM scan info attributes, if exists.
1027 imagej_tags: Record(dict)
1028 Consolidated ImageJ description and metadata tags, if exists.
1029
1030 All attributes are read-only.
1031
1032 """
1033 - def __init__(self, parent):
1034 """Initialize instance from file."""
1035 self.parent = parent
1036 self.index = len(parent.pages)
1037 self.shape = self._shape = ()
1038 self.dtype = self._dtype = None
1039 self.axes = ""
1040 self.tags = TiffTags()
1041
1042 self._fromfile()
1043 self._process_tags()
1044
1045 - def _fromfile(self):
1046 """Read TIFF IFD structure and its tags from file.
1047
1048 File cursor must be at storage position of IFD offset and is left at
1049 offset to next IFD.
1050
1051 Raises StopIteration if offset (first bytes read) is 0.
1052
1053 """
1054 fh = self.parent._fh
1055 byteorder = self.parent.byteorder
1056 offset_size = self.parent.offset_size
1057
1058 fmt = {4: 'I', 8: 'Q'}[offset_size]
1059 offset = struct.unpack(byteorder + fmt, fh.read(offset_size))[0]
1060 if not offset:
1061 raise StopIteration()
1062
1063
1064 tags = self.tags
1065 fh.seek(offset)
1066 fmt, size = {4: ('H', 2), 8: ('Q', 8)}[offset_size]
1067 try:
1068 numtags = struct.unpack(byteorder + fmt, fh.read(size))[0]
1069 except Exception:
1070 warnings.warn("corrupted page list")
1071 raise StopIteration()
1072
1073 tagcode = 0
1074 for _ in range(numtags):
1075 try:
1076 tag = TiffTag(self.parent)
1077 except TiffTag.Error as e:
1078 warnings.warn(str(e))
1079 finally:
1080 if tagcode > tag.code:
1081 warnings.warn("tags are not ordered by code")
1082 tagcode = tag.code
1083 if not tag.name in tags:
1084 tags[tag.name] = tag
1085 else:
1086
1087
1088 for ext in ('_1', '_2', '_3'):
1089 name = tag.name + ext
1090 if not name in tags:
1091 tags[name] = tag
1092 break
1093
1094
1095 if self.is_lsm:
1096 pos = fh.tell()
1097 for name, reader in CZ_LSM_INFO_READERS.items():
1098 try:
1099 offset = self.cz_lsm_info['offset_'+name]
1100 except KeyError:
1101 continue
1102 if not offset:
1103 continue
1104 fh.seek(offset)
1105 try:
1106 setattr(self, 'cz_lsm_'+name, reader(fh, byteorder))
1107 except ValueError:
1108 pass
1109 fh.seek(pos)
1110
1112 """Validate standard tags and initialize attributes.
1113
1114 Raise ValueError if tag values are not supported.
1115
1116 """
1117 tags = self.tags
1118 for code, (name, default, dtype, count, validate) in TIFF_TAGS.items():
1119 if not (name in tags or default is None):
1120 tags[name] = TiffTag(code, dtype=dtype, count=count,
1121 value=default, name=name)
1122 if name in tags and validate:
1123 try:
1124 if tags[name].count == 1:
1125 setattr(self, name, validate[tags[name].value])
1126 else:
1127 setattr(self, name, tuple(
1128 validate[value] for value in tags[name].value))
1129 except KeyError:
1130 raise ValueError("%s.value (%s) not supported" %
1131 (name, tags[name].value))
1132
1133 tag = tags['bits_per_sample']
1134 if tag.count == 1:
1135 self.bits_per_sample = tag.value
1136 else:
1137 value = tag.value[:self.samples_per_pixel]
1138 if any((v-value[0] for v in value)):
1139 self.bits_per_sample = value
1140 else:
1141 self.bits_per_sample = value[0]
1142
1143 tag = tags['sample_format']
1144 if tag.count == 1:
1145 self.sample_format = TIFF_SAMPLE_FORMATS[tag.value]
1146 else:
1147 value = tag.value[:self.samples_per_pixel]
1148 if any((v-value[0] for v in value)):
1149 self.sample_format = [TIFF_SAMPLE_FORMATS[v] for v in value]
1150 else:
1151 self.sample_format = TIFF_SAMPLE_FORMATS[value[0]]
1152
1153 if not 'photometric' in tags:
1154 self.photometric = None
1155
1156 if 'image_length' in tags:
1157 self.strips_per_image = int(math.floor(
1158 float(self.image_length + self.rows_per_strip - 1) /
1159 self.rows_per_strip))
1160 else:
1161 self.strips_per_image = 0
1162
1163 key = (self.sample_format, self.bits_per_sample)
1164 self.dtype = self._dtype = TIFF_SAMPLE_DTYPES.get(key, None)
1165
1166 if self.is_imagej:
1167
1168 if 'image_description_1' in self.tags:
1169 adict = imagej_description(tags['image_description_1'].value)
1170 else:
1171 adict = imagej_description(tags['image_description'].value)
1172 if 'imagej_metadata' in tags:
1173 try:
1174 adict.update(imagej_metadata(
1175 tags['imagej_metadata'].value,
1176 tags['imagej_byte_counts'].value,
1177 self.parent.byteorder))
1178 except Exception as e:
1179 warnings.warn(str(e))
1180 self.imagej_tags = Record(adict)
1181
1182 if not 'image_length' in self.tags or not 'image_width' in self.tags:
1183
1184 self.image_length = 0
1185 self.image_width = 0
1186 self.strip_offsets = 0
1187 self._shape = ()
1188 self.shape = ()
1189 self.axes = ''
1190
1191 if self.is_palette:
1192 self.dtype = self.tags['color_map'].dtype[1]
1193 self.color_map = numpy.array(self.color_map, self.dtype)
1194 dmax = self.color_map.max()
1195 if dmax < 256:
1196 self.dtype = numpy.uint8
1197 self.color_map = self.color_map.astype(self.dtype)
1198
1199
1200
1201
1202 self.color_map.shape = (3, -1)
1203
1204 if self.is_stk:
1205
1206 planes = tags['mm_uic2'].count
1207 self.mm_uic_tags = Record(tags['mm_uic2'].value)
1208 for key in ('mm_uic3', 'mm_uic4', 'mm_uic1'):
1209 if key in tags:
1210 self.mm_uic_tags.update(tags[key].value)
1211 if self.planar_configuration == 'contig':
1212 self._shape = (planes, 1, self.image_length, self.image_width,
1213 self.samples_per_pixel)
1214 self.shape = tuple(self._shape[i] for i in (0, 2, 3, 4))
1215 self.axes = 'PYXS'
1216 else:
1217 self._shape = (planes, self.samples_per_pixel,
1218 self.image_length, self.image_width, 1)
1219 self.shape = self._shape[:4]
1220 self.axes = 'PSYX'
1221 if self.is_palette and (self.color_map.shape[1]
1222 >= 2**self.bits_per_sample):
1223 self.shape = (3, planes, self.image_length, self.image_width)
1224 self.axes = 'CPYX'
1225 else:
1226 warnings.warn("palette cannot be applied")
1227 self.is_palette = False
1228 elif self.is_palette:
1229 samples = 1
1230 if 'extra_samples' in self.tags:
1231 samples += len(self.extra_samples)
1232 if self.planar_configuration == 'contig':
1233 self._shape = (
1234 1, 1, self.image_length, self.image_width, samples)
1235 else:
1236 self._shape = (
1237 1, samples, self.image_length, self.image_width, 1)
1238 if self.color_map.shape[1] >= 2**self.bits_per_sample:
1239 self.shape = (3, self.image_length, self.image_width)
1240 self.axes = 'CYX'
1241 else:
1242 warnings.warn("palette cannot be applied")
1243 self.is_palette = False
1244 self.shape = (self.image_length, self.image_width)
1245 self.axes = 'YX'
1246 elif self.is_rgb or self.samples_per_pixel > 1:
1247 if self.planar_configuration == 'contig':
1248 self._shape = (1, 1, self.image_length, self.image_width,
1249 self.samples_per_pixel)
1250 self.shape = (self.image_length, self.image_width,
1251 self.samples_per_pixel)
1252 self.axes = 'YXS'
1253 else:
1254 self._shape = (1, self.samples_per_pixel, self.image_length,
1255 self.image_width, 1)
1256 self.shape = self._shape[1:-1]
1257 self.axes = 'SYX'
1258 if self.is_rgb and 'extra_samples' in self.tags:
1259 extra_samples = self.extra_samples
1260 if self.tags['extra_samples'].count == 1:
1261 extra_samples = (extra_samples, )
1262 for exs in extra_samples:
1263 if exs in ('unassalpha', 'assocalpha', 'unspecified'):
1264 if self.planar_configuration == 'contig':
1265 self.shape = self.shape[:2] + (4,)
1266 else:
1267 self.shape = (4,) + self.shape[1:]
1268 break
1269 else:
1270 self._shape = (1, 1, self.image_length, self.image_width, 1)
1271 self.shape = self._shape[2:4]
1272 self.axes = 'YX'
1273
1274 if not self.compression and not 'strip_byte_counts' in tags:
1275 self.strip_byte_counts = numpy.prod(self.shape) * (
1276 self.bits_per_sample // 8)
1277
1278 - def asarray(self, squeeze=True, colormapped=True, rgbonly=True,
1279 memmap=False):
1280 """Read image data from file and return as numpy array.
1281
1282 Raise ValueError if format is unsupported.
1283 If any argument is False, the shape of the returned array might be
1284 different from the page shape.
1285
1286 Parameters
1287 ----------
1288 squeeze : bool
1289 If True, all length-1 dimensions (except X and Y) are
1290 squeezed out from result.
1291 colormapped : bool
1292 If True, color mapping is applied for palette-indexed images.
1293 rgbonly : bool
1294 If True, return RGB(A) image without additional extra samples.
1295 memmap : bool
1296 If True, use numpy.memmap to read array if possible.
1297
1298 """
1299 fh = self.parent._fh
1300 if not fh:
1301 raise IOError("TIFF file is not open")
1302 if self.dtype is None:
1303 raise ValueError("data type not supported: %s%i" % (
1304 self.sample_format, self.bits_per_sample))
1305 if self.compression not in TIFF_DECOMPESSORS:
1306 raise ValueError("cannot decompress %s" % self.compression)
1307 if ('ycbcr_subsampling' in self.tags
1308 and self.tags['ycbcr_subsampling'].value not in (1, (1, 1))):
1309 raise ValueError("YCbCr subsampling not supported")
1310 tag = self.tags['sample_format']
1311 if tag.count != 1 and any((i-tag.value[0] for i in tag.value)):
1312 raise ValueError("sample formats don't match %s" % str(tag.value))
1313
1314 dtype = self._dtype
1315 shape = self._shape
1316
1317 if not shape:
1318 return None
1319
1320 image_width = self.image_width
1321 image_length = self.image_length
1322 typecode = self.parent.byteorder + dtype
1323 bits_per_sample = self.bits_per_sample
1324 byteorder_is_native = ({'big': '>', 'little': '<'}[sys.byteorder] ==
1325 self.parent.byteorder)
1326
1327 if self.is_tiled:
1328 if 'tile_offsets' in self.tags:
1329 byte_counts = self.tile_byte_counts
1330 offsets = self.tile_offsets
1331 else:
1332 byte_counts = self.strip_byte_counts
1333 offsets = self.strip_offsets
1334 tile_width = self.tile_width
1335 tile_length = self.tile_length
1336 tw = (image_width + tile_width - 1) // tile_width
1337 tl = (image_length + tile_length - 1) // tile_length
1338 shape = shape[:-3] + (tl*tile_length, tw*tile_width, shape[-1])
1339 tile_shape = (tile_length, tile_width, shape[-1])
1340 runlen = tile_width
1341 else:
1342 byte_counts = self.strip_byte_counts
1343 offsets = self.strip_offsets
1344 runlen = image_width
1345
1346 try:
1347 offsets[0]
1348 except TypeError:
1349 offsets = (offsets, )
1350 byte_counts = (byte_counts, )
1351 if any(o < 2 for o in offsets):
1352 raise ValueError("corrupted page")
1353
1354 if (not self.is_tiled and (self.is_stk or (not self.compression
1355 and bits_per_sample in (8, 16, 32, 64)
1356 and all(offsets[i] == offsets[i+1] - byte_counts[i]
1357 for i in range(len(offsets)-1))))):
1358
1359 if (memmap and not (self.is_tiled or self.predictor or
1360 ('extra_samples' in self.tags) or
1361 (colormapped and self.is_palette) or
1362 (not byteorder_is_native))):
1363 result = numpy.memmap(fh, typecode, 'r', offsets[0], shape)
1364 else:
1365 fh.seek(offsets[0])
1366 result = numpy_fromfile(fh, typecode, numpy.prod(shape))
1367 result = result.astype('=' + dtype)
1368 else:
1369 if self.planar_configuration == 'contig':
1370 runlen *= self.samples_per_pixel
1371 if bits_per_sample in (8, 16, 32, 64, 128):
1372 if (bits_per_sample * runlen) % 8:
1373 raise ValueError("data and sample size mismatch")
1374
1375 def unpack(x):
1376 return numpy.fromstring(x, typecode)
1377 elif isinstance(bits_per_sample, tuple):
1378 def unpack(x):
1379 return unpackrgb(x, typecode, bits_per_sample)
1380 else:
1381 def unpack(x):
1382 return unpackints(x, typecode, bits_per_sample, runlen)
1383 decompress = TIFF_DECOMPESSORS[self.compression]
1384 if self.is_tiled:
1385 result = numpy.empty(shape, dtype)
1386 tw, tl, pl = 0, 0, 0
1387 for offset, bytecount in zip(offsets, byte_counts):
1388 fh.seek(offset)
1389 tile = unpack(decompress(fh.read(bytecount)))
1390 tile.shape = tile_shape
1391 if self.predictor == 'horizontal':
1392 numpy.cumsum(tile, axis=-2, dtype=dtype, out=tile)
1393 result[0, pl, tl:tl+tile_length,
1394 tw:tw+tile_width, :] = tile
1395 del tile
1396 tw += tile_width
1397 if tw >= shape[-2]:
1398 tw, tl = 0, tl + tile_length
1399 if tl >= shape[-3]:
1400 tl, pl = 0, pl + 1
1401 result = result[..., :image_length, :image_width, :]
1402 else:
1403 strip_size = (self.rows_per_strip * self.image_width *
1404 self.samples_per_pixel)
1405 result = numpy.empty(shape, dtype).reshape(-1)
1406 index = 0
1407 for offset, bytecount in zip(offsets, byte_counts):
1408 fh.seek(offset)
1409 strip = fh.read(bytecount)
1410 strip = unpack(decompress(strip))
1411 size = min(result.size, strip.size, strip_size,
1412 result.size - index)
1413 result[index:index+size] = strip[:size]
1414 del strip
1415 index += size
1416
1417 result.shape = self._shape
1418
1419 if self.predictor == 'horizontal' and not self.is_tiled:
1420
1421 if not (self.parent.is_lsm and not self.compression):
1422 numpy.cumsum(result, axis=-2, dtype=dtype, out=result)
1423
1424 if colormapped and self.is_palette:
1425 if self.color_map.shape[1] >= 2**bits_per_sample:
1426
1427 result = numpy.take(self.color_map,
1428 result[:, 0, :, :, 0], axis=1)
1429 elif rgbonly and self.is_rgb and 'extra_samples' in self.tags:
1430
1431 extra_samples = self.extra_samples
1432 if self.tags['extra_samples'].count == 1:
1433 extra_samples = (extra_samples, )
1434 for i, exs in enumerate(extra_samples):
1435 if exs in ('unassalpha', 'assocalpha', 'unspecified'):
1436 if self.planar_configuration == 'contig':
1437 result = result[..., [0, 1, 2, 3+i]]
1438 else:
1439 result = result[:, [0, 1, 2, 3+i]]
1440 break
1441 else:
1442 if self.planar_configuration == 'contig':
1443 result = result[..., :3]
1444 else:
1445 result = result[:, :3]
1446
1447 if squeeze:
1448 try:
1449 result.shape = self.shape
1450 except ValueError:
1451 warnings.warn("failed to reshape from %s to %s" % (
1452 str(result.shape), str(self.shape)))
1453
1454 return result
1455
1456 - def __str__(self):
1457 """Return string containing information about page."""
1458 s = ', '.join(s for s in (
1459 ' x '.join(str(i) for i in self.shape),
1460 str(numpy.dtype(self.dtype)),
1461 '%s bit' % str(self.bits_per_sample),
1462 self.photometric if 'photometric' in self.tags else '',
1463 self.compression if self.compression else 'raw',
1464 '|'.join(t[3:] for t in (
1465 'is_stk', 'is_lsm', 'is_nih', 'is_ome', 'is_imagej',
1466 'is_micromanager', 'is_fluoview', 'is_mdgel', 'is_mediacy',
1467 'is_reduced', 'is_tiled') if getattr(self, t))) if s)
1468 return "Page %i: %s" % (self.index, s)
1469
1470 - def __getattr__(self, name):
1471 """Return tag value."""
1472 if name in self.tags:
1473 value = self.tags[name].value
1474 setattr(self, name, value)
1475 return value
1476 raise AttributeError(name)
1477
1478 @lazyattr
1480 """True if page contains a RGB image."""
1481 return ('photometric' in self.tags and
1482 self.tags['photometric'].value == 2)
1483
1484 @lazyattr
1485 - def is_palette(self):
1486 """True if page contains a palette-colored image."""
1487 return ('photometric' in self.tags and
1488 self.tags['photometric'].value == 3)
1489
1490 @lazyattr
1491 - def is_tiled(self):
1492 """True if page contains tiled image."""
1493 return 'tile_width' in self.tags
1494
1495 @lazyattr
1496 - def is_reduced(self):
1497 """True if page is a reduced image of another image."""
1498 return bool(self.tags['new_subfile_type'].value & 1)
1499
1500 @lazyattr
1501 - def is_mdgel(self):
1502 """True if page contains md_file_tag tag."""
1503 return 'md_file_tag' in self.tags
1504
1505 @lazyattr
1507 """True if page contains Media Cybernetics Id tag."""
1508 return ('mc_id' in self.tags and
1509 self.tags['mc_id'].value.startswith(b'MC TIFF'))
1510
1511 @lazyattr
1513 """True if page contains MM_UIC2 tag."""
1514 return 'mm_uic2' in self.tags
1515
1516 @lazyattr
1518 """True if page contains LSM CZ_LSM_INFO tag."""
1519 return 'cz_lsm_info' in self.tags
1520
1521 @lazyattr
1522 - def is_fluoview(self):
1523 """True if page contains FluoView MM_STAMP tag."""
1524 return 'mm_stamp' in self.tags
1525
1526 @lazyattr
1528 """True if page contains NIH image header."""
1529 return 'nih_image_header' in self.tags
1530
1531 @lazyattr
1533 """True if page contains OME-XML in image_description tag."""
1534 return ('image_description' in self.tags and self.tags[
1535 'image_description'].value.startswith(b'<?xml version='))
1536
1537 @lazyattr
1538 - def is_shaped(self):
1539 """True if page contains shape in image_description tag."""
1540 return ('image_description' in self.tags and self.tags[
1541 'image_description'].value.startswith(b'shape=('))
1542
1543 @lazyattr
1544 - def is_imagej(self):
1545 """True if page contains ImageJ description."""
1546 return (
1547 ('image_description' in self.tags and
1548 self.tags['image_description'].value.startswith(b'ImageJ=')) or
1549 ('image_description_1' in self.tags and
1550 self.tags['image_description_1'].value.startswith(b'ImageJ=')))
1551
1552 @lazyattr
1553 - def is_micromanager(self):
1554 """True if page contains Micro-Manager metadata."""
1555 return 'micromanager_metadata' in self.tags
1556
1559 """A TIFF tag structure.
1560
1561 Attributes
1562 ----------
1563 name : string
1564 Attribute name of tag.
1565 code : int
1566 Decimal code of tag.
1567 dtype : str
1568 Datatype of tag data. One of TIFF_DATA_TYPES.
1569 count : int
1570 Number of values.
1571 value : various types
1572 Tag data as Python object.
1573 value_offset : int
1574 Location of value in file, if any.
1575
1576 All attributes are read-only.
1577
1578 """
1579 __slots__ = ('code', 'name', 'count', 'dtype', 'value', 'value_offset',
1580 '_offset', '_value')
1581
1582 - class Error(Exception):
1584
1586 """Initialize instance from file or arguments."""
1587 self._offset = None
1588 if hasattr(arg, '_fh'):
1589 self._fromfile(arg, **kwargs)
1590 else:
1591 self._fromdata(arg, **kwargs)
1592
1593 - def _fromdata(self, code, dtype, count, value, name=None):
1600
1602 """Read tag structure from open file. Advance file cursor."""
1603 fh = parent._fh
1604 byteorder = parent.byteorder
1605 self._offset = fh.tell()
1606 self.value_offset = self._offset + parent.offset_size + 4
1607
1608 fmt, size = {4: ('HHI4s', 12), 8: ('HHQ8s', 20)}[parent.offset_size]
1609 data = fh.read(size)
1610 code, dtype = struct.unpack(byteorder + fmt[:2], data[:4])
1611 count, value = struct.unpack(byteorder + fmt[2:], data[4:])
1612 self._value = value
1613
1614 if code in TIFF_TAGS:
1615 name = TIFF_TAGS[code][0]
1616 elif code in CUSTOM_TAGS:
1617 name = CUSTOM_TAGS[code][0]
1618 else:
1619 name = str(code)
1620
1621 try:
1622 dtype = TIFF_DATA_TYPES[dtype]
1623 except KeyError:
1624 raise TiffTag.Error("unknown tag data type %i" % dtype)
1625
1626 fmt = '%s%i%s' % (byteorder, count*int(dtype[0]), dtype[1])
1627 size = struct.calcsize(fmt)
1628 if size > parent.offset_size or code in CUSTOM_TAGS:
1629 pos = fh.tell()
1630 tof = {4: 'I', 8: 'Q'}[parent.offset_size]
1631 self.value_offset = offset = struct.unpack(byteorder+tof, value)[0]
1632 if offset < 0 or offset > parent._fsize:
1633 raise TiffTag.Error("corrupt file - invalid tag value offset")
1634 elif offset < 4:
1635 raise TiffTag.Error("corrupt value offset for tag %i" % code)
1636 fh.seek(offset)
1637 if code in CUSTOM_TAGS:
1638 readfunc = CUSTOM_TAGS[code][1]
1639 value = readfunc(fh, byteorder, dtype, count)
1640 fh.seek(0, 2)
1641 if isinstance(value, dict):
1642 value = Record(value)
1643 elif code in TIFF_TAGS or dtype[-1] == 's':
1644 value = struct.unpack(fmt, fh.read(size))
1645 else:
1646 value = read_numpy(fh, byteorder, dtype, count)
1647 fh.seek(0, 2)
1648 fh.seek(pos)
1649 else:
1650 value = struct.unpack(fmt, value[:size])
1651
1652 if not code in CUSTOM_TAGS:
1653 if len(value) == 1:
1654 value = value[0]
1655
1656 if dtype.endswith('s') and isinstance(value, bytes):
1657 value = stripnull(value)
1658
1659 self.code = code
1660 self.name = name
1661 self.dtype = dtype
1662 self.count = count
1663 self.value = value
1664
1666 """Return string containing information about tag."""
1667 return ' '.join(str(getattr(self, s)) for s in self.__slots__)
1668
1671 """Sequence of image files.
1672
1673 Properties
1674 ----------
1675 files : list
1676 List of file names.
1677 shape : tuple
1678 Shape of image sequence.
1679 axes : str
1680 Labels of axes in shape.
1681
1682 Examples
1683 --------
1684 >>> ims = TiffSequence("test.oif.files/*.tif")
1685 >>> ims = ims.asarray()
1686 >>> ims.shape
1687 (2, 100, 256, 256)
1688
1689 """
1690 _axes_pattern = """
1691 # matches Olympus OIF and Leica TIFF series
1692 _?(?:(q|l|p|a|c|t|x|y|z|ch|tp)(\d{1,4}))
1693 _?(?:(q|l|p|a|c|t|x|y|z|ch|tp)(\d{1,4}))?
1694 _?(?:(q|l|p|a|c|t|x|y|z|ch|tp)(\d{1,4}))?
1695 _?(?:(q|l|p|a|c|t|x|y|z|ch|tp)(\d{1,4}))?
1696 _?(?:(q|l|p|a|c|t|x|y|z|ch|tp)(\d{1,4}))?
1697 _?(?:(q|l|p|a|c|t|x|y|z|ch|tp)(\d{1,4}))?
1698 _?(?:(q|l|p|a|c|t|x|y|z|ch|tp)(\d{1,4}))?
1699 """
1700
1703
1705 """Initialize instance from multiple files.
1706
1707 Parameters
1708 ----------
1709 files : str, or sequence of str
1710 Glob pattern or sequence of file names.
1711 imread : function or class
1712 Image read function or class with asarray function returning numpy
1713 array from single file.
1714 pattern : str
1715 Regular expression pattern that matches axes names and sequence
1716 indices in file names.
1717
1718 """
1719 if isinstance(files, basestring):
1720 files = natural_sorted(glob.glob(files))
1721 files = list(files)
1722 if not files:
1723 raise ValueError("no files found")
1724
1725
1726 self.files = files
1727
1728 if hasattr(imread, 'asarray'):
1729 _imread = imread
1730
1731 def imread(fname, *args, **kwargs):
1732 with _imread(fname) as im:
1733 return im.asarray(*args, **kwargs)
1734
1735 self.imread = imread
1736
1737 self.pattern = self._axes_pattern if pattern == 'axes' else pattern
1738 try:
1739 self._parse()
1740 if not self.axes:
1741 self.axes = 'I'
1742 except self._ParseError:
1743 self.axes = 'I'
1744 self.shape = (len(files),)
1745 self._start_index = (0,)
1746 self._indices = ((i,) for i in range(len(files)))
1747
1749 """Return string with information about image sequence."""
1750 return "\n".join([
1751 self.files[0],
1752 '* files: %i' % len(self.files),
1753 '* axes: %s' % self.axes,
1754 '* shape: %s' % str(self.shape)])
1755
1757 return len(self.files)
1758
1761
1762 - def __exit__(self, exc_type, exc_value, traceback):
1764
1767
1768 - def asarray(self, *args, **kwargs):
1785
1787 """Get axes and shape from file names."""
1788 if not self.pattern:
1789 raise self._ParseError("invalid pattern")
1790 pattern = re.compile(self.pattern, re.IGNORECASE | re.VERBOSE)
1791 matches = pattern.findall(self.files[0])
1792 if not matches:
1793 raise self._ParseError("pattern doesn't match file names")
1794 matches = matches[-1]
1795 if len(matches) % 2:
1796 raise self._ParseError("pattern doesn't match axis name and index")
1797 axes = ''.join(m for m in matches[::2] if m)
1798 if not axes:
1799 raise self._ParseError("pattern doesn't match file names")
1800
1801 indices = []
1802 for fname in self.files:
1803 matches = pattern.findall(fname)[-1]
1804 if axes != ''.join(m for m in matches[::2] if m):
1805 raise ValueError("axes don't match within the image sequence")
1806 indices.append([int(m) for m in matches[1::2] if m])
1807 shape = tuple(numpy.max(indices, axis=0))
1808 start_index = tuple(numpy.min(indices, axis=0))
1809 shape = tuple(i-j+1 for i, j in zip(shape, start_index))
1810 if numpy.prod(shape) != len(self.files):
1811 warnings.warn("files are missing. Missing data are zeroed")
1812
1813 self.axes = axes.upper()
1814 self.shape = shape
1815 self._indices = indices
1816 self._start_index = start_index
1817
1820 """Dictionary with attribute access.
1821
1822 Can also be initialized with numpy.core.records.record.
1823
1824 """
1825 __slots__ = ()
1826
1827 - def __init__(self, arg=None, **kwargs):
1828 if kwargs:
1829 arg = kwargs
1830 elif arg is None:
1831 arg = {}
1832 try:
1833 dict.__init__(self, arg)
1834 except (TypeError, ValueError):
1835 for i, name in enumerate(arg.dtype.names):
1836 v = arg[i]
1837 self[name] = v if v.dtype.char != 'S' else stripnull(v)
1838
1841
1844
1846 """Pretty print Record."""
1847 s = []
1848 lists = []
1849 for k in sorted(self):
1850 if k.startswith('_'):
1851 continue
1852 v = self[k]
1853 if isinstance(v, (list, tuple)) and len(v):
1854 if isinstance(v[0], Record):
1855 lists.append((k, v))
1856 continue
1857 elif isinstance(v[0], TiffPage):
1858 v = [i.index for i in v if i]
1859 s.append(
1860 ("* %s: %s" % (k, str(v))).split("\n", 1)[0]
1861 [:PRINT_LINE_LEN].rstrip())
1862 for k, v in lists:
1863 l = []
1864 for i, w in enumerate(v):
1865 l.append("* %s[%i]\n %s" % (k, i,
1866 str(w).replace("\n", "\n ")))
1867 s.append('\n'.join(l))
1868 return '\n'.join(s)
1869
1882
1883
1884 -def read_bytes(fh, byteorder, dtype, count):
1885 """Read tag data from file and return as byte string."""
1886 return numpy_fromfile(fh, byteorder+dtype[-1], count).tostring()
1887
1888
1889 -def read_numpy(fh, byteorder, dtype, count):
1890 """Read tag data from file and return as numpy array."""
1891 return numpy_fromfile(fh, byteorder+dtype[-1], count)
1892
1893
1894 -def read_json(fh, byteorder, dtype, count):
1895 """Read tag data from file and return as object."""
1896 return json.loads(unicode(stripnull(fh.read(count)), 'utf-8'))
1897
1900 """Read MM_HEADER tag from file and return as numpy.rec.array."""
1901 return numpy.rec.fromfile(fh, MM_HEADER, 1, byteorder=byteorder)[0]
1902
1905 """Read MM_STAMP tag from file and return as numpy.array."""
1906 return numpy_fromfile(fh, byteorder+'8f8', 1)[0]
1907
1910 """Read MM_UIC1 tag from file and return as dictionary."""
1911 t = fh.read(8*count)
1912 t = struct.unpack('%s%iI' % (byteorder, 2*count), t)
1913 return dict((MM_TAG_IDS[k], v) for k, v in zip(t[::2], t[1::2])
1914 if k in MM_TAG_IDS)
1915
1927
1930 """Read MM_UIC3 tag from file and return as dictionary."""
1931 t = numpy_fromfile(fh, byteorder+'I', 2*count)
1932 return {'wavelengths': t[0::2] // t[1::2]}
1933
1936 """Read MM_UIC4 tag from file and return as dictionary."""
1937 t = struct.unpack(byteorder + 'hI'*count, fh.read(6*count))
1938 return dict((MM_TAG_IDS[k], v) for k, v in zip(t[::2], t[1::2])
1939 if k in MM_TAG_IDS)
1940
1943 """Read CS_LSM_INFO tag from file and return as numpy.rec.array."""
1944 result = numpy.rec.fromfile(fh, CZ_LSM_INFO, 1,
1945 byteorder=byteorder)[0]
1946 {50350412: '1.3', 67127628: '2.0'}[result.magic_number]
1947 return result
1948
1951 """Read LSM time stamps from file and return as list."""
1952 size, count = struct.unpack(byteorder+'II', fh.read(8))
1953 if size != (8 + 8 * count):
1954 raise ValueError("lsm_time_stamps block is too short")
1955 return struct.unpack(('%s%dd' % (byteorder, count)),
1956 fh.read(8*count))
1957
1960 """Read LSM events from file and return as list of (time, type, text)."""
1961 count = struct.unpack(byteorder+'II', fh.read(8))[1]
1962 events = []
1963 while count > 0:
1964 esize, etime, etype = struct.unpack(byteorder+'IdI', fh.read(16))
1965 etext = stripnull(fh.read(esize - 16))
1966 events.append((etime, etype, etext))
1967 count -= 1
1968 return events
1969
1972 """Read LSM scan information from file and return as Record."""
1973 block = Record()
1974 blocks = [block]
1975 unpack = struct.unpack
1976 if 0x10000000 != struct.unpack(byteorder+"I", fh.read(4))[0]:
1977 raise ValueError("not a lsm_scan_info structure")
1978 fh.read(8)
1979 while True:
1980 entry, dtype, size = unpack(byteorder+"III", fh.read(12))
1981 if dtype == 2:
1982 value = stripnull(fh.read(size))
1983 elif dtype == 4:
1984 value = unpack(byteorder+"i", fh.read(4))[0]
1985 elif dtype == 5:
1986 value = unpack(byteorder+"d", fh.read(8))[0]
1987 else:
1988 value = 0
1989 if entry in CZ_LSM_SCAN_INFO_ARRAYS:
1990 blocks.append(block)
1991 name = CZ_LSM_SCAN_INFO_ARRAYS[entry]
1992 newobj = []
1993 setattr(block, name, newobj)
1994 block = newobj
1995 elif entry in CZ_LSM_SCAN_INFO_STRUCTS:
1996 blocks.append(block)
1997 newobj = Record()
1998 block.append(newobj)
1999 block = newobj
2000 elif entry in CZ_LSM_SCAN_INFO_ATTRIBUTES:
2001 name = CZ_LSM_SCAN_INFO_ATTRIBUTES[entry]
2002 setattr(block, name, value)
2003 elif entry == 0xffffffff:
2004 block = blocks.pop()
2005 else:
2006 setattr(block, "unknown_%x" % entry, value)
2007 if not blocks:
2008 break
2009 return block
2010
2013 """Read NIH_IMAGE_HEADER tag from file and return as numpy.rec.array."""
2014 a = numpy.rec.fromfile(fh, NIH_IMAGE_HEADER, 1, byteorder=byteorder)[0]
2015 a = a.newbyteorder(byteorder)
2016 a.xunit = a.xunit[:a._xunit_len]
2017 a.um = a.um[:a._um_len]
2018 return a
2019
2028
2029 def read_double(data, byteorder):
2030 return struct.unpack(byteorder+('d' * (len(data) // 8)), data)
2031
2032 def read_bytes(data, byteorder):
2033
2034 return numpy.fromstring(data, 'uint8')
2035
2036 metadata_types = {
2037 b'info': ('info', read_string),
2038 b'labl': ('labels', read_string),
2039 b'rang': ('ranges', read_double),
2040 b'luts': ('luts', read_bytes),
2041 b'roi ': ('roi', read_bytes),
2042 b'over': ('overlays', read_bytes)}
2043 metadata_types.update(
2044 dict((k[::-1], v) for k, v in metadata_types.items()))
2045
2046 if not bytecounts:
2047 raise ValueError("no ImageJ meta data")
2048
2049 if not data[:4] in (b'IJIJ', b'JIJI'):
2050 raise ValueError("invalid ImageJ meta data")
2051
2052 header_size = bytecounts[0]
2053 if header_size < 12 or header_size > 804:
2054 raise ValueError("invalid ImageJ meta data header size")
2055
2056 ntypes = (header_size - 4) // 8
2057 header = struct.unpack(byteorder+'4sI'*ntypes, data[4:4+ntypes*8])
2058 pos = 4 + ntypes * 8
2059 counter = 0
2060 result = {}
2061 for mtype, count in zip(header[::2], header[1::2]):
2062 values = []
2063 name, func = metadata_types.get(mtype, (_str(mtype), read_bytes))
2064 for _ in range(count):
2065 counter += 1
2066 pos1 = pos + bytecounts[counter]
2067 values.append(func(data[pos:pos1], byteorder))
2068 pos = pos1
2069 result[name.strip()] = values[0] if count == 1 else values
2070 return result
2071
2074 """Return dict from ImageJ image_description tag."""
2075 def _bool(val):
2076 return {b'true': True, b'false': False}[val.lower()]
2077
2078 _str = str if sys.version_info[0] < 3 else lambda x: str(x, 'cp1252')
2079 result = {}
2080 for line in description.splitlines():
2081 try:
2082 key, val = line.split(b'=')
2083 except Exception:
2084 continue
2085 key = key.strip()
2086 val = val.strip()
2087 for dtype in (int, float, _bool, _str):
2088 try:
2089 val = dtype(val)
2090 break
2091 except Exception:
2092 pass
2093 result[_str(key)] = val
2094 return result
2095
2149
2150
2151 -def _replace_by(module_function, package=None, warn=True):
2152 """Try replace decorated function by module.function."""
2153 try:
2154 from importlib import import_module
2155 except ImportError:
2156 warnings.warn('Could not import module importlib')
2157 return lambda func: func
2158
2159 def decorate(func, module_function=module_function, warn=warn):
2160 try:
2161 module, function = module_function.split('.')
2162 if not package:
2163 module = import_module(module)
2164 else:
2165 module = import_module('.' + module, package=package)
2166 func, oldfunc = getattr(module, function), func
2167 globals()['__old_' + func.__name__] = oldfunc
2168 except Exception:
2169 if warn:
2170 warnings.warn("failed to import %s" % module_function)
2171 return func
2172
2173 return decorate
2174
2175
2176 @_replace_by('_tifffile.decodepackbits')
2177 -def decodepackbits(encoded):
2178 """Decompress PackBits encoded byte string.
2179
2180 PackBits is a simple byte-oriented run-length compression scheme.
2181
2182 """
2183 func = ord if sys.version[0] == '2' else lambda x: x
2184 result = []
2185 result_extend = result.extend
2186 i = 0
2187 try:
2188 while True:
2189 n = func(encoded[i]) + 1
2190 i += 1
2191 if n < 129:
2192 result_extend(encoded[i:i+n])
2193 i += n
2194 elif n > 129:
2195 result_extend(encoded[i:i+1] * (258-n))
2196 i += 1
2197 except IndexError:
2198 pass
2199 return b''.join(result) if sys.version[0] == '2' else bytes(result)
2200
2201
2202 @_replace_by('_tifffile.decodelzw')
2203 -def decodelzw(encoded):
2204 """Decompress LZW (Lempel-Ziv-Welch) encoded TIFF strip (byte string).
2205
2206 The strip must begin with a CLEAR code and end with an EOI code.
2207
2208 This is an implementation of the LZW decoding algorithm described in (1).
2209 It is not compatible with old style LZW compressed files like quad-lzw.tif.
2210
2211 """
2212 len_encoded = len(encoded)
2213 bitcount_max = len_encoded * 8
2214 unpack = struct.unpack
2215
2216 if sys.version[0] == '2':
2217 newtable = [chr(i) for i in range(256)]
2218 else:
2219 newtable = [bytes([i]) for i in range(256)]
2220 newtable.extend((0, 0))
2221
2222 def next_code():
2223 """Return integer of `bitw` bits at `bitcount` position in encoded."""
2224 start = bitcount // 8
2225 s = encoded[start:start+4]
2226 try:
2227 code = unpack('>I', s)[0]
2228 except Exception:
2229 code = unpack('>I', s + b'\x00'*(4-len(s)))[0]
2230 code <<= bitcount % 8
2231 code &= mask
2232 return code >> shr
2233
2234 switchbitch = {
2235 255: (9, 23, int(9*'1'+'0'*23, 2)),
2236 511: (10, 22, int(10*'1'+'0'*22, 2)),
2237 1023: (11, 21, int(11*'1'+'0'*21, 2)),
2238 2047: (12, 20, int(12*'1'+'0'*20, 2)), }
2239 bitw, shr, mask = switchbitch[255]
2240 bitcount = 0
2241
2242 if len_encoded < 4:
2243 raise ValueError("strip must be at least 4 characters long")
2244
2245 if next_code() != 256:
2246 raise ValueError("strip must begin with CLEAR code")
2247
2248 code = 0
2249 oldcode = 0
2250 result = []
2251 result_append = result.append
2252 while True:
2253 code = next_code()
2254 bitcount += bitw
2255 if code == 257 or bitcount >= bitcount_max:
2256 break
2257 if code == 256:
2258 table = newtable[:]
2259 table_append = table.append
2260 lentable = 258
2261 bitw, shr, mask = switchbitch[255]
2262 code = next_code()
2263 bitcount += bitw
2264 if code == 257:
2265 break
2266 result_append(table[code])
2267 else:
2268 if code < lentable:
2269 decoded = table[code]
2270 newcode = table[oldcode] + decoded[:1]
2271 else:
2272 newcode = table[oldcode]
2273 newcode += newcode[:1]
2274 decoded = newcode
2275 result_append(decoded)
2276 table_append(newcode)
2277 lentable += 1
2278 oldcode = code
2279 if lentable in switchbitch:
2280 bitw, shr, mask = switchbitch[lentable]
2281
2282 if code != 257:
2283 warnings.warn(
2284 "decodelzw encountered unexpected end of stream (code %i)" % code)
2285
2286 return b''.join(result)
2287
2288
2289 @_replace_by('_tifffile.unpackints')
2290 -def unpackints(data, dtype, itemsize, runlen=0):
2291 """Decompress byte string to array of integers of any bit size <= 32.
2292
2293 Parameters
2294 ----------
2295 data : byte str
2296 Data to decompress.
2297 dtype : numpy.dtype or str
2298 A numpy boolean or integer type.
2299 itemsize : int
2300 Number of bits per integer.
2301 runlen : int
2302 Number of consecutive integers, after which to start at next byte.
2303
2304 """
2305 if itemsize == 1:
2306 data = numpy.fromstring(data, '|B')
2307 data = numpy.unpackbits(data)
2308 if runlen % 8:
2309 data = data.reshape(-1, runlen + (8 - runlen % 8))
2310 data = data[:, :runlen].reshape(-1)
2311 return data.astype(dtype)
2312
2313 dtype = numpy.dtype(dtype)
2314 if itemsize in (8, 16, 32, 64):
2315 return numpy.fromstring(data, dtype)
2316 if itemsize < 1 or itemsize > 32:
2317 raise ValueError("itemsize out of range: %i" % itemsize)
2318 if dtype.kind not in "biu":
2319 raise ValueError("invalid dtype")
2320
2321 itembytes = next(i for i in (1, 2, 4, 8) if 8 * i >= itemsize)
2322 if itembytes != dtype.itemsize:
2323 raise ValueError("dtype.itemsize too small")
2324 if runlen == 0:
2325 runlen = len(data) // itembytes
2326 skipbits = runlen*itemsize % 8
2327 if skipbits:
2328 skipbits = 8 - skipbits
2329 shrbits = itembytes*8 - itemsize
2330 bitmask = int(itemsize*'1'+'0'*shrbits, 2)
2331 dtypestr = '>' + dtype.char
2332
2333 unpack = struct.unpack
2334 l = runlen * (len(data)*8 // (runlen*itemsize + skipbits))
2335 result = numpy.empty((l, ), dtype)
2336 bitcount = 0
2337 for i in range(len(result)):
2338 start = bitcount // 8
2339 s = data[start:start+itembytes]
2340 try:
2341 code = unpack(dtypestr, s)[0]
2342 except Exception:
2343 code = unpack(dtypestr, s + b'\x00'*(itembytes-len(s)))[0]
2344 code <<= bitcount % 8
2345 code &= bitmask
2346 result[i] = code >> shrbits
2347 bitcount += itemsize
2348 if (i+1) % runlen == 0:
2349 bitcount += skipbits
2350 return result
2351
2352
2353 -def unpackrgb(data, dtype='<B', bitspersample=(5, 6, 5), rescale=True):
2354 """Return array from byte string containing packed samples.
2355
2356 Use to unpack RGB565 or RGB555 to RGB888 format.
2357
2358 Parameters
2359 ----------
2360 data : byte str
2361 The data to be decoded. Samples in each pixel are stored consecutively.
2362 Pixels are aligned to 8, 16, or 32 bit boundaries.
2363 dtype : numpy.dtype
2364 The sample data type. The byteorder applies also to the data stream.
2365 bitspersample : tuple
2366 Number of bits for each sample in a pixel.
2367 rescale : bool
2368 Upscale samples to the number of bits in dtype.
2369
2370 Returns
2371 -------
2372 result : ndarray
2373 Flattened array of unpacked samples of native dtype.
2374
2375 Examples
2376 --------
2377 >>> data = struct.pack('BBBB', 0x21, 0x08, 0xff, 0xff)
2378 >>> print(unpackrgb(data, '<B', (5, 6, 5), False))
2379 [ 1 1 1 31 63 31]
2380 >>> print(unpackrgb(data, '<B', (5, 6, 5)))
2381 [ 8 4 8 255 255 255]
2382 >>> print(unpackrgb(data, '<B', (5, 5, 5)))
2383 [ 16 8 8 255 255 255]
2384
2385 """
2386 dtype = numpy.dtype(dtype)
2387 bits = int(numpy.sum(bitspersample))
2388 if not (bits <= 32 and all(i <= dtype.itemsize*8 for i in bitspersample)):
2389 raise ValueError("sample size not supported %s" % str(bitspersample))
2390 dt = next(i for i in 'BHI' if numpy.dtype(i).itemsize*8 >= bits)
2391 data = numpy.fromstring(data, dtype.byteorder+dt)
2392 result = numpy.empty((data.size, len(bitspersample)), dtype.char)
2393 for i, bps in enumerate(bitspersample):
2394 t = data >> int(numpy.sum(bitspersample[i+1:]))
2395 t &= int('0b'+'1'*bps, 2)
2396 if rescale:
2397 o = ((dtype.itemsize * 8) // bps + 1) * bps
2398 if o > data.dtype.itemsize * 8:
2399 t = t.astype('I')
2400 t *= (2**o - 1) // (2**bps - 1)
2401 t //= 2**(o - (dtype.itemsize * 8))
2402 result[:, i] = t
2403 return result.reshape(-1)
2404
2407 """Return reoriented view of image array.
2408
2409 Parameters
2410 ----------
2411 image : numpy array
2412 Non-squeezed output of asarray() functions.
2413 Axes -3 and -2 must be image length and width respectively.
2414 orientation : int or str
2415 One of TIFF_ORIENTATIONS keys or values.
2416
2417 """
2418 o = TIFF_ORIENTATIONS.get(orientation, orientation)
2419 if o == 'top_left':
2420 return image
2421 elif o == 'top_right':
2422 return image[..., ::-1, :]
2423 elif o == 'bottom_left':
2424 return image[..., ::-1, :, :]
2425 elif o == 'bottom_right':
2426 return image[..., ::-1, ::-1, :]
2427 elif o == 'left_top':
2428 return numpy.swapaxes(image, -3, -2)
2429 elif o == 'right_top':
2430 return numpy.swapaxes(image, -3, -2)[..., ::-1, :]
2431 elif o == 'left_bottom':
2432 return numpy.swapaxes(image, -3, -2)[..., ::-1, :, :]
2433 elif o == 'right_bottom':
2434 return numpy.swapaxes(image, -3, -2)[..., ::-1, ::-1, :]
2435
2438 """Return array from data in binary file.
2439
2440 Work around numpy issue #2230, "numpy.fromfile does not accept StringIO
2441 object" https://github.com/numpy/numpy/issues/2230.
2442
2443 """
2444 try:
2445 return numpy.fromfile(arg, dtype, count, sep)
2446 except IOError:
2447 if count < 0:
2448 size = 2**30
2449 else:
2450 size = count * numpy.dtype(dtype).itemsize
2451 data = arg.read(int(size))
2452 return numpy.fromstring(data, dtype, count, sep)
2453
2456 """Return string truncated at first null character."""
2457 i = string.find(b'\x00')
2458 return string if (i < 0) else string[:i]
2459
2467
2470 """Return human sorted list of strings.
2471
2472 >>> natural_sorted(['f1', 'f2', 'f10'])
2473 ['f1', 'f2', 'f10']
2474
2475 """
2476 def sortkey(x):
2477 return [(int(c) if c.isdigit() else c) for c in re.split(numbers, x)]
2478 numbers = re.compile('(\d+)')
2479 return sorted(iterable, key=sortkey)
2480
2483 """Return datetime object from timestamp in Excel serial format.
2484
2485 Examples
2486 --------
2487 >>> datetime_from_timestamp(40237.029999999795)
2488 datetime.datetime(2010, 2, 28, 0, 43, 11, 999982)
2489
2490 """
2491 return epoch + datetime.timedelta(n)
2492
2495 """Read all images in directory. Print error message on failure.
2496
2497 Examples
2498 --------
2499 >>> test_tifffile(verbose=False)
2500
2501 """
2502 successful = 0
2503 failed = 0
2504 start = time.time()
2505 for f in glob.glob(os.path.join(directory, '*.*')):
2506 if verbose:
2507 print("\n%s>\n" % f.lower(), end='')
2508 t0 = time.time()
2509 try:
2510 tif = TiffFile(f, multifile=True)
2511 except Exception as e:
2512 if not verbose:
2513 print(f, end=' ')
2514 print("ERROR:", e)
2515 failed += 1
2516 continue
2517 try:
2518 img = tif.asarray()
2519 except ValueError:
2520 try:
2521 img = tif[0].asarray()
2522 except Exception as e:
2523 if not verbose:
2524 print(f, end=' ')
2525 print("ERROR:", e)
2526 failed += 1
2527 continue
2528 finally:
2529 tif.close()
2530 successful += 1
2531 if verbose:
2532 print("%s, %s %s, %s, %.0f ms" % (
2533 str(tif), str(img.shape), img.dtype, tif[0].compression,
2534 (time.time()-t0) * 1e3))
2535 if verbose:
2536 print("\nSuccessfully read %i of %i files in %.3f s\n" % (
2537 successful, successful+failed, time.time()-start))
2538
2550
2551
2552 TIFF_PHOTOMETRICS = {
2553 0: 'miniswhite',
2554 1: 'minisblack',
2555 2: 'rgb',
2556 3: 'palette',
2557 4: 'mask',
2558 5: 'separated',
2559 6: 'cielab',
2560 7: 'icclab',
2561 8: 'itulab',
2562 32844: 'logl',
2563 32845: 'logluv',
2564 }
2565
2566 TIFF_COMPESSIONS = {
2567 1: None,
2568 2: 'ccittrle',
2569 3: 'ccittfax3',
2570 4: 'ccittfax4',
2571 5: 'lzw',
2572 6: 'ojpeg',
2573 7: 'jpeg',
2574 8: 'adobe_deflate',
2575 9: 't85',
2576 10: 't43',
2577 32766: 'next',
2578 32771: 'ccittrlew',
2579 32773: 'packbits',
2580 32809: 'thunderscan',
2581 32895: 'it8ctpad',
2582 32896: 'it8lw',
2583 32897: 'it8mp',
2584 32898: 'it8bl',
2585 32908: 'pixarfilm',
2586 32909: 'pixarlog',
2587 32946: 'deflate',
2588 32947: 'dcs',
2589 34661: 'jbig',
2590 34676: 'sgilog',
2591 34677: 'sgilog24',
2592 34712: 'jp2000',
2593 34713: 'nef',
2594 }
2595
2596 TIFF_DECOMPESSORS = {
2597 None: lambda x: x,
2598 'adobe_deflate': zlib.decompress,
2599 'deflate': zlib.decompress,
2600 'packbits': decodepackbits,
2601 'lzw': decodelzw,
2602 }
2603
2604 TIFF_DATA_TYPES = {
2605 1: '1B',
2606 2: '1s',
2607
2608 3: '1H',
2609 4: '1I',
2610 5: '2I',
2611
2612 6: '1b',
2613 7: '1B',
2614
2615 8: '1h',
2616 9: '1i',
2617 10: '2i',
2618
2619 11: '1f',
2620 12: '1d',
2621 13: '1I',
2622
2623
2624 16: '1Q',
2625 17: '1q',
2626 18: '1Q',
2627 }
2628
2629 TIFF_SAMPLE_FORMATS = {
2630 1: 'uint',
2631 2: 'int',
2632 3: 'float',
2633
2634
2635 6: 'complex',
2636 }
2637
2638 TIFF_SAMPLE_DTYPES = {
2639 ('uint', 1): '?',
2640 ('uint', 2): 'B',
2641 ('uint', 3): 'B',
2642 ('uint', 4): 'B',
2643 ('uint', 5): 'B',
2644 ('uint', 6): 'B',
2645 ('uint', 7): 'B',
2646 ('uint', 8): 'B',
2647 ('uint', 9): 'H',
2648 ('uint', 10): 'H',
2649 ('uint', 11): 'H',
2650 ('uint', 12): 'H',
2651 ('uint', 13): 'H',
2652 ('uint', 14): 'H',
2653 ('uint', 15): 'H',
2654 ('uint', 16): 'H',
2655 ('uint', 17): 'I',
2656 ('uint', 18): 'I',
2657 ('uint', 19): 'I',
2658 ('uint', 20): 'I',
2659 ('uint', 21): 'I',
2660 ('uint', 22): 'I',
2661 ('uint', 23): 'I',
2662 ('uint', 24): 'I',
2663 ('uint', 25): 'I',
2664 ('uint', 26): 'I',
2665 ('uint', 27): 'I',
2666 ('uint', 28): 'I',
2667 ('uint', 29): 'I',
2668 ('uint', 30): 'I',
2669 ('uint', 31): 'I',
2670 ('uint', 32): 'I',
2671 ('uint', 64): 'Q',
2672 ('int', 8): 'b',
2673 ('int', 16): 'h',
2674 ('int', 32): 'i',
2675 ('int', 64): 'q',
2676 ('float', 16): 'e',
2677 ('float', 32): 'f',
2678 ('float', 64): 'd',
2679 ('complex', 64): 'F',
2680 ('complex', 128): 'D',
2681 ('uint', (5, 6, 5)): 'B',
2682 }
2683
2684 TIFF_ORIENTATIONS = {
2685 1: 'top_left',
2686 2: 'top_right',
2687 3: 'bottom_right',
2688 4: 'bottom_left',
2689 5: 'left_top',
2690 6: 'right_top',
2691 7: 'right_bottom',
2692 8: 'left_bottom',
2693 }
2694
2695 AXES_LABELS = {
2696 'X': 'width',
2697 'Y': 'height',
2698 'Z': 'depth',
2699 'S': 'sample',
2700 'P': 'plane',
2701 'T': 'time',
2702 'C': 'channel',
2703 'A': 'angle',
2704 'F': 'phase',
2705 'R': 'tile',
2706 'H': 'lifetime',
2707 'E': 'lambda',
2708 'L': 'exposure',
2709 'V': 'event',
2710 'Q': 'other',
2711 }
2712
2713 AXES_LABELS.update(dict((v, k) for k, v in AXES_LABELS.items()))
2714
2715
2716 NIH_IMAGE_HEADER = [
2717 ('fileid', 'a8'),
2718 ('nlines', 'i2'),
2719 ('pixelsperline', 'i2'),
2720 ('version', 'i2'),
2721 ('oldlutmode', 'i2'),
2722 ('oldncolors', 'i2'),
2723 ('colors', 'u1', (3, 32)),
2724 ('oldcolorstart', 'i2'),
2725 ('colorwidth', 'i2'),
2726 ('extracolors', 'u2', (6, 3)),
2727 ('nextracolors', 'i2'),
2728 ('foregroundindex', 'i2'),
2729 ('backgroundindex', 'i2'),
2730 ('xscale', 'f8'),
2731 ('_x0', 'i2'),
2732 ('_x1', 'i2'),
2733 ('units_t', 'i2'),
2734 ('p1', [('x', 'i2'), ('y', 'i2')]),
2735 ('p2', [('x', 'i2'), ('y', 'i2')]),
2736 ('curvefit_t', 'i2'),
2737 ('ncoefficients', 'i2'),
2738 ('coeff', 'f8', 6),
2739 ('_um_len', 'u1'),
2740 ('um', 'a15'),
2741 ('_x2', 'u1'),
2742 ('binarypic', 'b1'),
2743 ('slicestart', 'i2'),
2744 ('sliceend', 'i2'),
2745 ('scalemagnification', 'f4'),
2746 ('nslices', 'i2'),
2747 ('slicespacing', 'f4'),
2748 ('currentslice', 'i2'),
2749 ('frameinterval', 'f4'),
2750 ('pixelaspectratio', 'f4'),
2751 ('colorstart', 'i2'),
2752 ('colorend', 'i2'),
2753 ('ncolors', 'i2'),
2754 ('fill1', '3u2'),
2755 ('fill2', '3u2'),
2756 ('colortable_t', 'u1'),
2757 ('lutmode_t', 'u1'),
2758 ('invertedtable', 'b1'),
2759 ('zeroclip', 'b1'),
2760 ('_xunit_len', 'u1'),
2761 ('xunit', 'a11'),
2762 ('stacktype_t', 'i2'),
2763 ]
2764
2765
2766
2767
2768
2769
2770
2771
2772
2773
2774
2775
2776
2777
2778
2779
2780
2781
2782 MM_TAG_IDS = {
2783 0: 'auto_scale',
2784 1: 'min_scale',
2785 2: 'max_scale',
2786 3: 'spatial_calibration',
2787
2788
2789
2790
2791 8: 'thresh_state',
2792 9: 'thresh_state_red',
2793 11: 'thresh_state_green',
2794 12: 'thresh_state_blue',
2795 13: 'thresh_state_lo',
2796 14: 'thresh_state_hi',
2797 15: 'zoom',
2798
2799
2800 18: 'current_buffer',
2801 19: 'gray_fit',
2802 20: 'gray_point_count',
2803
2804
2805
2806
2807
2808 26: 'standard_lut',
2809 27: 'wavelength',
2810
2811
2812
2813
2814
2815
2816
2817
2818 36: 'image_property',
2819
2820
2821
2822
2823
2824
2825
2826
2827
2828
2829 47: 'new_lut',
2830
2831 49: 'plane_property',
2832
2833 51: 'red_autoscale_info',
2834
2835
2836 54: 'red_minscale_info',
2837 55: 'red_maxscale_info',
2838 56: 'green_autoscale_info',
2839
2840
2841 59: 'green_minscale_info',
2842 60: 'green_maxscale_info',
2843 61: 'blue_autoscale_info',
2844
2845
2846 64: 'blue_min_scale_info',
2847 65: 'blue_max_scale_info',
2848
2849 }
2850
2851
2852 MM_DIMENSION = [
2853 ('name', 'a16'),
2854 ('size', 'i4'),
2855 ('origin', 'f8'),
2856 ('resolution', 'f8'),
2857 ('unit', 'a64'),
2858 ]
2859
2860 MM_HEADER = [
2861 ('header_flag', 'i2'),
2862 ('image_type', 'u1'),
2863 ('image_name', 'a257'),
2864 ('offset_data', 'u4'),
2865 ('palette_size', 'i4'),
2866 ('offset_palette0', 'u4'),
2867 ('offset_palette1', 'u4'),
2868 ('comment_size', 'i4'),
2869 ('offset_comment', 'u4'),
2870 ('dimensions', MM_DIMENSION, 10),
2871 ('offset_position', 'u4'),
2872 ('map_type', 'i2'),
2873 ('map_min', 'f8'),
2874 ('map_max', 'f8'),
2875 ('min_value', 'f8'),
2876 ('max_value', 'f8'),
2877 ('offset_map', 'u4'),
2878 ('gamma', 'f8'),
2879 ('offset', 'f8'),
2880 ('gray_channel', MM_DIMENSION),
2881 ('offset_thumbnail', 'u4'),
2882 ('voice_field', 'i4'),
2883 ('offset_voice_field', 'u4'),
2884 ]
2885
2886
2887 CZ_LSM_INFO = [
2888 ('magic_number', 'i4'),
2889 ('structure_size', 'i4'),
2890 ('dimension_x', 'i4'),
2891 ('dimension_y', 'i4'),
2892 ('dimension_z', 'i4'),
2893 ('dimension_channels', 'i4'),
2894 ('dimension_time', 'i4'),
2895 ('dimension_data_type', 'i4'),
2896 ('thumbnail_x', 'i4'),
2897 ('thumbnail_y', 'i4'),
2898 ('voxel_size_x', 'f8'),
2899 ('voxel_size_y', 'f8'),
2900 ('voxel_size_z', 'f8'),
2901 ('origin_x', 'f8'),
2902 ('origin_y', 'f8'),
2903 ('origin_z', 'f8'),
2904 ('scan_type', 'u2'),
2905 ('spectral_scan', 'u2'),
2906 ('data_type', 'u4'),
2907 ('offset_vector_overlay', 'u4'),
2908 ('offset_input_lut', 'u4'),
2909 ('offset_output_lut', 'u4'),
2910 ('offset_channel_colors', 'u4'),
2911 ('time_interval', 'f8'),
2912 ('offset_channel_data_types', 'u4'),
2913 ('offset_scan_information', 'u4'),
2914 ('offset_ks_data', 'u4'),
2915 ('offset_time_stamps', 'u4'),
2916 ('offset_event_list', 'u4'),
2917 ('offset_roi', 'u4'),
2918 ('offset_bleach_roi', 'u4'),
2919 ('offset_next_recording', 'u4'),
2920 ('display_aspect_x', 'f8'),
2921 ('display_aspect_y', 'f8'),
2922 ('display_aspect_z', 'f8'),
2923 ('display_aspect_time', 'f8'),
2924 ('offset_mean_of_roi_overlay', 'u4'),
2925 ('offset_topo_isoline_overlay', 'u4'),
2926 ('offset_topo_profile_overlay', 'u4'),
2927 ('offset_linescan_overlay', 'u4'),
2928 ('offset_toolbar_flags', 'u4'),
2929 ]
2930
2931
2932 CZ_LSM_INFO_READERS = {
2933 'scan_information': read_cz_lsm_scan_info,
2934 'time_stamps': read_cz_lsm_time_stamps,
2935 'event_list': read_cz_lsm_event_list,
2936 }
2937
2938
2939 CZ_SCAN_TYPES = {
2940 0: 'XYZCT',
2941 1: 'XYZCT',
2942 2: 'XYZCT',
2943 3: 'XYTCZ',
2944 4: 'XYZTC',
2945 5: 'XYTCZ',
2946 6: 'XYZTC',
2947 7: 'XYCTZ',
2948 8: 'XYCZT',
2949 9: 'XYTCZ',
2950 10: 'XYZCT',
2951 }
2952
2953
2954 CZ_DIMENSIONS = {
2955 'X': 'dimension_x',
2956 'Y': 'dimension_y',
2957 'Z': 'dimension_z',
2958 'C': 'dimension_channels',
2959 'T': 'dimension_time',
2960 }
2961
2962
2963 CZ_DATA_TYPES = {
2964 0: 'varying data types',
2965 2: '12 bit unsigned integer',
2966 5: '32 bit float',
2967 }
2968
2969 CZ_LSM_SCAN_INFO_ARRAYS = {
2970 0x20000000: "tracks",
2971 0x30000000: "lasers",
2972 0x60000000: "detectionchannels",
2973 0x80000000: "illuminationchannels",
2974 0xa0000000: "beamsplitters",
2975 0xc0000000: "datachannels",
2976 0x13000000: "markers",
2977 0x11000000: "timers",
2978 }
2979
2980 CZ_LSM_SCAN_INFO_STRUCTS = {
2981 0x40000000: "tracks",
2982 0x50000000: "lasers",
2983 0x70000000: "detectionchannels",
2984 0x90000000: "illuminationchannels",
2985 0xb0000000: "beamsplitters",
2986 0xd0000000: "datachannels",
2987 0x14000000: "markers",
2988 0x12000000: "timers",
2989 }
2990
2991 CZ_LSM_SCAN_INFO_ATTRIBUTES = {
2992 0x10000001: "name",
2993 0x10000002: "description",
2994 0x10000003: "notes",
2995 0x10000004: "objective",
2996 0x10000005: "processing_summary",
2997 0x10000006: "special_scan_mode",
2998 0x10000007: "oledb_recording_scan_type",
2999 0x10000008: "oledb_recording_scan_mode",
3000 0x10000009: "number_of_stacks",
3001 0x1000000a: "lines_per_plane",
3002 0x1000000b: "samples_per_line",
3003 0x1000000c: "planes_per_volume",
3004 0x1000000d: "images_width",
3005 0x1000000e: "images_height",
3006 0x1000000f: "images_number_planes",
3007 0x10000010: "images_number_stacks",
3008 0x10000011: "images_number_channels",
3009 0x10000012: "linscan_xy_size",
3010 0x10000013: "scan_direction",
3011 0x10000014: "time_series",
3012 0x10000015: "original_scan_data",
3013 0x10000016: "zoom_x",
3014 0x10000017: "zoom_y",
3015 0x10000018: "zoom_z",
3016 0x10000019: "sample_0x",
3017 0x1000001a: "sample_0y",
3018 0x1000001b: "sample_0z",
3019 0x1000001c: "sample_spacing",
3020 0x1000001d: "line_spacing",
3021 0x1000001e: "plane_spacing",
3022 0x1000001f: "plane_width",
3023 0x10000020: "plane_height",
3024 0x10000021: "volume_depth",
3025 0x10000023: "nutation",
3026 0x10000034: "rotation",
3027 0x10000035: "precession",
3028 0x10000036: "sample_0time",
3029 0x10000037: "start_scan_trigger_in",
3030 0x10000038: "start_scan_trigger_out",
3031 0x10000039: "start_scan_event",
3032 0x10000040: "start_scan_time",
3033 0x10000041: "stop_scan_trigger_in",
3034 0x10000042: "stop_scan_trigger_out",
3035 0x10000043: "stop_scan_event",
3036 0x10000044: "stop_scan_time",
3037 0x10000045: "use_rois",
3038 0x10000046: "use_reduced_memory_rois",
3039 0x10000047: "user",
3040 0x10000048: "use_bccorrection",
3041 0x10000049: "position_bccorrection1",
3042 0x10000050: "position_bccorrection2",
3043 0x10000051: "interpolation_y",
3044 0x10000052: "camera_binning",
3045 0x10000053: "camera_supersampling",
3046 0x10000054: "camera_frame_width",
3047 0x10000055: "camera_frame_height",
3048 0x10000056: "camera_offset_x",
3049 0x10000057: "camera_offset_y",
3050
3051 0x50000001: "name",
3052 0x50000002: "acquire",
3053 0x50000003: "power",
3054
3055 0x40000001: "multiplex_type",
3056 0x40000002: "multiplex_order",
3057 0x40000003: "sampling_mode",
3058 0x40000004: "sampling_method",
3059 0x40000005: "sampling_number",
3060 0x40000006: "acquire",
3061 0x40000007: "sample_observation_time",
3062 0x4000000b: "time_between_stacks",
3063 0x4000000c: "name",
3064 0x4000000d: "collimator1_name",
3065 0x4000000e: "collimator1_position",
3066 0x4000000f: "collimator2_name",
3067 0x40000010: "collimator2_position",
3068 0x40000011: "is_bleach_track",
3069 0x40000012: "is_bleach_after_scan_number",
3070 0x40000013: "bleach_scan_number",
3071 0x40000014: "trigger_in",
3072 0x40000015: "trigger_out",
3073 0x40000016: "is_ratio_track",
3074 0x40000017: "bleach_count",
3075 0x40000018: "spi_center_wavelength",
3076 0x40000019: "pixel_time",
3077 0x40000021: "condensor_frontlens",
3078 0x40000023: "field_stop_value",
3079 0x40000024: "id_condensor_aperture",
3080 0x40000025: "condensor_aperture",
3081 0x40000026: "id_condensor_revolver",
3082 0x40000027: "condensor_filter",
3083 0x40000028: "id_transmission_filter1",
3084 0x40000029: "id_transmission1",
3085 0x40000030: "id_transmission_filter2",
3086 0x40000031: "id_transmission2",
3087 0x40000032: "repeat_bleach",
3088 0x40000033: "enable_spot_bleach_pos",
3089 0x40000034: "spot_bleach_posx",
3090 0x40000035: "spot_bleach_posy",
3091 0x40000036: "spot_bleach_posz",
3092 0x40000037: "id_tubelens",
3093 0x40000038: "id_tubelens_position",
3094 0x40000039: "transmitted_light",
3095 0x4000003a: "reflected_light",
3096 0x4000003b: "simultan_grab_and_bleach",
3097 0x4000003c: "bleach_pixel_time",
3098
3099 0x70000001: "integration_mode",
3100 0x70000002: "special_mode",
3101 0x70000003: "detector_gain_first",
3102 0x70000004: "detector_gain_last",
3103 0x70000005: "amplifier_gain_first",
3104 0x70000006: "amplifier_gain_last",
3105 0x70000007: "amplifier_offs_first",
3106 0x70000008: "amplifier_offs_last",
3107 0x70000009: "pinhole_diameter",
3108 0x7000000a: "counting_trigger",
3109 0x7000000b: "acquire",
3110 0x7000000c: "point_detector_name",
3111 0x7000000d: "amplifier_name",
3112 0x7000000e: "pinhole_name",
3113 0x7000000f: "filter_set_name",
3114 0x70000010: "filter_name",
3115 0x70000013: "integrator_name",
3116 0x70000014: "detection_channel_name",
3117 0x70000015: "detection_detector_gain_bc1",
3118 0x70000016: "detection_detector_gain_bc2",
3119 0x70000017: "detection_amplifier_gain_bc1",
3120 0x70000018: "detection_amplifier_gain_bc2",
3121 0x70000019: "detection_amplifier_offset_bc1",
3122 0x70000020: "detection_amplifier_offset_bc2",
3123 0x70000021: "detection_spectral_scan_channels",
3124 0x70000022: "detection_spi_wavelength_start",
3125 0x70000023: "detection_spi_wavelength_stop",
3126 0x70000026: "detection_dye_name",
3127 0x70000027: "detection_dye_folder",
3128
3129 0x90000001: "name",
3130 0x90000002: "power",
3131 0x90000003: "wavelength",
3132 0x90000004: "aquire",
3133 0x90000005: "detchannel_name",
3134 0x90000006: "power_bc1",
3135 0x90000007: "power_bc2",
3136
3137 0xb0000001: "filter_set",
3138 0xb0000002: "filter",
3139 0xb0000003: "name",
3140
3141 0xd0000001: "name",
3142 0xd0000003: "acquire",
3143 0xd0000004: "color",
3144 0xd0000005: "sample_type",
3145 0xd0000006: "bits_per_sample",
3146 0xd0000007: "ratio_type",
3147 0xd0000008: "ratio_track1",
3148 0xd0000009: "ratio_track2",
3149 0xd000000a: "ratio_channel1",
3150 0xd000000b: "ratio_channel2",
3151 0xd000000c: "ratio_const1",
3152 0xd000000d: "ratio_const2",
3153 0xd000000e: "ratio_const3",
3154 0xd000000f: "ratio_const4",
3155 0xd0000010: "ratio_const5",
3156 0xd0000011: "ratio_const6",
3157 0xd0000012: "ratio_first_images1",
3158 0xd0000013: "ratio_first_images2",
3159 0xd0000014: "dye_name",
3160 0xd0000015: "dye_folder",
3161 0xd0000016: "spectrum",
3162 0xd0000017: "acquire",
3163
3164 0x14000001: "name",
3165 0x14000002: "description",
3166 0x14000003: "trigger_in",
3167 0x14000004: "trigger_out",
3168
3169 0x12000001: "name",
3170 0x12000002: "description",
3171 0x12000003: "interval",
3172 0x12000004: "trigger_in",
3173 0x12000005: "trigger_out",
3174 0x12000006: "activation_time",
3175 0x12000007: "activation_number",
3176 }
3177
3178
3179 TIFF_TAGS = {
3180 254: ('new_subfile_type', 0, 4, 1, TIFF_SUBFILE_TYPES()),
3181 255: ('subfile_type', None, 3, 1,
3182 {0: 'undefined', 1: 'image', 2: 'reduced_image', 3: 'page'}),
3183 256: ('image_width', None, 4, 1, None),
3184 257: ('image_length', None, 4, 1, None),
3185 258: ('bits_per_sample', 1, 3, 1, None),
3186 259: ('compression', 1, 3, 1, TIFF_COMPESSIONS),
3187 262: ('photometric', None, 3, 1, TIFF_PHOTOMETRICS),
3188 266: ('fill_order', 1, 3, 1, {1: 'msb2lsb', 2: 'lsb2msb'}),
3189 269: ('document_name', None, 2, None, None),
3190 270: ('image_description', None, 2, None, None),
3191 271: ('make', None, 2, None, None),
3192 272: ('model', None, 2, None, None),
3193 273: ('strip_offsets', None, 4, None, None),
3194 274: ('orientation', 1, 3, 1, TIFF_ORIENTATIONS),
3195 277: ('samples_per_pixel', 1, 3, 1, None),
3196 278: ('rows_per_strip', 2**32-1, 4, 1, None),
3197 279: ('strip_byte_counts', None, 4, None, None),
3198 280: ('min_sample_value', None, 3, None, None),
3199 281: ('max_sample_value', None, 3, None, None),
3200 282: ('x_resolution', None, 5, 1, None),
3201 283: ('y_resolution', None, 5, 1, None),
3202 284: ('planar_configuration', 1, 3, 1, {1: 'contig', 2: 'separate'}),
3203 285: ('page_name', None, 2, None, None),
3204 286: ('x_position', None, 5, 1, None),
3205 287: ('y_position', None, 5, 1, None),
3206 296: ('resolution_unit', 2, 4, 1, {1: 'none', 2: 'inch', 3: 'centimeter'}),
3207 297: ('page_number', None, 3, 2, None),
3208 305: ('software', None, 2, None, None),
3209 306: ('datetime', None, 2, None, None),
3210 315: ('artist', None, 2, None, None),
3211 316: ('host_computer', None, 2, None, None),
3212 317: ('predictor', 1, 3, 1, {1: None, 2: 'horizontal'}),
3213 320: ('color_map', None, 3, None, None),
3214 322: ('tile_width', None, 4, 1, None),
3215 323: ('tile_length', None, 4, 1, None),
3216 324: ('tile_offsets', None, 4, None, None),
3217 325: ('tile_byte_counts', None, 4, None, None),
3218 338: ('extra_samples', None, 3, None,
3219 {0: 'unspecified', 1: 'assocalpha', 2: 'unassalpha'}),
3220 339: ('sample_format', 1, 3, 1, TIFF_SAMPLE_FORMATS),
3221 347: ('jpeg_tables', None, None, None, None),
3222 530: ('ycbcr_subsampling', 1, 3, 2, None),
3223 531: ('ycbcr_positioning', 1, 3, 1, None),
3224 32997: ('image_depth', None, 4, 1, None),
3225 32998: ('tile_depth', None, 4, 1, None),
3226 33432: ('copyright', None, 1, None, None),
3227 33445: ('md_file_tag', None, 4, 1, None),
3228 33446: ('md_scale_pixel', None, 5, 1, None),
3229 33447: ('md_color_table', None, 3, None, None),
3230 33448: ('md_lab_name', None, 2, None, None),
3231 33449: ('md_sample_info', None, 2, None, None),
3232 33450: ('md_prep_date', None, 2, None, None),
3233 33451: ('md_prep_time', None, 2, None, None),
3234 33452: ('md_file_units', None, 2, None, None),
3235 33550: ('model_pixel_scale', None, 12, 3, None),
3236 33922: ('model_tie_point', None, 12, None, None),
3237 37510: ('user_comment', None, None, None, None),
3238 34665: ('exif_ifd', None, None, 1, None),
3239 34735: ('geo_key_directory', None, 3, None, None),
3240 34736: ('geo_double_params', None, 12, None, None),
3241 34737: ('geo_ascii_params', None, 2, None, None),
3242 34853: ('gps_ifd', None, None, 1, None),
3243 42112: ('gdal_metadata', None, 2, None, None),
3244 42113: ('gdal_nodata', None, 2, None, None),
3245 50838: ('imagej_byte_counts', None, None, None, None),
3246 50289: ('mc_xy_position', None, 12, 2, None),
3247 50290: ('mc_z_position', None, 12, 1, None),
3248 50291: ('mc_xy_calibration', None, 12, 3, None),
3249 50292: ('mc_lens_lem_na_n', None, 12, 3, None),
3250 50293: ('mc_channel_name', None, 1, None, None),
3251 50294: ('mc_ex_wavelength', None, 12, 1, None),
3252 50295: ('mc_time_stamp', None, 12, 1, None),
3253 65200: ('flex_xml', None, 2, None, None),
3254
3255 }
3256
3257
3258 CUSTOM_TAGS = {
3259 700: ('xmp', read_bytes),
3260 34377: ('photoshop', read_numpy),
3261 33723: ('iptc', read_bytes),
3262 34675: ('icc_profile', read_numpy),
3263 33628: ('mm_uic1', read_mm_uic1),
3264 33629: ('mm_uic2', read_mm_uic2),
3265 33630: ('mm_uic3', read_mm_uic3),
3266 33631: ('mm_uic4', read_mm_uic4),
3267 34361: ('mm_header', read_mm_header),
3268 34362: ('mm_stamp', read_mm_stamp),
3269 34386: ('mm_user_block', read_bytes),
3270 34412: ('cz_lsm_info', read_cz_lsm_info),
3271 43314: ('nih_image_header', read_nih_image_header),
3272
3273 40100: ('mc_id_old', read_bytes),
3274 50288: ('mc_id', read_bytes),
3275 50296: ('mc_frame_properties', read_bytes),
3276 50839: ('imagej_metadata', read_bytes),
3277 51123: ('micromanager_metadata', read_json),
3278 }
3279
3280
3281 PRINT_LINE_LEN = 79
3282
3283
3284 -def imshow(data, title=None, vmin=0, vmax=None, cmap=None,
3285 bitspersample=None, photometric='rgb', interpolation='nearest',
3286 dpi=96, figure=None, subplot=111, maxdim=8192, **kwargs):
3287 """Plot n-dimensional images using matplotlib.pyplot.
3288
3289 Return figure, subplot and plot axis.
3290 Requires pyplot already imported ``from matplotlib import pyplot``.
3291
3292 Parameters
3293 ----------
3294 bitspersample : int or None
3295 Number of bits per channel in integer RGB images.
3296 photometric : {'miniswhite', 'minisblack', 'rgb', or 'palette'}
3297 The color space of the image data.
3298 title : str
3299 Window and subplot title.
3300 figure : matplotlib.figure.Figure (optional).
3301 Matplotlib to use for plotting.
3302 subplot : int
3303 A matplotlib.pyplot.subplot axis.
3304 maxdim : int
3305 maximum image size in any dimension.
3306 kwargs : optional
3307 Arguments for matplotlib.pyplot.imshow.
3308
3309 """
3310
3311
3312
3313 isrgb = photometric in ('rgb', 'palette')
3314 data = numpy.atleast_2d(data.squeeze())
3315 data = data[(slice(0, maxdim), ) * len(data.shape)]
3316
3317 dims = data.ndim
3318 if dims < 2:
3319 raise ValueError("not an image")
3320 elif dims == 2:
3321 dims = 0
3322 isrgb = False
3323 else:
3324 if isrgb and data.shape[-3] in (3, 4):
3325 data = numpy.swapaxes(data, -3, -2)
3326 data = numpy.swapaxes(data, -2, -1)
3327 elif not isrgb and data.shape[-1] in (3, 4):
3328 data = numpy.swapaxes(data, -3, -1)
3329 data = numpy.swapaxes(data, -2, -1)
3330 isrgb = isrgb and data.shape[-1] in (3, 4)
3331 dims -= 3 if isrgb else 2
3332
3333 if photometric == 'palette' and isrgb:
3334 datamax = data.max()
3335 if datamax > 255:
3336 data >>= 8
3337 data = data.astype('B')
3338 elif data.dtype.kind in 'ui':
3339 if not (isrgb and data.dtype.itemsize <= 1) or bitspersample is None:
3340 try:
3341 bitspersample = int(math.ceil(math.log(data.max(), 2)))
3342 except Exception:
3343 bitspersample = data.dtype.itemsize * 8
3344 elif not isinstance(bitspersample, int):
3345
3346 bitspersample = data.dtype.itemsize * 8
3347 datamax = 2**bitspersample
3348 if isrgb:
3349 if bitspersample < 8:
3350 data <<= 8 - bitspersample
3351 elif bitspersample > 8:
3352 data >>= bitspersample - 8
3353 data = data.astype('B')
3354 elif data.dtype.kind == 'f':
3355 datamax = data.max()
3356 if isrgb and datamax > 1.0:
3357 if data.dtype.char == 'd':
3358 data = data.astype('f')
3359 data /= datamax
3360 elif data.dtype.kind == 'b':
3361 datamax = 1
3362 elif data.dtype.kind == 'c':
3363 raise NotImplementedError("complex type")
3364
3365 if not isrgb:
3366 if vmax is None:
3367 vmax = datamax
3368 if vmin is None:
3369 if data.dtype.kind == 'i':
3370 dtmin = numpy.iinfo(data.dtype).min
3371 vmin = numpy.min(data)
3372 if vmin == dtmin:
3373 vmin = numpy.min(data > dtmin)
3374 if data.dtype.kind == 'f':
3375 dtmin = numpy.finfo(data.dtype).min
3376 vmin = numpy.min(data)
3377 if vmin == dtmin:
3378 vmin = numpy.min(data > dtmin)
3379 else:
3380 vmin = 0
3381
3382 pyplot = sys.modules['matplotlib.pyplot']
3383
3384 if figure is None:
3385 pyplot.rc('font', family='sans-serif', weight='normal', size=8)
3386 figure = pyplot.figure(dpi=dpi, figsize=(10.3, 6.3), frameon=True,
3387 facecolor='1.0', edgecolor='w')
3388 try:
3389 figure.canvas.manager.window.title(title)
3390 except Exception:
3391 pass
3392 pyplot.subplots_adjust(bottom=0.03*(dims+2), top=0.9,
3393 left=0.1, right=0.95, hspace=0.05, wspace=0.0)
3394 subplot = pyplot.subplot(subplot)
3395
3396 if title:
3397 try:
3398 title = unicode(title, 'Windows-1252')
3399 except TypeError:
3400 pass
3401 pyplot.title(title, size=11)
3402
3403 if cmap is None:
3404 if data.dtype.kind in 'ub' and vmin == 0:
3405 cmap = 'gray'
3406 else:
3407 cmap = 'coolwarm'
3408 if photometric == 'miniswhite':
3409 cmap += '_r'
3410
3411 image = pyplot.imshow(data[(0, ) * dims].squeeze(), vmin=vmin, vmax=vmax,
3412 cmap=cmap, interpolation=interpolation, **kwargs)
3413
3414 if not isrgb:
3415 pyplot.colorbar()
3416
3417 def format_coord(x, y):
3418
3419 x = int(x + 0.5)
3420 y = int(y + 0.5)
3421 try:
3422 if dims:
3423 return "%s @ %s [%4i, %4i]" % (cur_ax_dat[1][y, x],
3424 current, x, y)
3425 else:
3426 return "%s @ [%4i, %4i]" % (data[y, x], x, y)
3427 except IndexError:
3428 return ""
3429
3430 pyplot.gca().format_coord = format_coord
3431
3432 if dims:
3433 current = list((0, ) * dims)
3434 cur_ax_dat = [0, data[tuple(current)].squeeze()]
3435 sliders = [pyplot.Slider(
3436 pyplot.axes([0.125, 0.03*(axis+1), 0.725, 0.025]),
3437 'Dimension %i' % axis, 0, data.shape[axis]-1, 0, facecolor='0.5',
3438 valfmt='%%.0f [%i]' % data.shape[axis]) for axis in range(dims)]
3439 for slider in sliders:
3440 slider.drawon = False
3441
3442 def set_image(current, sliders=sliders, data=data):
3443
3444 cur_ax_dat[1] = data[tuple(current)].squeeze()
3445 image.set_data(cur_ax_dat[1])
3446 for ctrl, index in zip(sliders, current):
3447 ctrl.eventson = False
3448 ctrl.set_val(index)
3449 ctrl.eventson = True
3450 figure.canvas.draw()
3451
3452 def on_changed(index, axis, data=data, current=current):
3453
3454 index = int(round(index))
3455 cur_ax_dat[0] = axis
3456 if index == current[axis]:
3457 return
3458 if index >= data.shape[axis]:
3459 index = 0
3460 elif index < 0:
3461 index = data.shape[axis] - 1
3462 current[axis] = index
3463 set_image(current)
3464
3465 def on_keypressed(event, data=data, current=current):
3466
3467 key = event.key
3468 axis = cur_ax_dat[0]
3469 if str(key) in '0123456789':
3470 on_changed(key, axis)
3471 elif key == 'right':
3472 on_changed(current[axis] + 1, axis)
3473 elif key == 'left':
3474 on_changed(current[axis] - 1, axis)
3475 elif key == 'up':
3476 cur_ax_dat[0] = 0 if axis == len(data.shape)-1 else axis + 1
3477 elif key == 'down':
3478 cur_ax_dat[0] = len(data.shape)-1 if axis == 0 else axis - 1
3479 elif key == 'end':
3480 on_changed(data.shape[axis] - 1, axis)
3481 elif key == 'home':
3482 on_changed(0, axis)
3483
3484 figure.canvas.mpl_connect('key_press_event', on_keypressed)
3485 for axis, ctrl in enumerate(sliders):
3486 ctrl.on_changed(lambda k, a=axis: on_changed(k, a))
3487
3488 return figure, subplot, image
3489
3492 """Block the GUI. For use as skimage plugin."""
3493 pyplot = sys.modules['matplotlib.pyplot']
3494 pyplot.show()
3495
3496
3497 -def main(argv=None):
3498 """Command line usage main function."""
3499 if float(sys.version[0:3]) < 2.6:
3500 print("This script requires Python version 2.6 or better.")
3501 print("This is Python version %s" % sys.version)
3502 return 0
3503 if argv is None:
3504 argv = sys.argv
3505
3506 import optparse
3507
3508 search_doc = lambda r, d: re.search(r, __doc__).group(1) if __doc__ else d
3509 parser = optparse.OptionParser(
3510 usage="usage: %prog [options] path",
3511 description=search_doc("\n\n([^|]*?)\n\n", ''),
3512 version="%%prog %s" % search_doc(":Version: (.*)", "Unknown"))
3513 opt = parser.add_option
3514 opt('-p', '--page', dest='page', type='int', default=-1,
3515 help="display single page")
3516 opt('-s', '--series', dest='series', type='int', default=-1,
3517 help="display series of pages of same shape")
3518 opt('--nomultifile', dest='nomultifile', action='store_true',
3519 default=False, help="don't read OME series from multiple files")
3520 opt('--noplot', dest='noplot', action='store_true', default=False,
3521 help="don't display images")
3522 opt('--interpol', dest='interpol', metavar='INTERPOL', default='bilinear',
3523 help="image interpolation method")
3524 opt('--dpi', dest='dpi', type='int', default=96,
3525 help="set plot resolution")
3526 opt('--debug', dest='debug', action='store_true', default=False,
3527 help="raise exception on failures")
3528 opt('--test', dest='test', action='store_true', default=False,
3529 help="try read all images in path")
3530 opt('--doctest', dest='doctest', action='store_true', default=False,
3531 help="runs the internal tests")
3532 opt('-v', '--verbose', dest='verbose', action='store_true', default=True)
3533 opt('-q', '--quiet', dest='verbose', action='store_false')
3534
3535 settings, path = parser.parse_args()
3536 path = ' '.join(path)
3537
3538 if settings.doctest:
3539 import doctest
3540 doctest.testmod()
3541 return 0
3542 if not path:
3543 parser.error("No file specified")
3544 if settings.test:
3545 test_tifffile(path, settings.verbose)
3546 return 0
3547
3548 if any(i in path for i in '?*'):
3549 path = glob.glob(path)
3550 if not path:
3551 print('no files match the pattern')
3552 return 0
3553
3554
3555 path = path[0]
3556
3557 print("Reading file structure...", end=' ')
3558 start = time.time()
3559 try:
3560 tif = TiffFile(path, multifile=not settings.nomultifile)
3561 except Exception as e:
3562 if settings.debug:
3563 raise
3564 else:
3565 print("\n", e)
3566 sys.exit(0)
3567 print("%.3f ms" % ((time.time()-start) * 1e3))
3568
3569 if tif.is_ome:
3570 settings.norgb = True
3571
3572 images = [(None, tif[0 if settings.page < 0 else settings.page])]
3573 if not settings.noplot:
3574 print("Reading image data... ", end=' ')
3575
3576 def notnone(x):
3577 return next(i for i in x if i is not None)
3578 start = time.time()
3579 try:
3580 if settings.page >= 0:
3581 images = [(tif.asarray(key=settings.page),
3582 tif[settings.page])]
3583 elif settings.series >= 0:
3584 images = [(tif.asarray(series=settings.series),
3585 notnone(tif.series[settings.series].pages))]
3586 else:
3587 images = []
3588 for i, s in enumerate(tif.series):
3589 try:
3590 images.append(
3591 (tif.asarray(series=i), notnone(s.pages)))
3592 except ValueError as e:
3593 images.append((None, notnone(s.pages)))
3594 if settings.debug:
3595 raise
3596 else:
3597 print("\n* series %i failed: %s... " % (i, e),
3598 end='')
3599 print("%.3f ms" % ((time.time()-start) * 1e3))
3600 except Exception as e:
3601 if settings.debug:
3602 raise
3603 else:
3604 print(e)
3605
3606 tif.close()
3607
3608 print("\nTIFF file:", tif)
3609 print()
3610 for i, s in enumerate(tif.series):
3611 print ("Series %i" % i)
3612 print(s)
3613 print()
3614 for i, page in images:
3615 print(page)
3616 print(page.tags)
3617 if page.is_palette:
3618 print("\nColor Map:", page.color_map.shape, page.color_map.dtype)
3619 for attr in ('cz_lsm_info', 'cz_lsm_scan_information', 'mm_uic_tags',
3620 'mm_header', 'imagej_tags', 'micromanager_metadata',
3621 'nih_image_header'):
3622 if hasattr(page, attr):
3623 print("", attr.upper(), Record(getattr(page, attr)), sep="\n")
3624 print()
3625 if page.is_micromanager:
3626 print('MICROMANAGER_FILE_METADATA')
3627 print(Record(tif.micromanager_metadata))
3628
3629 if images and not settings.noplot:
3630 try:
3631 import matplotlib
3632 matplotlib.use('TkAgg')
3633 from matplotlib import pyplot
3634 except ImportError as e:
3635 warnings.warn("failed to import matplotlib.\n%s" % e)
3636 else:
3637 for img, page in images:
3638 if img is None:
3639 continue
3640 vmin, vmax = None, None
3641 if 'gdal_nodata' in page.tags:
3642 vmin = numpy.min(img[img > float(page.gdal_nodata)])
3643 if page.is_stk:
3644 try:
3645 vmin = page.mm_uic_tags['min_scale']
3646 vmax = page.mm_uic_tags['max_scale']
3647 except KeyError:
3648 pass
3649 else:
3650 if vmax <= vmin:
3651 vmin, vmax = None, None
3652 title = "%s\n %s" % (str(tif), str(page))
3653 imshow(img, title=title, vmin=vmin, vmax=vmax,
3654 bitspersample=page.bits_per_sample,
3655 photometric=page.photometric,
3656 interpolation=settings.interpol,
3657 dpi=settings.dpi)
3658 pyplot.show()
3659
3660
3661 TIFFfile = TiffFile
3662
3663 if sys.version_info[0] > 2:
3664 basestring = str, bytes
3665 unicode = str
3666
3667 if __name__ == "__main__":
3668 sys.exit(main())
3669