Coverage for larch/io/tifffile.py: 12%

1380 statements  

« prev     ^ index     » next       coverage.py v7.6.0, created at 2024-10-16 21:04 +0000

1#!/usr/bin/env python 

2# -*- coding: utf-8 -*- 

3# tifffile.py 

4 

5# Copyright (c) 2008-2012, Christoph Gohlke 

6# Copyright (c) 2008-2012, The Regents of the University of California 

7# Produced at the Laboratory for Fluorescence Dynamics 

8# All rights reserved. 

9# 

10# Redistribution and use in source and binary forms, with or without 

11# modification, are permitted provided that the following conditions are met: 

12# 

13# * Redistributions of source code must retain the above copyright 

14# notice, this list of conditions and the following disclaimer. 

15# * Redistributions in binary form must reproduce the above copyright 

16# notice, this list of conditions and the following disclaimer in the 

17# documentation and/or other materials provided with the distribution. 

18# * Neither the name of the copyright holders nor the names of any 

19# contributors may be used to endorse or promote products derived 

20# from this software without specific prior written permission. 

21# 

22# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 

23# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 

24# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 

25# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 

26# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 

27# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 

28# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 

29# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 

30# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 

31# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 

32# POSSIBILITY OF SUCH DAMAGE. 

33 

34"""Read and write image data from and to TIFF files. 

35 

36Image and meta-data can be read from TIFF, BigTIFF, OME-TIFF, STK, LSM, NIH, 

37and FluoView files. Only a subset of the TIFF specification is supported, 

38mainly uncompressed and losslessly compressed 2**(0 to 6) bit integer, 

3916, 32 and 64-bit float, grayscale and RGB(A) images, which are commonly 

40used in bio-scientific imaging. Specifically, reading JPEG/CCITT 

41compressed image data or EXIF/IPTC/GPS/XMP meta-data is not implemented. 

42Only primary info records are read for STK, FluoView, and NIH image formats. 

43 

44TIFF, the Tagged Image File Format, is under the control of Adobe Systems. 

45BigTIFF allows for files greater than 4 GB. STK, LSM, FluoView, and OME-TIFF 

46are custom extensions defined by MetaMorph, Carl Zeiss MicroImaging, 

47Olympus, and the Open Microscopy Environment consortium respectively. 

48 

49The API is not stable yet and might change between revisions. 

50Tested on little-endian platforms only. 

51 

52For command line usage run ``python tifffile.py --help`` 

53 

54:Authors: 

55 `Christoph Gohlke <http://www.lfd.uci.edu/~gohlke/>`__, 

56 Laboratory for Fluorescence Dynamics, University of California, Irvine 

57 

58:Version: 2012.07.05 

59 

60Requirements 

61------------ 

62* `CPython 2.7 or 3.2 <http://www.python.org>`__ 

63* `Numpy 1.6 <http://numpy.scipy.org>`__ 

64* `Matplotlib 1.1 <http://matplotlib.sourceforge.net>`__ 

65 (optional for plotting) 

66* `tifffile.c 2012.01.01 <http://www.lfd.uci.edu/~gohlke/>`__ 

67 (optional for faster decoding of PackBits and LZW encoded strings) 

68 

69Acknowledgements 

70---------------- 

71* Egor Zindy, University of Manchester, for cz_lsm_scan_info specifics. 

72* Wim Lewis, for a bug fix and some read_cz_lsm functions. 

73 

74References 

75---------- 

76(1) TIFF 6.0 Specification and Supplements. Adobe Systems Incorporated. 

77 http://partners.adobe.com/public/developer/tiff/ 

78(2) TIFF File Format FAQ. http://www.awaresystems.be/imaging/tiff/faq.html 

79(3) MetaMorph Stack (STK) Image File Format. 

80 http://support.meta.moleculardevices.com/docs/t10243.pdf 

81(4) File Format Description - LSM 5xx Release 2.0. 

82 http://ibb.gsf.de/homepage/karsten.rodenacker/IDL/Lsmfile.doc 

83(5) BioFormats. http://www.loci.wisc.edu/ome/formats.html 

84(6) The OME-TIFF format. 

85 http://www.openmicroscopy.org/site/support/file-formats/ome-tiff 

86 

87Examples 

88-------- 

89>>> data = numpy.random.rand(301, 219) 

90>>> imsave('temp.tif', data) 

91>>> image = imread('temp.tif') 

92>>> assert numpy.all(image == data) 

93 

94>>> tif = TIFFfile('test.tif') 

95>>> images = tif.asarray() 

96>>> image0 = tif[0].asarray() 

97>>> for page in tif: 

98... for tag in page.tags.values(): 

99... t = tag.name, tag.value 

100... image = page.asarray() 

101... if page.is_rgb: pass 

102... if page.is_palette: 

103... t = page.color_map 

104... if page.is_stk: 

105... t = page.mm_uic_tags.number_planes 

106... if page.is_lsm: 

107... t = page.cz_lsm_info 

108>>> tif.close() 

109 

110""" 

111 

112from __future__ import division, print_function 

113 

114import sys 

115import os 

116import math 

117import zlib 

118import time 

119import struct 

120import warnings 

121import datetime 

122import collections 

123from xml.etree import cElementTree as ElementTree 

124 

125import numpy 

126 

127__all__ = ['imsave', 'imread', 'imshow', 'TIFFfile'] 

128 

129 

130def imsave(filename, data, photometric=None, planarconfig=None, 

131 resolution=None, description=None, software='tifffile.py', 

132 byteorder=None, bigtiff=False): 

133 """Write image data to TIFF file. 

134 

135 Image data are written uncompressed in one stripe per plane. 

136 Dimensions larger than 2 or 3 (depending on photometric mode and 

137 planar configuration) are flattened and saved as separate pages. 

138 

139 Parameters 

140 ---------- 

141 filename : str 

142 Name of file to write. 

143 data : array_like 

144 Input image. The last dimensions are assumed to be image height, 

145 width, and samples. 

146 photometric : {'minisblack', 'miniswhite', 'rgb'} 

147 The color space of the image data. 

148 By default this setting is inferred from the data shape. 

149 planarconfig : {'contig', 'planar'} 

150 Specifies if samples are stored contiguous or in separate planes. 

151 By default this setting is inferred from the data shape. 

152 'contig': last dimension contains samples. 

153 'planar': third last dimension contains samples. 

154 resolution : ((int, int), (int, int)) 

155 X and Y resolution in dots per inch as rational numbers. 

156 description : str 

157 The subject of the image. Saved with the first page only. 

158 software : str 

159 Name of the software used to create the image. 

160 Saved with the first page only. 

161 byteorder : {'<', '>'} 

162 The endianness of the data in the file. 

163 By default this is the system's native byte order. 

164 bigtiff : bool 

165 If True the BigTIFF format is used. 

166 By default the standard TIFF format is used for data less than 2040 MB. 

167 

168 Examples 

169 -------- 

170 >>> data = numpy.random.rand(10, 3, 301, 219) 

171 >>> imsave('temp.tif', data) 

172 

173 """ 

174 assert(photometric in (None, 'minisblack', 'miniswhite', 'rgb')) 

175 assert(planarconfig in (None, 'contig', 'planar')) 

176 assert(byteorder in (None, '<', '>')) 

177 

178 if byteorder is None: 

179 byteorder = '<' if sys.byteorder == 'little' else '>' 

180 

181 data = numpy.asarray(data, dtype=byteorder+data.dtype.char, order='C') 

182 data_shape = shape = data.shape 

183 data = numpy.atleast_2d(data) 

184 

185 if not bigtiff and data.size * data.dtype.itemsize < 2040*2**20: 

186 bigtiff = False 

187 offset_size = 4 

188 tag_size = 12 

189 numtag_format = 'H' 

190 offset_format = 'I' 

191 val_format = '4s' 

192 else: 

193 bigtiff = True 

194 offset_size = 8 

195 tag_size = 20 

196 numtag_format = 'Q' 

197 offset_format = 'Q' 

198 val_format = '8s' 

199 

200 # unify shape of data 

201 samplesperpixel = 1 

202 extrasamples = 0 

203 if photometric is None: 

204 if data.ndim > 2 and (shape[-3] in (3, 4) or shape[-1] in (3, 4)): 

205 photometric = 'rgb' 

206 else: 

207 photometric = 'minisblack' 

208 if photometric == 'rgb': 

209 if len(shape) < 3: 

210 raise ValueError("not a RGB(A) image") 

211 if planarconfig is None: 

212 planarconfig = 'planar' if shape[-3] in (3, 4) else 'contig' 

213 if planarconfig == 'contig': 

214 if shape[-1] not in (3, 4): 

215 raise ValueError("not a contiguous RGB(A) image") 

216 data = data.reshape((-1, 1) + shape[-3:]) 

217 samplesperpixel = shape[-1] 

218 else: 

219 if shape[-3] not in (3, 4): 

220 raise ValueError("not a planar RGB(A) image") 

221 data = data.reshape((-1, ) + shape[-3:] + (1, )) 

222 samplesperpixel = shape[-3] 

223 if samplesperpixel == 4: 

224 extrasamples = 1 

225 elif planarconfig and len(shape) > 2: 

226 if planarconfig == 'contig': 

227 data = data.reshape((-1, 1) + shape[-3:]) 

228 samplesperpixel = shape[-1] 

229 else: 

230 data = data.reshape((-1, ) + shape[-3:] + (1, )) 

231 samplesperpixel = shape[-3] 

232 extrasamples = samplesperpixel - 1 

233 else: 

234 planarconfig = None 

235 data = data.reshape((-1, 1) + shape[-2:] + (1, )) 

236 

237 shape = data.shape # (pages, planes, height, width, contig samples) 

238 

239 bytestr = bytes if sys.version[0] == '2' else lambda x: bytes(x, 'ascii') 

240 tifftypes = {'B': 1, 's': 2, 'H': 3, 'I': 4, '2I': 5, 'b': 6, 

241 'h': 8, 'i': 9, 'f': 11, 'd': 12, 'Q': 16, 'q': 17} 

242 tifftags = {'new_subfile_type': 254, 'subfile_type': 255, 

243 'image_width': 256, 'image_length': 257, 'bits_per_sample': 258, 

244 'compression': 259, 'photometric': 262, 'fill_order': 266, 

245 'document_name': 269, 'image_description': 270, 'strip_offsets': 273, 

246 'orientation': 274, 'samples_per_pixel': 277, 'rows_per_strip': 278, 

247 'strip_byte_counts': 279, 'x_resolution': 282, 'y_resolution': 283, 

248 'planar_configuration': 284, 'page_name': 285, 'resolution_unit': 296, 

249 'software': 305, 'datetime': 306, 'predictor': 317, 'color_map': 320, 

250 'extra_samples': 338, 'sample_format': 339} 

251 

252 tags = [] 

253 tag_data = [] 

254 

255 def pack(fmt, *val): 

256 return struct.pack(byteorder+fmt, *val) 

257 

258 def tag(name, dtype, number, value, offset=[0]): 

259 # append tag binary string to tags list 

260 # append (offset, value as binary string) to tag_data list 

261 # increment offset by tag_size 

262 if dtype == 's': 

263 value = bytestr(value) + b'\0' 

264 number = len(value) 

265 value = (value, ) 

266 t = [pack('HH', tifftags[name], tifftypes[dtype]), 

267 pack(offset_format, number)] 

268 if len(dtype) > 1: 

269 number *= int(dtype[:-1]) 

270 dtype = dtype[-1] 

271 if number == 1: 

272 if isinstance(value, (tuple, list)): 

273 value = value[0] 

274 t.append(pack(val_format, pack(dtype, value))) 

275 elif struct.calcsize(dtype) * number <= offset_size: 

276 t.append(pack(val_format, pack(str(number)+dtype, *value))) 

277 else: 

278 t.append(pack(offset_format, 0)) 

279 tag_data.append((offset[0] + offset_size + 4, 

280 pack(str(number)+dtype, *value))) 

281 tags.append(b''.join(t)) 

282 offset[0] += tag_size 

283 

284 if software: 

285 tag('software', 's', 0, software) 

286 if description: 

287 tag('image_description', 's', 0, description) 

288 elif shape != data_shape: 

289 tag('image_description', 's', 0, 

290 "shape=(%s)" % (",".join('%i' % i for i in data_shape))) 

291 tag('datetime', 's', 0, 

292 datetime.datetime.now().strftime("%Y:%m:%d %H:%M:%S")) 

293 # write previous tags only once 

294 writeonce = (len(tags), len(tag_data)) if shape[0] > 1 else None 

295 tag('compression', 'H', 1, 1) 

296 tag('orientation', 'H', 1, 1) 

297 tag('image_width', 'I', 1, shape[-2]) 

298 tag('image_length', 'I', 1, shape[-3]) 

299 tag('new_subfile_type', 'I', 1, 0 if shape[0] == 1 else 2) 

300 tag('sample_format', 'H', 1, 

301 {'u': 1, 'i': 2, 'f': 3, 'c': 6}[data.dtype.kind]) 

302 tag('photometric', 'H', 1, 

303 {'miniswhite': 0, 'minisblack': 1, 'rgb': 2}[photometric]) 

304 tag('samples_per_pixel', 'H', 1, samplesperpixel) 

305 if planarconfig: 

306 tag('planar_configuration', 'H', 1, 1 if planarconfig=='contig' else 2) 

307 tag('bits_per_sample', 'H', samplesperpixel, 

308 (data.dtype.itemsize * 8, ) * samplesperpixel) 

309 else: 

310 tag('bits_per_sample', 'H', 1, data.dtype.itemsize * 8) 

311 if extrasamples: 

312 if photometric == 'rgb': 

313 tag('extra_samples', 'H', 1, 1) # alpha channel 

314 else: 

315 tag('extra_samples', 'H', extrasamples, (0, ) * extrasamples) 

316 if resolution: 

317 tag('x_resolution', '2I', 1, resolution[0]) 

318 tag('y_resolution', '2I', 1, resolution[1]) 

319 tag('resolution_unit', 'H', 1, 2) 

320 tag('rows_per_strip', 'I', 1, shape[-3]) 

321 # use one strip per plane 

322 strip_byte_counts = (data[0, 0].size * data.dtype.itemsize, ) * shape[1] 

323 tag('strip_byte_counts', offset_format, shape[1], strip_byte_counts) 

324 # strip_offsets must be the last tag; will be updated later 

325 tag('strip_offsets', offset_format, shape[1], (0, ) * shape[1]) 

326 

327 fd = open(filename, 'wb') 

328 seek = fd.seek 

329 tell = fd.tell 

330 

331 def write(arg, *args): 

332 fd.write(pack(arg, *args) if args else arg) 

333 

334 write({'<': b'II', '>': b'MM'}[byteorder]) 

335 if bigtiff: 

336 write('HHH', 43, 8, 0) 

337 else: 

338 write('H', 42) 

339 ifd_offset = tell() 

340 write(offset_format, 0) # first IFD 

341 for i in range(shape[0]): 

342 # update pointer at ifd_offset 

343 pos = tell() 

344 seek(ifd_offset) 

345 write(offset_format, pos) 

346 seek(pos) 

347 # write tags 

348 write(numtag_format, len(tags)) 

349 tag_offset = tell() 

350 write(b''.join(tags)) 

351 ifd_offset = tell() 

352 write(offset_format, 0) # offset to next ifd 

353 # write extra tag data and update pointers 

354 for off, dat in tag_data: 

355 pos = tell() 

356 seek(tag_offset + off) 

357 write(offset_format, pos) 

358 seek(pos) 

359 write(dat) 

360 # update strip_offsets 

361 pos = tell() 

362 if len(strip_byte_counts) == 1: 

363 seek(ifd_offset - offset_size) 

364 write(offset_format, pos) 

365 else: 

366 seek(pos - offset_size*shape[1]) 

367 strip_offset = pos 

368 for size in strip_byte_counts: 

369 write(offset_format, strip_offset) 

370 strip_offset += size 

371 seek(pos) 

372 # write data 

373 data[i].tofile(fd) # if this fails, try update Python and numpy 

374 fd.flush() 

375 # remove tags that should be written only once 

376 if writeonce: 

377 tags = tags[writeonce[0]:] 

378 d = writeonce[0] * tag_size 

379 tag_data = [(o-d, v) for (o, v) in tag_data[writeonce[1]:]] 

380 writeonce = None 

381 fd.close() 

382 

383 

384def imread(filename, *args, **kwargs): 

385 """Return image data from TIFF file as numpy array. 

386 

387 The first image series is returned if no arguments are provided. 

388 

389 Parameters 

390 ---------- 

391 key : int, slice, or sequence of page indices 

392 Defines which pages to return as array. 

393 series : int 

394 Defines which series of pages to return as array. 

395 

396 Examples 

397 -------- 

398 >>> image = imread('test.tif', 0) 

399 

400 """ 

401# with TIFFfile(filename) as tif: 

402# return tif.asarray(*args, **kwargs) 

403 tif = TIFFfile(filename) 

404 images = tif.asarray() 

405 tif.close() 

406 return images 

407 

408def tiff_exposure_time(filename): 

409 return return_tiff_tag(filename,'FrameTime') 

410 

411def tiff_frames(filename): 

412 return return_tiff_tag(filename,'NFrames') 

413 

414def return_tiff_tag(filename,key): 

415 

416 tif = TIFFfile(filename) 

417 tagvalue = None 

418 for page in tif: 

419 for tag in page.tags.values(): 

420 if key in tag.name: 

421 tagvalue = tag.value 

422 elif key in str(tag.value): 

423 tagvalue = tag.value.split(':',1)[-1] 

424 try: 

425 tagvalue = float(tagvalue) 

426 except: 

427 pass 

428 

429 tif.close() 

430 return tagvalue 

431 

432 

433 

434class lazyattr(object): 

435 """Lazy object attribute whose value is computed on first access.""" 

436 def __init__(self, func): 

437 self.func = func 

438 

439 def __get__(self, instance, owner): 

440 if instance is None: 

441 return self 

442 result = self.func(instance) 

443 if result is NotImplemented: 

444 return getattr(super(owner, instance), self.func.__name__) 

445 setattr(instance, self.func.__name__, result) 

446 return result 

447 

448 

449class TIFFfile(object): 

450 """Read image and meta-data from TIFF, STK, LSM, and FluoView files. 

451 

452 TIFFfile instances must be closed using the close method. 

453 

454 Attributes 

455 ---------- 

456 pages : list 

457 All TIFFpages in file. 

458 series : list of Records(shape, dtype, axes, TIFFpages) 

459 TIFF pages with compatible shapes and types. 

460 

461 All attributes are read-only. 

462 

463 Examples 

464 -------- 

465 >>> tif = TIFFfile('test.tif') 

466 ... try: 

467 ... images = tif.asarray() 

468 ... except Exception as e: 

469 ... print(e) 

470 ... finally: 

471 ... tif.close() 

472 

473 """ 

474 def __init__(self, filename): 

475 """Initialize instance from file.""" 

476 filename = os.path.abspath(filename) 

477 self._fd = open(filename, 'rb') 

478 self.fname = os.path.basename(filename) 

479 self.fpath = os.path.dirname(filename) 

480 self._tiffs = {self.fname: self} # cache of TIFFfiles 

481 self.offset_size = None 

482 self.pages = [] 

483 try: 

484 self._fromfile() 

485 except Exception: 

486 self._fd.close() 

487 raise 

488 

489 def close(self): 

490 """Close open file handle(s).""" 

491# if not hasattr(self, 'tiffs'): 

492# return 

493 for tif in self._tiffs.values(): 

494 if tif._fd: 

495 tif._fd.close() 

496 tif._fd = None 

497 

498 def _fromfile(self): 

499 """Read TIFF header and all page records from file.""" 

500 self._fd.seek(0) 

501 try: 

502 self.byte_order = {b'II': '<', b'MM': '>'}[self._fd.read(2)] 

503 except KeyError: 

504 raise ValueError("not a valid TIFF file") 

505 version = struct.unpack(self.byte_order+'H', self._fd.read(2))[0] 

506 if version == 43: # BigTiff 

507 self.offset_size, zero = struct.unpack(self.byte_order+'HH', 

508 self._fd.read(4)) 

509 if zero or self.offset_size != 8: 

510 raise ValueError("not a valid BigTIFF file") 

511 elif version == 42: 

512 self.offset_size = 4 

513 else: 

514 raise ValueError("not a TIFF file") 

515 self.pages = [] 

516 while True: 

517 try: 

518 page = TIFFpage(self) 

519 self.pages.append(page) 

520 except StopIteration: 

521 break 

522 if not self.pages: 

523 raise ValueError("empty TIFF file") 

524 

525 @lazyattr 

526 def series(self): 

527 """Return series of TIFFpage with compatible shape and properties.""" 

528 if self.is_ome: 

529 series = self._omeseries() 

530 elif self.is_fluoview: 

531 dims = {b'X': 'X', b'Y': 'Y', b'Z': 'Z', b'T': 'T', 

532 b'WAVELENGTH': 'C', b'TIME': 'T', b'XY': 'R', 

533 b'EVENT': 'V', b'EXPOSURE': 'L'} 

534 mmhd = list(reversed(self.pages[0].mm_header.dimensions)) 

535 series = [Record( 

536 axes=''.join(dims.get(i[0].strip().upper(), 'O') 

537 for i in mmhd if i[1] > 1), 

538 shape=tuple(int(i[1]) for i in mmhd if i[1] > 1), 

539 pages=self.pages, dtype=numpy.dtype(self.pages[0].dtype))] 

540 elif self.is_lsm: 

541 lsmi = self.pages[0].cz_lsm_info 

542 axes = CZ_SCAN_TYPES[lsmi.scan_type] 

543 if self.pages[0].is_rgb: 

544 axes = axes.replace('C', '').replace('XY', 'XYC') 

545 axes = axes[::-1] 

546 shape = [getattr(lsmi, CZ_DIMENSIONS[i]) for i in axes] 

547 pages = [p for p in self.pages if not p.is_reduced] 

548 series = [Record(axes=axes, shape=shape, pages=pages, 

549 dtype=numpy.dtype(pages[0].dtype))] 

550 if len(pages) != len(self.pages): # reduced RGB pages 

551 pages = [p for p in self.pages if p.is_reduced] 

552 cp = 1 

553 i = 0 

554 while cp < len(pages) and i < len(shape)-2: 

555 cp *= shape[i] 

556 i += 1 

557 shape = shape[:i] + list(pages[0].shape) 

558 axes = axes[:i] + 'CYX' 

559 series.append(Record(axes=axes, shape=shape, pages=pages, 

560 dtype=numpy.dtype(pages[0].dtype))) 

561 elif self.is_nih: 

562 series = [Record(pages=self.pages, 

563 shape=(len(self.pages),) + self.pages[0].shape, 

564 axes='I' + self.pages[0].axes, 

565 dtype=numpy.dtype(self.pages[0].dtype))] 

566 elif self.pages[0].is_shaped: 

567 shape = self.pages[0].tags['image_description'].value[7:-1] 

568 shape = tuple(int(i) for i in shape.split(b',')) 

569 series = [Record(pages=self.pages, shape=shape, 

570 axes='O' * len(shape), 

571 dtype=numpy.dtype(self.pages[0].dtype))] 

572 else: 

573 shapes = [] 

574 pages = {} 

575 for page in self.pages: 

576 shape = page.shape + (page.axes, 

577 page.compression in TIFF_DECOMPESSORS) 

578 if not shape in pages: 

579 shapes.append(shape) 

580 pages[shape] = [page] 

581 else: 

582 pages[shape].append(page) 

583 series = [Record(pages=pages[s], 

584 axes=(('I' + s[-2]) 

585 if len(pages[s]) > 1 else s[-2]), 

586 dtype=numpy.dtype(pages[s][0].dtype), 

587 shape=((len(pages[s]), ) + s[:-2] 

588 if len(pages[s]) > 1 else s[:-2])) 

589 for s in shapes] 

590 return series 

591 

592 def asarray(self, key=None, series=None): 

593 """Return image data of multiple TIFF pages as numpy array. 

594 

595 By default the first image series is returned. 

596 

597 Parameters 

598 ---------- 

599 key : int, slice, or sequence of page indices 

600 Defines which pages to return as array. 

601 series : int 

602 Defines which series of pages to return as array. 

603 

604 """ 

605 if key is None and series is None: 

606 series = 0 

607 if series is not None: 

608 pages = self.series[series].pages 

609 else: 

610 pages = self.pages 

611 

612 if key is None: 

613 pass 

614 elif isinstance(key, int): 

615 pages = [pages[key]] 

616 elif isinstance(key, slice): 

617 pages = pages[key] 

618 elif isinstance(key, collections.Iterable): 

619 pages = [pages[k] for k in key] 

620 else: 

621 raise TypeError('key must be an int, slice, or sequence') 

622 

623 if len(pages) == 1: 

624 return pages[0].asarray() 

625 elif self.is_nih: 

626 result = numpy.vstack(p.asarray(colormapped=False, 

627 squeeze=False) for p in pages) 

628 if pages[0].is_palette: 

629 result = numpy.take(pages[0].color_map, result, axis=1) 

630 result = numpy.swapaxes(result, 0, 1) 

631 else: 

632 if self.is_ome and any(p is None for p in pages): 

633 firstpage = next(p for p in pages if p) 

634 nopage = numpy.zeros_like( 

635 firstpage.asarray()) 

636 result = numpy.vstack((p.asarray() if p else nopage) 

637 for p in pages) 

638 if key is None: 

639 try: 

640 result.shape = self.series[series].shape 

641 except ValueError: 

642 warnings.warn("failed to reshape %s to %s" % ( 

643 result.shape, self.series[series].shape)) 

644 result.shape = (-1,) + pages[0].shape 

645 else: 

646 result.shape = (-1,) + pages[0].shape 

647 return result 

648 

649 def _omeseries(self): 

650 """Return image series in OME-TIFF files.""" 

651 root = ElementTree.XML(self.pages[0].tags['image_description'].value) 

652 uuid = root.attrib.get('UUID', None) 

653 self._tiffs = {uuid: self} 

654 modulo = {} 

655 result = [] 

656 for element in root: 

657 if element.tag.endswith('BinaryOnly'): 

658 warnings.warn("not an OME-TIFF master file") 

659 break 

660 if element.tag.endswith('StructuredAnnotations'): 

661 for annot in element: 

662 if not annot.attrib.get('Namespace', 

663 '').endswith('modulo'): 

664 continue 

665 for value in annot: 

666 for modul in value: 

667 for along in modul: 

668 if not along.tag[:-1].endswith('Along'): 

669 continue 

670 axis = along.tag[-1] 

671 newaxis = along.attrib.get('Type', 'other') 

672 newaxis = AXES_LABELS[newaxis] 

673 if 'Start' in along.attrib: 

674 labels = range( 

675 int(along.attrib['Start']), 

676 int(along.attrib['End']) + 1, 

677 int(along.attrib.get('Step', 1))) 

678 else: 

679 labels = [label.text for label in along 

680 if label.tag.endswith('Label')] 

681 modulo[axis] = (newaxis, labels) 

682 if not element.tag.endswith('Image'): 

683 continue 

684 for pixels in element: 

685 if not pixels.tag.endswith('Pixels'): 

686 continue 

687 atr = pixels.attrib 

688 axes = "".join(reversed(atr['DimensionOrder'])) 

689 shape = list(int(atr['Size'+ax]) for ax in axes) 

690 size = numpy.prod(shape[:-2]) 

691 ifds = [None] * size 

692 for data in pixels: 

693 if not data.tag.endswith('TiffData'): 

694 continue 

695 atr = data.attrib 

696 ifd = int(atr.get('IFD', 0)) 

697 num = int(atr.get('NumPlanes', 1 if 'IFD' in atr else 0)) 

698 num = int(atr.get('PlaneCount', num)) 

699 idx = [int(atr.get('First'+ax, 0)) for ax in axes[:-2]] 

700 idx = numpy.ravel_multi_index(idx, shape[:-2]) 

701 for uuid in data: 

702 if uuid.tag.endswith('UUID'): 

703 if uuid.text not in self._tiffs: 

704 fn = uuid.attrib['FileName'] 

705 try: 

706 tf = TIFFfile(os.path.join(self.fpath, fn)) 

707 except (IOError, ValueError): 

708 warnings.warn("failed to read %s" % fn) 

709 break 

710 self._tiffs[uuid.text] = tf 

711 pages = self._tiffs[uuid.text].pages 

712 try: 

713 for i in range(num if num else len(pages)): 

714 ifds[idx + i] = pages[ifd + i] 

715 except IndexError: 

716 warnings.warn("ome-xml: index out of range") 

717 break 

718 else: 

719 pages = self.pages 

720 try: 

721 for i in range(num if num else len(pages)): 

722 ifds[idx + i] = pages[ifd + i] 

723 except IndexError: 

724 warnings.warn("ome-xml: index out of range") 

725 result.append(Record(axes=axes, shape=shape, pages=ifds, 

726 dtype=numpy.dtype(ifds[0].dtype))) 

727 

728 for record in result: 

729 for axis, (newaxis, labels) in modulo.items(): 

730 i = record.axes.index(axis) 

731 size = len(labels) 

732 if record.shape[i] == size: 

733 record.axes = record.axes.replace(axis, newaxis, 1) 

734 else: 

735 record.shape[i] //= size 

736 record.shape.insert(i+1, size) 

737 record.axes = record.axes.replace(axis, axis+newaxis, 1) 

738 

739 return result 

740 

741 def __len__(self): 

742 """Return number of image pages in file.""" 

743 return len(self.pages) 

744 

745 def __getitem__(self, key): 

746 """Return specified page.""" 

747 return self.pages[key] 

748 

749 def __iter__(self): 

750 """Return iterator over pages.""" 

751 return iter(self.pages) 

752 

753 def __str__(self): 

754 """Return string containing information about file.""" 

755 result = [self.fname.capitalize(), 

756 "%.2f MB" % (self.fstat[6] / 1048576), 

757 {'<': 'little endian', '>': 'big endian'}[self.byte_order]] 

758 if self.is_bigtiff: 

759 result.append("bigtiff") 

760 if len(self.pages) > 1: 

761 result.append("%i pages" % len(self.pages)) 

762 if len(self.series) > 1: 

763 result.append("%i series" % len(self.series)) 

764 if len(self._tiffs) > 1: 

765 result.append("%i files" % (len(self._tiffs))) 

766 return ", ".join(result) 

767 

768 def __enter__(self): 

769 return self 

770 

771 def __exit__(self, *args): 

772 self.close() 

773 

774 @lazyattr 

775 def fstat(self): 

776 return os.fstat(self._fd.fileno()) 

777 

778 @lazyattr 

779 def is_bigtiff(self): 

780 return self.offset_size != 4 

781 

782 @lazyattr 

783 def is_rgb(self): 

784 return all(p.is_rgb for p in self.pages) 

785 

786 @lazyattr 

787 def is_palette(self): 

788 return all(p.is_palette for p in self.pages) 

789 

790 @lazyattr 

791 def is_stk(self): 

792 return all(p.is_stk for p in self.pages) 

793 

794 @lazyattr 

795 def is_lsm(self): 

796 return self.pages[0].is_lsm 

797 

798 @lazyattr 

799 def is_nih(self): 

800 return self.pages[0].is_nih 

801 

802 @lazyattr 

803 def is_fluoview(self): 

804 return self.pages[0].is_fluoview 

805 

806 @lazyattr 

807 def is_ome(self): 

808 return self.pages[0].is_ome 

809 

810 

811class TIFFpage(object): 

812 """A TIFF image file directory (IFD). 

813 

814 Attributes 

815 ---------- 

816 index : int 

817 Index of page in file. 

818 dtype : str {TIFF_SAMPLE_DTYPES} 

819 Data type of image, colormapped if applicable. 

820 shape : tuple 

821 Dimensions of the image array in TIFF page, 

822 colormapped and with one alpha channel if applicable. 

823 axes : str 

824 Axes label codes: 

825 'X' width, 'Y' height, 'S' sample, 'P' plane, 'I' image series, 

826 'Z' depth, 'C' color|em-wavelength|channel, 'E' ex-wavelength|lambda, 

827 'T' time, 'R' region|tile, 'A' angle, 'F' phase, 'H' lifetime, 

828 'L' exposure, 'V' event, 'O' unknown, '_' missing 

829 tags : TiffTags 

830 Dictionary of tags in page. 

831 Tag values are also directly accessible as attributes. 

832 color_map : numpy array 

833 Color look up table if exists. 

834 mm_uic_tags: Record(dict) 

835 Consolidated MetaMorph mm_uic# tags, if exists. 

836 cz_lsm_scan_info: Record(dict) 

837 LSM scan info attributes, if exists. 

838 

839 All attributes are read-only. 

840 

841 """ 

842 def __init__(self, parent): 

843 """Initialize instance from file.""" 

844 self.parent = parent 

845 self.index = len(parent.pages) 

846 self.shape = self._shape = () 

847 self.dtype = self._dtype = None 

848 self.axes = "" 

849 self.tags = TiffTags() 

850 

851 self._fromfile() 

852 self._process_tags() 

853 

854 def _fromfile(self): 

855 """Read TIFF IFD structure and its tags from file. 

856 

857 File cursor must be at storage position of IFD offset and is left at 

858 offset to next IFD. 

859 

860 Raises StopIteration if offset (first bytes read) is 0. 

861 

862 """ 

863 fd = self.parent._fd 

864 byte_order = self.parent.byte_order 

865 offset_size = self.parent.offset_size 

866 

867 fmt = {4: 'I', 8: 'Q'}[offset_size] 

868 offset = struct.unpack(byte_order + fmt, fd.read(offset_size))[0] 

869 if not offset: 

870 raise StopIteration() 

871 

872 # read standard tags 

873 tags = self.tags 

874 fd.seek(offset) 

875 fmt, size = {4: ('H', 2), 8: ('Q', 8)}[offset_size] 

876 try: 

877 numtags = struct.unpack(byte_order + fmt, fd.read(size))[0] 

878 except Exception: 

879 warnings.warn("corrupted page list") 

880 raise StopIteration() 

881 

882 for _ in range(numtags): 

883 tag = TIFFtag(self.parent) 

884 tags[tag.name] = tag 

885 

886 # read LSM info subrecords 

887 if self.is_lsm: 

888 pos = fd.tell() 

889 for name, reader in CZ_LSM_INFO_READERS.items(): 

890 try: 

891 offset = self.cz_lsm_info["offset_"+name] 

892 except KeyError: 

893 continue 

894 if not offset: 

895 continue 

896 fd.seek(offset) 

897 try: 

898 setattr(self, "cz_lsm_"+name, reader(fd, byte_order)) 

899 except ValueError: 

900 pass 

901 fd.seek(pos) 

902 

903 def _process_tags(self): 

904 """Validate standard tags and initialize attributes. 

905 

906 Raise ValueError if tag values are not supported. 

907 

908 """ 

909 tags = self.tags 

910 for code, (name, default, dtype, count, validate) in TIFF_TAGS.items(): 

911 if not (name in tags or default is None): 

912 tags[name] = TIFFtag(code, dtype=dtype, count=count, 

913 value=default, name=name) 

914 if name in tags and validate: 

915 try: 

916 if tags[name].count == 1: 

917 setattr(self, name, validate[tags[name].value]) 

918 else: 

919 setattr(self, name, tuple(validate[value] 

920 for value in tags[name].value)) 

921 except KeyError: 

922 raise ValueError("%s.value (%s) not supported" % 

923 (name, tags[name].value)) 

924 

925 tag = tags['bits_per_sample'] 

926 if tag.count == 1: 

927 self.bits_per_sample = tag.value 

928 else: 

929 value = tag.value[:self.samples_per_pixel] 

930 if any((v-value[0] for v in value)): 

931 self.bits_per_sample = value 

932 else: 

933 self.bits_per_sample = value[0] 

934 

935 tag = tags['sample_format'] 

936 if tag.count == 1: 

937 self.sample_format = TIFF_SAMPLE_FORMATS[tag.value] 

938 else: 

939 value = tag.value[:self.samples_per_pixel] 

940 if any((v-value[0] for v in value)): 

941 self.sample_format = [TIFF_SAMPLE_FORMATS[v] for v in value] 

942 else: 

943 self.sample_format = TIFF_SAMPLE_FORMATS[value[0]] 

944 

945 self.strips_per_image = int(math.floor(float(self.image_length + 

946 self.rows_per_strip - 1) / self.rows_per_strip)) 

947 

948 key = (self.sample_format, self.bits_per_sample) 

949 self.dtype = self._dtype = TIFF_SAMPLE_DTYPES.get(key, None) 

950 

951 if self.is_stk: 

952 planes = tags['mm_uic2'].count 

953 # consolidate mm_uci tags 

954 self.mm_uic_tags = Record(tags['mm_uic2'].value) 

955 for key in ('mm_uic3', 'mm_uic4', 'mm_uic1'): 

956 if key in tags: 

957 self.mm_uic_tags.update(tags[key].value) 

958 if self.planar_configuration == 'contig': 

959 self._shape = (planes, 1, self.image_length, 

960 self.image_width, self.samples_per_pixel) 

961 self.shape = tuple(self._shape[i] for i in (0, 2, 3, 4)) 

962 self.axes = "PYXS" 

963 else: 

964 self._shape = (planes, self.samples_per_pixel, 

965 self.image_length, self.image_width, 1) 

966 self.shape = self._shape[:4] 

967 self.axes = "PSYX" 

968 elif self.is_palette: 

969 self.dtype = self.tags['color_map'].dtype[1] 

970 self.color_map = numpy.array(self.color_map, self.dtype) 

971 dmax = self.color_map.max() 

972 if dmax < 256: 

973 self.dtype = numpy.uint8 

974 self.color_map = self.color_map.astype(self.dtype) 

975 #else: 

976 # self.dtype = numpy.uint8 

977 # self.color_map >>= 8 

978 # self.color_map = self.color_map.astype(self.dtype) 

979 self.color_map.shape = (3, -1) 

980 self._shape = (1, 1, self.image_length, self.image_width, 1) 

981 if self.color_map.shape[1] >= 2**self.bits_per_sample: 

982 self.shape = (3, self.image_length, self.image_width) 

983 self.axes = "SYX" 

984 else: 

985 # LSM and FluoView 

986 self.shape = (self.image_length, self.image_width) 

987 self.axes = "YX" 

988 elif self.is_rgb or self.samples_per_pixel > 1: 

989 if self.planar_configuration == 'contig': 

990 self._shape = (1, 1, self.image_length, self.image_width, 

991 self.samples_per_pixel) 

992 self.shape = (self.image_length, self.image_width, 

993 self.samples_per_pixel) 

994 self.axes = "YXS" 

995 else: 

996 self._shape = (1, self.samples_per_pixel, self.image_length, 

997 self.image_width, 1) 

998 self.shape = self._shape[1:-1] 

999 self.axes = "SYX" 

1000 if self.is_rgb and 'extra_samples' in self.tags: 

1001 extra_samples = self.extra_samples 

1002 if self.tags['extra_samples'].count == 1: 

1003 extra_samples = (extra_samples, ) 

1004 for exs in extra_samples: 

1005 if exs in ('unassalpha', 'assocalpha'): 

1006 if self.planar_configuration == 'contig': 

1007 self.shape = self.shape[:2] + (4,) 

1008 else: 

1009 self.shape = (4,) + self.shape[1:] 

1010 break 

1011 else: 

1012 self._shape = (1, 1, self.image_length, self.image_width, 1) 

1013 self.shape = self._shape[2:4] 

1014 self.axes = "YX" 

1015 

1016 if not self.compression and not 'strip_byte_counts' in tags: 

1017 self.strip_byte_counts = numpy.prod(self.shape) * ( 

1018 self.bits_per_sample // 8) 

1019 

1020 def asarray(self, squeeze=True, colormapped=True, rgbonly=True): 

1021 """Read image data from file and return as numpy array. 

1022 

1023 Raise ValueError if format is unsupported. 

1024 If any argument is False, the shape of the returned array might be 

1025 different from the page shape. 

1026 

1027 Parameters 

1028 ---------- 

1029 squeeze : bool 

1030 If True all length-1 dimensions (except X and Y) are 

1031 squeezed out from result. 

1032 colormapped : bool 

1033 If True color mapping is applied for palette-indexed images. 

1034 rgbonly : bool 

1035 If True return RGB(A) image without additional extra samples. 

1036 

1037 """ 

1038 fd = self.parent._fd 

1039 if not fd: 

1040 raise IOError("TIFF file is not open") 

1041 if self.dtype is None: 

1042 raise ValueError("data type not supported: %s%i" % ( 

1043 self.sample_format, self.bits_per_sample)) 

1044 if self.compression not in TIFF_DECOMPESSORS: 

1045 raise ValueError("cannot decompress %s" % self.compression) 

1046 if ('ycbcr_subsampling' in self.tags and 

1047 self.tags['ycbcr_subsampling'].value not in (1, (1, 1))): 

1048 raise ValueError("YCbCr subsampling not supported") 

1049 tag = self.tags['sample_format'] 

1050 if tag.count != 1 and any((i-tag.value[0] for i in tag.value)): 

1051 raise ValueError("sample formats don't match %s" % str(tag.value)) 

1052 

1053 dtype = self._dtype 

1054 shape = self._shape 

1055 image_width = self.image_width 

1056 image_length = self.image_length 

1057 typecode = self.parent.byte_order + dtype 

1058 bits_per_sample = self.bits_per_sample 

1059 

1060 if self.is_tiled: 

1061 if 'tile_offsets' in self.tags: 

1062 byte_counts = self.tile_byte_counts 

1063 offsets = self.tile_offsets 

1064 else: 

1065 byte_counts = self.strip_byte_counts 

1066 offsets = self.strip_offsets 

1067 tile_width = self.tile_width 

1068 tile_length = self.tile_length 

1069 tw = (image_width + tile_width - 1) // tile_width 

1070 tl = (image_length + tile_length - 1) // tile_length 

1071 shape = shape[:-3] + (tl*tile_length, tw*tile_width, shape[-1]) 

1072 tile_shape = (tile_length, tile_width, shape[-1]) 

1073 runlen = tile_width 

1074 else: 

1075 byte_counts = self.strip_byte_counts 

1076 offsets = self.strip_offsets 

1077 runlen = image_width 

1078 

1079 try: 

1080 offsets[0] 

1081 except TypeError: 

1082 offsets = (offsets, ) 

1083 byte_counts = (byte_counts, ) 

1084 if any(o < 2 for o in offsets): 

1085 raise ValueError("corrupted file") 

1086 

1087 if (not self.is_tiled and (self.is_stk or (not self.compression 

1088 and bits_per_sample in (8, 16, 32, 64) 

1089 and all(offsets[i] == offsets[i+1] - byte_counts[i] 

1090 for i in range(len(offsets)-1))))): 

1091 # contiguous data 

1092 fd.seek(offsets[0]) 

1093 result = numpy.fromfile(fd, typecode, numpy.prod(shape)) 

1094 result = result.astype('=' + dtype) 

1095 else: 

1096 if self.planar_configuration == 'contig': 

1097 runlen *= self.samples_per_pixel 

1098 if bits_per_sample in (8, 16, 32, 64, 128): 

1099 if (bits_per_sample * runlen) % 8: 

1100 raise ValueError("data and sample size mismatch") 

1101 unpack = lambda x: numpy.fromstring(x, typecode) 

1102 elif isinstance(bits_per_sample, tuple): 

1103 unpack = lambda x: unpackrgb(x, typecode, bits_per_sample) 

1104 else: 

1105 unpack = lambda x: unpackints(x, typecode, bits_per_sample, 

1106 runlen) 

1107 decompress = TIFF_DECOMPESSORS[self.compression] 

1108 if self.is_tiled: 

1109 result = numpy.empty(shape, dtype) 

1110 tw, tl, pl = 0, 0, 0 

1111 for offset, bytecount in zip(offsets, byte_counts): 

1112 fd.seek(offset) 

1113 tile = unpack(decompress(fd.read(bytecount))) 

1114 tile.shape = tile_shape 

1115 result[0, pl, tl:tl+tile_length, 

1116 tw:tw+tile_width, :] = tile 

1117 del tile 

1118 tw += tile_width 

1119 if tw >= shape[-2]: 

1120 tw, tl = 0, tl + tile_length 

1121 if tl >= shape[-3]: 

1122 tl, pl = 0, pl + 1 

1123 result = result[..., :image_length, :image_width, :] 

1124 else: 

1125 result = numpy.empty(shape, dtype).reshape(-1) 

1126 index = 0 

1127 for offset, bytecount in zip(offsets, byte_counts): 

1128 fd.seek(offset) 

1129 stripe = unpack(decompress(fd.read(bytecount))) 

1130 size = min(result.size, stripe.size) 

1131 result[index:index+size] = stripe[:size] 

1132 del stripe 

1133 index += size 

1134 

1135 result.shape = self._shape 

1136 

1137 if self.predictor == 'horizontal': 

1138 # workaround bug in LSM510 software 

1139 if not (self.parent.is_lsm and not self.compression): 

1140 numpy.cumsum(result, axis=3, dtype=dtype, out=result) 

1141 

1142 if colormapped and self.is_palette: 

1143 if self.color_map.shape[1] >= 2**bits_per_sample: 

1144 # FluoView and LSM might fail here 

1145 result = numpy.take(self.color_map, result, axis=1) 

1146 elif rgbonly and self.is_rgb and 'extra_samples' in self.tags: 

1147 # return only RGB and first alpha channel if exists 

1148 extra_samples = self.extra_samples 

1149 if self.tags['extra_samples'].count == 1: 

1150 extra_samples = (extra_samples, ) 

1151 for i, exs in enumerate(extra_samples): 

1152 if exs in ('unassalpha', 'assocalpha'): 

1153 if self.planar_configuration == 'contig': 

1154 result = result[..., [0, 1, 2, 3+i]] 

1155 else: 

1156 result = result[:, [0, 1, 2, 3+i]] 

1157 break 

1158 else: 

1159 if self.planar_configuration == 'contig': 

1160 result = result[..., :3] 

1161 else: 

1162 result = result[:, :3] 

1163 

1164 if squeeze: 

1165 try: 

1166 result.shape = self.shape 

1167 except ValueError: 

1168 pass 

1169 

1170 return result 

1171 

1172 def __str__(self): 

1173 """Return string containing information about page.""" 

1174 s = ', '.join(s for s in ( 

1175 ' x '.join(str(i) for i in self.shape), 

1176 str(numpy.dtype(self.dtype)), 

1177 '%s bit' % str(self.bits_per_sample), 

1178 self.photometric, 

1179 self.compression if self.compression else 'raw', 

1180 ','.join(t[3:] for t in ('is_stk', 'is_lsm', 'is_nih', 'is_ome', 

1181 'is_fluoview', 'is_reduced', 'is_tiled') 

1182 if getattr(self, t))) if s) 

1183 return "Page %i: %s" % (self.index, s) 

1184 

1185 def __getattr__(self, name): 

1186 """Return tag value.""" 

1187 if name in self.tags: 

1188 value = self.tags[name].value 

1189 setattr(self, name, value) 

1190 return value 

1191 raise AttributeError(name) 

1192 

1193 @lazyattr 

1194 def is_rgb(self): 

1195 """True if page contains a RGB image.""" 

1196 return self.tags['photometric'].value == 2 

1197 

1198 @lazyattr 

1199 def is_palette(self): 

1200 """True if page contains a palette-colored image.""" 

1201 return self.tags['photometric'].value == 3 

1202 

1203 @lazyattr 

1204 def is_tiled(self): 

1205 """True if page contains tiled image.""" 

1206 return 'tile_width' in self.tags 

1207 

1208 @lazyattr 

1209 def is_reduced(self): 

1210 """True if page is a reduced image of another image.""" 

1211 return bool(self.tags['new_subfile_type'].value & 1) 

1212 

1213 @lazyattr 

1214 def is_stk(self): 

1215 """True if page contains MM_UIC2 tag.""" 

1216 return 'mm_uic2' in self.tags 

1217 

1218 @lazyattr 

1219 def is_lsm(self): 

1220 """True if page contains LSM CZ_LSM_INFO tag.""" 

1221 return 'cz_lsm_info' in self.tags 

1222 

1223 @lazyattr 

1224 def is_fluoview(self): 

1225 """True if page contains FluoView MM_STAMP tag.""" 

1226 return 'mm_stamp' in self.tags 

1227 

1228 @lazyattr 

1229 def is_nih(self): 

1230 """True if page contains NIH image header.""" 

1231 return 'nih_image_header' in self.tags 

1232 

1233 @lazyattr 

1234 def is_ome(self): 

1235 """True if page contains OME-XML in image_description tag.""" 

1236 return ('image_description' in self.tags and self.tags[ 

1237 'image_description'].value.startswith(b'<?xml version=')) 

1238 

1239 @lazyattr 

1240 def is_shaped(self): 

1241 """True if page contains shape in image_description tag.""" 

1242 return ('image_description' in self.tags and self.tags[ 

1243 'image_description'].value.startswith(b'shape=(')) 

1244 

1245 

1246class TIFFtag(object): 

1247 """A TIFF tag structure. 

1248 

1249 Attributes 

1250 ---------- 

1251 name : string 

1252 Attribute name of tag. 

1253 code : int 

1254 Decimal code of tag. 

1255 dtype : str 

1256 Datatype of tag data. One of TIFF_DATA_TYPES. 

1257 count : int 

1258 Number of values. 

1259 value : various types 

1260 Tag data. For codes in CUSTOM_TAGS the 4 bytes file content. 

1261 value_offset : int 

1262 Location of value in file 

1263 

1264 All attributes are read-only. 

1265 

1266 """ 

1267 __slots__ = ('code', 'name', 'count', 'dtype', 'value', 'value_offset', 

1268 '_offset') 

1269 

1270 def __init__(self, arg, **kwargs): 

1271 """Initialize instance from file or arguments.""" 

1272 self._offset = None 

1273 if hasattr(arg, '_fd'): 

1274 self._fromfile(arg, **kwargs) 

1275 else: 

1276 self._fromdata(arg, **kwargs) 

1277 

1278 def _fromdata(self, code, dtype, count, value, name=None): 

1279 """Initialize instance from arguments.""" 

1280 self.code = int(code) 

1281 self.name = name if name else str(code) 

1282 self.dtype = TIFF_DATA_TYPES[dtype] 

1283 self.count = int(count) 

1284 self.value = value 

1285 

1286 def _fromfile(self, parent): 

1287 """Read tag structure from open file. Advance file cursor.""" 

1288 fd = parent._fd 

1289 byte_order = parent.byte_order 

1290 

1291 self._offset = fd.tell() 

1292 self.value_offset = self._offset + parent.offset_size + 4 

1293 

1294 fmt, size = {4: ('HHI4s', 12), 8: ('HHQ8s', 20)}[parent.offset_size] 

1295 data = fd.read(size) 

1296 code, dtype = struct.unpack(byte_order + fmt[:2], data[:4]) 

1297 count, value = struct.unpack(byte_order + fmt[2:], data[4:]) 

1298 

1299 if code in TIFF_TAGS: 

1300 name = TIFF_TAGS[code][0] 

1301 elif code in CUSTOM_TAGS: 

1302 name = CUSTOM_TAGS[code][0] 

1303 else: 

1304 name = str(code) 

1305 

1306 try: 

1307 dtype = TIFF_DATA_TYPES[dtype] 

1308 except KeyError: 

1309 raise ValueError("unknown TIFF tag data type %i" % dtype) 

1310 

1311 fmt = '%s%i%s' % (byte_order, count*int(dtype[0]), dtype[1]) 

1312 size = struct.calcsize(fmt) 

1313 if size > parent.offset_size or code in CUSTOM_TAGS: 

1314 pos = fd.tell() 

1315 tof = {4: 'I', 8: 'Q'}[parent.offset_size] 

1316 self.value_offset = struct.unpack(byte_order+tof, value)[0] 

1317 fd.seek(self.value_offset) 

1318 if code in CUSTOM_TAGS: 

1319 readfunc = CUSTOM_TAGS[code][1] 

1320 value = readfunc(fd, byte_order, dtype, count) 

1321 fd.seek(0, 2) # bug in numpy/Python 3.x ? 

1322 if isinstance(value, dict): # numpy.core.records.record 

1323 value = Record(value) 

1324 elif code in TIFF_TAGS or dtype[-1] == 's': 

1325 value = struct.unpack(fmt, fd.read(size)) 

1326 else: 

1327 value = read_numpy(fd, byte_order, dtype, count) 

1328 fd.seek(0, 2) # bug in numpy/Python 3.x ? 

1329 fd.seek(pos) 

1330 else: 

1331 value = struct.unpack(fmt, value[:size]) 

1332 

1333 if not code in CUSTOM_TAGS: 

1334 if len(value) == 1: 

1335 value = value[0] 

1336 

1337 if dtype.endswith('s'): 

1338 value = stripnull(value) 

1339 

1340 self.code = code 

1341 self.name = name 

1342 self.dtype = dtype 

1343 self.count = count 

1344 self.value = value 

1345 

1346 def __str__(self): 

1347 """Return string containing information about tag.""" 

1348 return ' '.join(str(getattr(self, s)) for s in self.__slots__) 

1349 

1350 

1351class Record(dict): 

1352 """Dictionary with attribute access. 

1353 

1354 Can also be initialized with numpy.core.records.record. 

1355 

1356 """ 

1357 __slots__ = () 

1358 

1359 def __init__(self, arg=None, **kwargs): 

1360 if kwargs: 

1361 arg = kwargs 

1362 elif arg is None: 

1363 arg = {} 

1364 try: 

1365 dict.__init__(self, arg) 

1366 except TypeError: 

1367 for i, name in enumerate(arg.dtype.names): 

1368 v = arg[i] 

1369 self[name] = v if v.dtype.char != 'S' else stripnull(v) 

1370 

1371 def __getattr__(self, name): 

1372 return self[name] 

1373 

1374 def __setattr__(self, name, value): 

1375 self.__setitem__(name, value) 

1376 

1377 def __str__(self): 

1378 """Pretty print Record.""" 

1379 s = [] 

1380 lists = [] 

1381 for k in sorted(self): 

1382 if k.startswith('_'): 

1383 continue 

1384 v = self[k] 

1385 if isinstance(v, (list, tuple)) and len(v): 

1386 if isinstance(v[0], Record): 

1387 lists.append((k, v)) 

1388 continue 

1389 elif isinstance(v[0], TIFFpage): 

1390 v = [i.index for i in v if i] 

1391 s.append(("* %s: %s" % (k, str(v))).split("\n", 

1392 1)[0][:PRINT_LINE_LEN]) 

1393 for k, v in lists: 

1394 l = [] 

1395 for i, w in enumerate(v): 

1396 l.append("* %s[%i]\n %s" % (k, i, 

1397 str(w).replace("\n", "\n "))) 

1398 s.append('\n'.join(l)) 

1399 return '\n'.join(s) 

1400 

1401 

1402class TiffTags(Record): 

1403 """Dictionary of TIFFtags with attribute access.""" 

1404 def __str__(self): 

1405 """Return string with information about all tags.""" 

1406 s = [] 

1407 #sortbycode = lambda a, b: cmp(a.code, b.code) 

1408 #for tag in sorted(self.values(), sortbycode): 

1409 for tag in sorted(self.values(), key=lambda x: x.code): 

1410 typecode = "%i%s" % (tag.count * int(tag.dtype[0]), tag.dtype[1]) 

1411 line = "* %i %s (%s) %s" % (tag.code, tag.name, typecode, 

1412 str(tag.value).split('\n', 1)[0]) 

1413 s.append(line[:PRINT_LINE_LEN]) 

1414 return '\n'.join(s) 

1415 

1416 

1417def read_bytes(fd, byte_order, dtype, count): 

1418 """Read tag data from file and return as byte string.""" 

1419 return numpy.fromfile(fd, byte_order+dtype[-1], count).tostring() 

1420 

1421 

1422def read_numpy(fd, byte_order, dtype, count): 

1423 """Read tag data from file and return as numpy array.""" 

1424 return numpy.fromfile(fd, byte_order+dtype[-1], count) 

1425 

1426 

1427def read_nih_image_header(fd, byte_order, dtype, count): 

1428 """Read NIH_IMAGE_HEADER tag from file and return as dictionary.""" 

1429 fd.seek(12, 1) 

1430 return {'version': struct.unpack(byte_order+'H', fd.read(2))[0]} 

1431 

1432 

1433def read_mm_header(fd, byte_order, dtype, count): 

1434 """Read MM_HEADER tag from file and return as numpy.rec.array.""" 

1435 return numpy.rec.fromfile(fd, MM_HEADER, 1, byteorder=byte_order)[0] 

1436 

1437 

1438def read_mm_stamp(fd, byte_order, dtype, count): 

1439 """Read MM_STAMP tag from file and return as numpy.array.""" 

1440 return numpy.fromfile(fd, byte_order+'8f8', 1)[0] 

1441 

1442 

1443def read_mm_uic1(fd, byte_order, dtype, count): 

1444 """Read MM_UIC1 tag from file and return as dictionary.""" 

1445 t = fd.read(8*count) 

1446 t = struct.unpack('%s%iI' % (byte_order, 2*count), t) 

1447 return dict((MM_TAG_IDS[k], v) for k, v in zip(t[::2], t[1::2]) 

1448 if k in MM_TAG_IDS) 

1449 

1450 

1451def read_mm_uic2(fd, byte_order, dtype, count): 

1452 """Read MM_UIC2 tag from file and return as dictionary.""" 

1453 result = {'number_planes': count} 

1454 values = numpy.fromfile(fd, byte_order+'I', 6*count) 

1455 result['z_distance'] = values[0::6] // values[1::6] 

1456 #result['date_created'] = tuple(values[2::6]) 

1457 #result['time_created'] = tuple(values[3::6]) 

1458 #result['date_modified'] = tuple(values[4::6]) 

1459 #result['time_modified'] = tuple(values[5::6]) 

1460 return result 

1461 

1462 

1463def read_mm_uic3(fd, byte_order, dtype, count): 

1464 """Read MM_UIC3 tag from file and return as dictionary.""" 

1465 t = numpy.fromfile(fd, byte_order+'I', 2*count) 

1466 return {'wavelengths': t[0::2] // t[1::2]} 

1467 

1468 

1469def read_mm_uic4(fd, byte_order, dtype, count): 

1470 """Read MM_UIC4 tag from file and return as dictionary.""" 

1471 t = struct.unpack(byte_order + 'hI'*count, fd.read(6*count)) 

1472 return dict((MM_TAG_IDS[k], v) for k, v in zip(t[::2], t[1::2]) 

1473 if k in MM_TAG_IDS) 

1474 

1475 

1476def read_cz_lsm_info(fd, byte_order, dtype, count): 

1477 """Read CS_LSM_INFO tag from file and return as numpy.rec.array.""" 

1478 result = numpy.rec.fromfile(fd, CZ_LSM_INFO, 1, 

1479 byteorder=byte_order)[0] 

1480 {50350412: '1.3', 67127628: '2.0'}[result.magic_number] # validation 

1481 return result 

1482 

1483 

1484def read_cz_lsm_time_stamps(fd, byte_order): 

1485 """Read LSM time stamps from file and return as list.""" 

1486 size, count = struct.unpack(byte_order+'II', fd.read(8)) 

1487 if size != (8 + 8 * count): 

1488 raise ValueError("lsm_time_stamps block is too short") 

1489 return struct.unpack(('%s%dd' % (byte_order, count)), 

1490 fd.read(8*count)) 

1491 

1492 

1493def read_cz_lsm_event_list(fd, byte_order): 

1494 """Read LSM events from file and return as list of (time, type, text).""" 

1495 count = struct.unpack(byte_order+'II', fd.read(8))[1] 

1496 events = [] 

1497 while count > 0: 

1498 esize, etime, etype = struct.unpack(byte_order+'IdI', fd.read(16)) 

1499 etext = stripnull(fd.read(esize - 16)) 

1500 events.append((etime, etype, etext)) 

1501 count -= 1 

1502 return events 

1503 

1504 

1505def read_cz_lsm_scan_info(fd, byte_order): 

1506 """Read LSM scan information from file and return as Record.""" 

1507 block = Record() 

1508 blocks = [block] 

1509 unpack = struct.unpack 

1510 if 0x10000000 != struct.unpack(byte_order+"I", fd.read(4))[0]: 

1511 raise ValueError("not a lsm_scan_info structure") 

1512 fd.read(8) 

1513 while True: 

1514 entry, dtype, size = unpack(byte_order+"III", fd.read(12)) 

1515 if dtype == 2: 

1516 value = stripnull(fd.read(size)) 

1517 elif dtype == 4: 

1518 value = unpack(byte_order+"i", fd.read(4))[0] 

1519 elif dtype == 5: 

1520 value = unpack(byte_order+"d", fd.read(8))[0] 

1521 else: 

1522 value = 0 

1523 if entry in CZ_LSM_SCAN_INFO_ARRAYS: 

1524 blocks.append(block) 

1525 name = CZ_LSM_SCAN_INFO_ARRAYS[entry] 

1526 newobj = [] 

1527 setattr(block, name, newobj) 

1528 block = newobj 

1529 elif entry in CZ_LSM_SCAN_INFO_STRUCTS: 

1530 blocks.append(block) 

1531 newobj = Record() 

1532 block.append(newobj) 

1533 block = newobj 

1534 elif entry in CZ_LSM_SCAN_INFO_ATTRIBUTES: 

1535 name = CZ_LSM_SCAN_INFO_ATTRIBUTES[entry] 

1536 setattr(block, name, value) 

1537 elif entry == 0xffffffff: 

1538 block = blocks.pop() 

1539 else: 

1540 setattr(block, "unknown_%x" % entry, value) 

1541 if not blocks: 

1542 break 

1543 return block 

1544 

1545 

1546def _replace_by(module_function, warn=False): 

1547 """Try replace decorated function by module.function.""" 

1548 def decorate(func, module_function=module_function, warn=warn): 

1549 sys.path.append(os.path.dirname(__file__)) 

1550 try: 

1551 module, function = module_function.split('.') 

1552 func, oldfunc = getattr(__import__(module), function), func 

1553 globals()['__old_' + func.__name__] = oldfunc 

1554 except Exception: 

1555 if warn: 

1556 warnings.warn("failed to import %s" % module_function) 

1557 sys.path.pop() 

1558 return func 

1559 

1560 return decorate 

1561 

1562 

1563@_replace_by('_tifffile.decodepackbits') 

1564def decodepackbits(encoded): 

1565 """Decompress PackBits encoded byte string. 

1566 

1567 PackBits is a simple byte-oriented run-length compression scheme. 

1568 

1569 """ 

1570 func = ord if sys.version[0] == '2' else lambda x: x 

1571 result = [] 

1572 i = 0 

1573 try: 

1574 while True: 

1575 n = func(encoded[i]) + 1 

1576 i += 1 

1577 if n < 129: 

1578 result.extend(encoded[i:i+n]) 

1579 i += n 

1580 elif n > 129: 

1581 result.extend(encoded[i:i+1] * (258-n)) 

1582 i += 1 

1583 except IndexError: 

1584 pass 

1585 return b''.join(result) if sys.version[0] == '2' else bytes(result) 

1586 

1587 

1588@_replace_by('_tifffile.decodelzw') 

1589def decodelzw(encoded): 

1590 """Decompress LZW (Lempel-Ziv-Welch) encoded TIFF strip (byte string). 

1591 

1592 The strip must begin with a CLEAR code and end with an EOI code. 

1593 

1594 This is an implementation of the LZW decoding algorithm described in (1). 

1595 It is not compatible with old style LZW compressed files like quad-lzw.tif. 

1596 

1597 """ 

1598 unpack = struct.unpack 

1599 

1600 if sys.version[0] == '2': 

1601 newtable = [chr(i) for i in range(256)] 

1602 else: 

1603 newtable = [bytes([i]) for i in range(256)] 

1604 newtable.extend((0, 0)) 

1605 

1606 def next_code(): 

1607 """Return integer of `bitw` bits at `bitcount` position in encoded.""" 

1608 start = bitcount // 8 

1609 s = encoded[start:start+4] 

1610 try: 

1611 code = unpack('>I', s)[0] 

1612 except Exception: 

1613 code = unpack('>I', s + b'\x00'*(4-len(s)))[0] 

1614 code = code << (bitcount % 8) 

1615 code = code & mask 

1616 return code >> shr 

1617 

1618 switchbitch = { # code: bit-width, shr-bits, bit-mask 

1619 255: (9, 23, int(9*'1'+'0'*23, 2)), 

1620 511: (10, 22, int(10*'1'+'0'*22, 2)), 

1621 1023: (11, 21, int(11*'1'+'0'*21, 2)), 

1622 2047: (12, 20, int(12*'1'+'0'*20, 2)), } 

1623 bitw, shr, mask = switchbitch[255] 

1624 bitcount = 0 

1625 

1626 if len(encoded) < 4: 

1627 raise ValueError("strip must be at least 4 characters long") 

1628 

1629 if next_code() != 256: 

1630 raise ValueError("strip must begin with CLEAR code") 

1631 

1632 code = oldcode = 0 

1633 result = [] 

1634 while True: 

1635 code = next_code() # ~5% faster when inlining this function 

1636 bitcount += bitw 

1637 if code == 257: # EOI 

1638 break 

1639 if code == 256: # CLEAR 

1640 table = newtable[:] 

1641 lentable = 258 

1642 bitw, shr, mask = switchbitch[255] 

1643 code = next_code() 

1644 bitcount += bitw 

1645 if code == 257: # EOI 

1646 break 

1647 result.append(table[code]) 

1648 else: 

1649 if code < lentable: 

1650 decoded = table[code] 

1651 newcode = table[oldcode] + decoded[:1] 

1652 else: 

1653 newcode = table[oldcode] 

1654 newcode += newcode[:1] 

1655 decoded = newcode 

1656 result.append(decoded) 

1657 table.append(newcode) 

1658 lentable += 1 

1659 oldcode = code 

1660 if lentable in switchbitch: 

1661 bitw, shr, mask = switchbitch[lentable] 

1662 

1663 if code != 257: 

1664 raise ValueError("unexpected end of stream (code %i)" % code) 

1665 

1666 return b''.join(result) 

1667 

1668 

1669@_replace_by('_tifffile.unpackints') 

1670def unpackints(data, dtype, itemsize, runlen=0): 

1671 """Decompress byte string to array of integers of any bit size <= 32. 

1672 

1673 Parameters 

1674 ---------- 

1675 data : byte str 

1676 Data to decompress. 

1677 dtype : numpy.dtype or str 

1678 A numpy boolean or integer type. 

1679 itemsize : int 

1680 Number of bits per integer. 

1681 runlen : int 

1682 Number of consecutive integers, after which to start at next byte. 

1683 

1684 """ 

1685 if itemsize == 1: # bitarray 

1686 data = numpy.fromstring(data, '|B') 

1687 data = numpy.unpackbits(data) 

1688 if runlen % 8: 

1689 data = data.reshape(-1, runlen+(8-runlen%8)) 

1690 data = data[:, :runlen].reshape(-1) 

1691 return data.astype(dtype) 

1692 

1693 dtype = numpy.dtype(dtype) 

1694 if itemsize in (8, 16, 32, 64): 

1695 return numpy.fromstring(data, dtype) 

1696 if itemsize < 1 or itemsize > 32: 

1697 raise ValueError("itemsize out of range: %i" % itemsize) 

1698 if dtype.kind not in "biu": 

1699 raise ValueError("invalid dtype") 

1700 

1701 itembytes = next(i for i in (1, 2, 4, 8) if 8 * i >= itemsize) 

1702 if itembytes != dtype.itemsize: 

1703 raise ValueError("dtype.itemsize too small") 

1704 if runlen == 0: 

1705 runlen = len(data) // itembytes 

1706 skipbits = runlen*itemsize % 8 

1707 if skipbits: 

1708 skipbits = 8 - skipbits 

1709 shrbits = itembytes*8 - itemsize 

1710 bitmask = int(itemsize*'1'+'0'*shrbits, 2) 

1711 dtypestr = '>' + dtype.char # dtype always big endian? 

1712 

1713 unpack = struct.unpack 

1714 l = runlen * (len(data)*8 // (runlen*itemsize + skipbits)) 

1715 result = numpy.empty((l, ), dtype) 

1716 bitcount = 0 

1717 for i in range(len(result)): 

1718 start = bitcount // 8 

1719 s = data[start:start+itembytes] 

1720 try: 

1721 code = unpack(dtypestr, s)[0] 

1722 except Exception: 

1723 code = unpack(dtypestr, s + b'\x00'*(itembytes-len(s)))[0] 

1724 code = code << (bitcount % 8) 

1725 code = code & bitmask 

1726 result[i] = code >> shrbits 

1727 bitcount += itemsize 

1728 if (i+1) % runlen == 0: 

1729 bitcount += skipbits 

1730 return result 

1731 

1732 

1733def unpackrgb(data, dtype='<B', bitspersample=(5, 6, 5), rescale=True): 

1734 """Return array from byte string containing packed samples. 

1735 

1736 Use to unpack RGB565 or RGB555 to RGB888 format. 

1737 

1738 Parameters 

1739 ---------- 

1740 data : byte str 

1741 The data to be decoded. Samples in each pixel are stored consecutively. 

1742 Pixels are aligned to 8, 16, or 32 bit boundaries. 

1743 dtype : numpy.dtype 

1744 The sample data type. The byteorder applies also to the data stream. 

1745 bitspersample : tuple 

1746 Number of bits for each sample in a pixel. 

1747 rescale : bool 

1748 Upscale samples to the number of bits in dtype. 

1749 

1750 Returns 

1751 ------- 

1752 result : ndarray 

1753 Flattened array of unpacked samples of native dtype. 

1754 

1755 Examples 

1756 -------- 

1757 >>> data = struct.pack('BBBB', 0x21, 0x08, 0xff, 0xff) 

1758 >>> print(unpackrgb(data, '<B', (5, 6, 5), False)) 

1759 [ 1 1 1 31 63 31] 

1760 >>> print(unpackrgb(data, '<B', (5, 6, 5))) 

1761 [ 8 4 8 255 255 255] 

1762 >>> print(unpackrgb(data, '<B', (5, 5, 5))) 

1763 [ 16 8 8 255 255 255] 

1764 

1765 """ 

1766 dtype = numpy.dtype(dtype) 

1767 bits = int(numpy.sum(bitspersample)) 

1768 if not (bits <= 32 and all(i <= dtype.itemsize*8 for i in bitspersample)): 

1769 raise ValueError("sample size not supported %s" % str(bitspersample)) 

1770 dt = next(i for i in 'BHI' if numpy.dtype(i).itemsize*8 >= bits) 

1771 data = numpy.fromstring(data, dtype.byteorder+dt) 

1772 result = numpy.empty((data.size, len(bitspersample)), dtype.char) 

1773 for i, bps in enumerate(bitspersample): 

1774 t = data >> int(numpy.sum(bitspersample[i+1:])) 

1775 t &= int('0b'+'1'*bps, 2) 

1776 if rescale: 

1777 o = ((dtype.itemsize * 8) // bps + 1) * bps 

1778 if o > data.dtype.itemsize * 8: 

1779 t = t.astype('I') 

1780 t *= (2**o - 1) // (2**bps - 1) 

1781 t //= 2**(o - (dtype.itemsize * 8)) 

1782 result[:, i] = t 

1783 return result.reshape(-1) 

1784 

1785 

1786def reorient(image, orientation): 

1787 """Return reoriented view of image array. 

1788 

1789 Parameters 

1790 ---------- 

1791 image : numpy array 

1792 Non-squeezed output of asarray() functions. 

1793 Axes -3 and -2 must be image length and width respectively. 

1794 orientation : int or str 

1795 One of TIFF_ORIENTATIONS keys or values. 

1796 

1797 """ 

1798 o = TIFF_ORIENTATIONS.get(orientation, orientation) 

1799 if o == 'top_left': 

1800 return image 

1801 elif o == 'top_right': 

1802 return image[..., ::-1, :] 

1803 elif o == 'bottom_left': 

1804 return image[..., ::-1, :, :] 

1805 elif o == 'bottom_right': 

1806 return image[..., ::-1, ::-1, :] 

1807 elif o == 'left_top': 

1808 return numpy.swapaxes(image, -3, -2) 

1809 elif o == 'right_top': 

1810 return numpy.swapaxes(image, -3, -2)[..., ::-1, :] 

1811 elif o == 'left_bottom': 

1812 return numpy.swapaxes(image, -3, -2)[..., ::-1, :, :] 

1813 elif o == 'right_bottom': 

1814 return numpy.swapaxes(image, -3, -2)[..., ::-1, ::-1, :] 

1815 

1816 

1817def stripnull(string): 

1818 """Return string truncated at first null character.""" 

1819 i = string.find(b'\x00') 

1820 return string if (i < 0) else string[:i] 

1821 

1822 

1823def datetime_from_timestamp(n, epoch=datetime.datetime.fromordinal(693594)): 

1824 """Return datetime object from timestamp in Excel serial format. 

1825 

1826 Examples 

1827 -------- 

1828 >>> datetime_from_timestamp(40237.029999999795) 

1829 datetime.datetime(2010, 2, 28, 0, 43, 11, 999982) 

1830 

1831 """ 

1832 return epoch + datetime.timedelta(n) 

1833 

1834 

1835def test_tifffile(directory='testimages', verbose=True): 

1836 """Read all images in directory. Print error message on failure. 

1837 

1838 Examples 

1839 -------- 

1840 >>> test_tifffile(verbose=False) 

1841 

1842 """ 

1843 import glob 

1844 

1845 successful = 0 

1846 failed = 0 

1847 start = time.time() 

1848 for f in glob.glob(os.path.join(directory, '*.*')): 

1849 if verbose: 

1850 print("\n%s>\n" % f.lower(), end='') 

1851 t0 = time.time() 

1852 try: 

1853 tif = TIFFfile(f) 

1854 except Exception as e: 

1855 if not verbose: 

1856 print(f, end=' ') 

1857 print("ERROR:", e) 

1858 failed += 1 

1859 continue 

1860 try: 

1861 img = tif.asarray() 

1862 except ValueError: 

1863 try: 

1864 img = tif[0].asarray() 

1865 except Exception as e: 

1866 if not verbose: 

1867 print(f, end=' ') 

1868 print("ERROR:", e) 

1869 failed += 1 

1870 continue 

1871 finally: 

1872 tif.close() 

1873 successful += 1 

1874 if verbose: 

1875 print("%s, %s %s, %s, %.0f ms" % (str(tif), str(img.shape), 

1876 img.dtype, tif[0].compression, (time.time()-t0) * 1e3)) 

1877 if verbose: 

1878 print("\nSuccessfully read %i of %i files in %.3f s\n" % ( 

1879 successful, successful+failed, time.time()-start)) 

1880 

1881 

1882class TIFF_SUBFILE_TYPES(object): 

1883 def __getitem__(self, key): 

1884 result = [] 

1885 if key & 1: 

1886 result.append('reduced_image') 

1887 if key & 2: 

1888 result.append('page') 

1889 if key & 4: 

1890 result.append('mask') 

1891 return tuple(result) 

1892 

1893 

1894TIFF_PHOTOMETRICS = { 

1895 0: 'miniswhite', 

1896 1: 'minisblack', 

1897 2: 'rgb', 

1898 3: 'palette', 

1899 4: 'mask', 

1900 5: 'separated', 

1901 6: 'cielab', 

1902 7: 'icclab', 

1903 8: 'itulab', 

1904 32844: 'logl', 

1905 32845: 'logluv'} 

1906 

1907TIFF_COMPESSIONS = { 

1908 1: None, 

1909 2: 'ccittrle', 

1910 3: 'ccittfax3', 

1911 4: 'ccittfax4', 

1912 5: 'lzw', 

1913 6: 'ojpeg', 

1914 7: 'jpeg', 

1915 8: 'adobe_deflate', 

1916 9: 't85', 

1917 10: 't43', 

1918 32766: 'next', 

1919 32771: 'ccittrlew', 

1920 32773: 'packbits', 

1921 32809: 'thunderscan', 

1922 32895: 'it8ctpad', 

1923 32896: 'it8lw', 

1924 32897: 'it8mp', 

1925 32898: 'it8bl', 

1926 32908: 'pixarfilm', 

1927 32909: 'pixarlog', 

1928 32946: 'deflate', 

1929 32947: 'dcs', 

1930 34661: 'jbig', 

1931 34676: 'sgilog', 

1932 34677: 'sgilog24', 

1933 34712: 'jp2000'} 

1934 

1935TIFF_DECOMPESSORS = { 

1936 None: lambda x: x, 

1937 'adobe_deflate': zlib.decompress, 

1938 'deflate': zlib.decompress, 

1939 'packbits': decodepackbits, 

1940 'lzw': decodelzw} 

1941 

1942TIFF_DATA_TYPES = { 

1943 1: '1B', # BYTE 8-bit unsigned integer. 

1944 2: '1s', # ASCII 8-bit byte that contains a 7-bit ASCII code; 

1945 # the last byte must be NULL (binary zero). 

1946 3: '1H', # SHORT 16-bit (2-byte) unsigned integer 

1947 4: '1I', # LONG 32-bit (4-byte) unsigned integer. 

1948 5: '2I', # RATIONAL Two LONGs: the first represents the numerator of 

1949 # a fraction; the second, the denominator. 

1950 6: '1b', # SBYTE An 8-bit signed (twos-complement) integer. 

1951 7: '1B', # UNDEFINED An 8-bit byte that may contain anything, 

1952 # depending on the definition of the field. 

1953 8: '1h', # SSHORT A 16-bit (2-byte) signed (twos-complement) integer. 

1954 9: '1i', # SLONG A 32-bit (4-byte) signed (twos-complement) integer. 

1955 10: '2i', # SRATIONAL Two SLONGs: the first represents the numerator 

1956 # of a fraction, the second the denominator. 

1957 11: '1f', # FLOAT Single precision (4-byte) IEEE format. 

1958 12: '1d', # DOUBLE Double precision (8-byte) IEEE format. 

1959 13: '1I', # IFD unsigned 4 byte IFD offset. 

1960 #14: '', # UNICODE 

1961 #15: '', # COMPLEX 

1962 16: '1Q', # LONG8 unsigned 8 byte integer (BigTiff) 

1963 17: '1q', # SLONG8 signed 8 byte integer (BigTiff) 

1964 18: '1Q'} # IFD8 unsigned 8 byte IFD offset (BigTiff) 

1965 

1966TIFF_SAMPLE_FORMATS = { 

1967 1: 'uint', 

1968 2: 'int', 

1969 3: 'float', 

1970 #4: 'void', 

1971 #5: 'complex_int', 

1972 6: 'complex'} 

1973 

1974TIFF_SAMPLE_DTYPES = { 

1975 ('uint', 1): '?', # bitmap 

1976 ('uint', 2): 'B', 

1977 ('uint', 3): 'B', 

1978 ('uint', 4): 'B', 

1979 ('uint', 5): 'B', 

1980 ('uint', 6): 'B', 

1981 ('uint', 7): 'B', 

1982 ('uint', 8): 'B', 

1983 ('uint', 9): 'H', 

1984 ('uint', 10): 'H', 

1985 ('uint', 11): 'H', 

1986 ('uint', 12): 'H', 

1987 ('uint', 13): 'H', 

1988 ('uint', 14): 'H', 

1989 ('uint', 15): 'H', 

1990 ('uint', 16): 'H', 

1991 ('uint', 17): 'I', 

1992 ('uint', 18): 'I', 

1993 ('uint', 19): 'I', 

1994 ('uint', 20): 'I', 

1995 ('uint', 21): 'I', 

1996 ('uint', 22): 'I', 

1997 ('uint', 23): 'I', 

1998 ('uint', 24): 'I', 

1999 ('uint', 25): 'I', 

2000 ('uint', 26): 'I', 

2001 ('uint', 27): 'I', 

2002 ('uint', 28): 'I', 

2003 ('uint', 29): 'I', 

2004 ('uint', 30): 'I', 

2005 ('uint', 31): 'I', 

2006 ('uint', 32): 'I', 

2007 ('uint', 64): 'Q', 

2008 ('int', 8): 'b', 

2009 ('int', 16): 'h', 

2010 ('int', 32): 'i', 

2011 ('int', 64): 'q', 

2012 ('float', 16): 'e', 

2013 ('float', 32): 'f', 

2014 ('float', 64): 'd', 

2015 ('complex', 64): 'F', 

2016 ('complex', 128): 'D', 

2017 ('uint', (5, 6, 5)): 'B'} 

2018 

2019TIFF_ORIENTATIONS = { 

2020 1: 'top_left', 

2021 2: 'top_right', 

2022 3: 'bottom_right', 

2023 4: 'bottom_left', 

2024 5: 'left_top', 

2025 6: 'right_top', 

2026 7: 'right_bottom', 

2027 8: 'left_bottom'} 

2028 

2029AXES_LABELS = { 

2030 'X': 'width', 

2031 'Y': 'height', 

2032 'Z': 'depth', 

2033 'S': 'sample', 

2034 'P': 'plane', 

2035 'T': 'time', 

2036 'C': 'channel', # color, emission wavelength 

2037 'A': 'angle', 

2038 'F': 'phase', 

2039 'R': 'tile', # region 

2040 'H': 'lifetime', # histogram 

2041 'E': 'lambda', # excitation wavelength 

2042 'L': 'exposure', # lux 

2043 'V': 'event', 

2044 'O': 'other'} 

2045 

2046AXES_LABELS.update(dict((v, k) for k, v in AXES_LABELS.items())) 

2047 

2048# MetaMorph STK tags 

2049MM_TAG_IDS = { 

2050 0: 'auto_scale', 

2051 1: 'min_scale', 

2052 2: 'max_scale', 

2053 3: 'spatial_calibration', 

2054 #4: 'x_calibration', 

2055 #5: 'y_calibration', 

2056 #6: 'calibration_units', 

2057 #7: 'name', 

2058 8: 'thresh_state', 

2059 9: 'thresh_state_red', 

2060 11: 'thresh_state_green', 

2061 12: 'thresh_state_blue', 

2062 13: 'thresh_state_lo', 

2063 14: 'thresh_state_hi', 

2064 15: 'zoom', 

2065 #16: 'create_time', 

2066 #17: 'last_saved_time', 

2067 18: 'current_buffer', 

2068 19: 'gray_fit', 

2069 20: 'gray_point_count', 

2070 #21: 'gray_x', 

2071 #22: 'gray_y', 

2072 #23: 'gray_min', 

2073 #24: 'gray_max', 

2074 #25: 'gray_unit_name', 

2075 26: 'standard_lut', 

2076 27: 'wavelength', 

2077 #28: 'stage_position', 

2078 #29: 'camera_chip_offset', 

2079 #30: 'overlay_mask', 

2080 #31: 'overlay_compress', 

2081 #32: 'overlay', 

2082 #33: 'special_overlay_mask', 

2083 #34: 'special_overlay_compress', 

2084 #35: 'special_overlay', 

2085 36: 'image_property', 

2086 #37: 'stage_label', 

2087 #38: 'autoscale_lo_info', 

2088 #39: 'autoscale_hi_info', 

2089 #40: 'absolute_z', 

2090 #41: 'absolute_z_valid', 

2091 #42: 'gamma', 

2092 #43: 'gamma_red', 

2093 #44: 'gamma_green', 

2094 #45: 'gamma_blue', 

2095 #46: 'camera_bin', 

2096 47: 'new_lut', 

2097 #48: 'image_property_ex', 

2098 49: 'plane_property', 

2099 #50: 'user_lut_table', 

2100 51: 'red_autoscale_info', 

2101 #52: 'red_autoscale_lo_info', 

2102 #53: 'red_autoscale_hi_info', 

2103 54: 'red_minscale_info', 

2104 55: 'red_maxscale_info', 

2105 56: 'green_autoscale_info', 

2106 #57: 'green_autoscale_lo_info', 

2107 #58: 'green_autoscale_hi_info', 

2108 59: 'green_minscale_info', 

2109 60: 'green_maxscale_info', 

2110 61: 'blue_autoscale_info', 

2111 #62: 'blue_autoscale_lo_info', 

2112 #63: 'blue_autoscale_hi_info', 

2113 64: 'blue_min_scale_info', 

2114 65: 'blue_max_scale_info'} 

2115 #66: 'overlay_plane_color', 

2116 

2117# Olymus Fluoview 

2118MM_DIMENSION = [ 

2119 ('name', 'a16'), 

2120 ('size', 'i4'), 

2121 ('origin', 'f8'), 

2122 ('resolution', 'f8'), 

2123 ('unit', 'a64')] 

2124 

2125MM_HEADER = [ 

2126 ('header_flag', 'i2'), 

2127 ('image_type', 'u1'), 

2128 ('image_name', 'a257'), 

2129 ('offset_data', 'u4'), 

2130 ('palette_size', 'i4'), 

2131 ('offset_palette0', 'u4'), 

2132 ('offset_palette1', 'u4'), 

2133 ('comment_size', 'i4'), 

2134 ('offset_comment', 'u4'), 

2135 ('dimensions', MM_DIMENSION, 10), 

2136 ('offset_position', 'u4'), 

2137 ('map_type', 'i2'), 

2138 ('map_min', 'f8'), 

2139 ('map_max', 'f8'), 

2140 ('min_value', 'f8'), 

2141 ('max_value', 'f8'), 

2142 ('offset_map', 'u4'), 

2143 ('gamma', 'f8'), 

2144 ('offset', 'f8'), 

2145 ('gray_channel', MM_DIMENSION), 

2146 ('offset_thumbnail', 'u4'), 

2147 ('voice_field', 'i4'), 

2148 ('offset_voice_field', 'u4')] 

2149 

2150# Carl Zeiss LSM 

2151CZ_LSM_INFO = [ 

2152 ('magic_number', 'i4'), 

2153 ('structure_size', 'i4'), 

2154 ('dimension_x', 'i4'), 

2155 ('dimension_y', 'i4'), 

2156 ('dimension_z', 'i4'), 

2157 ('dimension_channels', 'i4'), 

2158 ('dimension_time', 'i4'), 

2159 ('dimension_data_type', 'i4'), 

2160 ('thumbnail_x', 'i4'), 

2161 ('thumbnail_y', 'i4'), 

2162 ('voxel_size_x', 'f8'), 

2163 ('voxel_size_y', 'f8'), 

2164 ('voxel_size_z', 'f8'), 

2165 ('origin_x', 'f8'), 

2166 ('origin_y', 'f8'), 

2167 ('origin_z', 'f8'), 

2168 ('scan_type', 'u2'), 

2169 ('spectral_scan', 'u2'), 

2170 ('data_type', 'u4'), 

2171 ('offset_vector_overlay', 'u4'), 

2172 ('offset_input_lut', 'u4'), 

2173 ('offset_output_lut', 'u4'), 

2174 ('offset_channel_colors', 'u4'), 

2175 ('time_interval', 'f8'), 

2176 ('offset_channel_data_types', 'u4'), 

2177 ('offset_scan_information', 'u4'), 

2178 ('offset_ks_data', 'u4'), 

2179 ('offset_time_stamps', 'u4'), 

2180 ('offset_event_list', 'u4'), 

2181 ('offset_roi', 'u4'), 

2182 ('offset_bleach_roi', 'u4'), 

2183 ('offset_next_recording', 'u4'), 

2184 ('display_aspect_x', 'f8'), 

2185 ('display_aspect_y', 'f8'), 

2186 ('display_aspect_z', 'f8'), 

2187 ('display_aspect_time', 'f8'), 

2188 ('offset_mean_of_roi_overlay', 'u4'), 

2189 ('offset_topo_isoline_overlay', 'u4'), 

2190 ('offset_topo_profile_overlay', 'u4'), 

2191 ('offset_linescan_overlay', 'u4'), 

2192 ('offset_toolbar_flags', 'u4')] 

2193 

2194# Import functions for LSM_INFO subrecords 

2195CZ_LSM_INFO_READERS = { 

2196 'scan_information': read_cz_lsm_scan_info, 

2197 'time_stamps': read_cz_lsm_time_stamps, 

2198 'event_list': read_cz_lsm_event_list} 

2199 

2200# Map cz_lsm_info.scan_type to dimension order 

2201CZ_SCAN_TYPES = { 

2202 0: 'XYZCT', # x-y-z scan 

2203 1: 'XYZCT', # z scan (x-z plane) 

2204 2: 'XYZCT', # line scan 

2205 3: 'XYTCZ', # time series x-y 

2206 4: 'XYZTC', # time series x-z 

2207 5: 'XYTCZ', # time series 'Mean of ROIs' 

2208 6: 'XYZTC', # time series x-y-z 

2209 7: 'XYCTZ', # spline scan 

2210 8: 'XYCZT', # spline scan x-z 

2211 9: 'XYTCZ', # time series spline plane x-z 

2212 10: 'XYZCT'} # point mode 

2213 

2214# Map dimension codes to cz_lsm_info attribute 

2215CZ_DIMENSIONS = { 

2216 'X': 'dimension_x', 

2217 'Y': 'dimension_y', 

2218 'Z': 'dimension_z', 

2219 'C': 'dimension_channels', 

2220 'T': 'dimension_time'} 

2221 

2222# Descriptions of cz_lsm_info.data_type 

2223CZ_DATA_TYPES = { 

2224 0: 'varying data types', 

2225 2: '12 bit unsigned integer', 

2226 5: '32 bit float'} 

2227 

2228CZ_LSM_SCAN_INFO_ARRAYS = { 

2229 0x20000000: "tracks", 

2230 0x30000000: "lasers", 

2231 0x60000000: "detectionchannels", 

2232 0x80000000: "illuminationchannels", 

2233 0xa0000000: "beamsplitters", 

2234 0xc0000000: "datachannels", 

2235 0x13000000: "markers", 

2236 0x11000000: "timers"} 

2237 

2238CZ_LSM_SCAN_INFO_STRUCTS = { 

2239 0x40000000: "tracks", 

2240 0x50000000: "lasers", 

2241 0x70000000: "detectionchannels", 

2242 0x90000000: "illuminationchannels", 

2243 0xb0000000: "beamsplitters", 

2244 0xd0000000: "datachannels", 

2245 0x14000000: "markers", 

2246 0x12000000: "timers"} 

2247 

2248CZ_LSM_SCAN_INFO_ATTRIBUTES = { 

2249 0x10000001: "name", 

2250 0x10000002: "description", 

2251 0x10000003: "notes", 

2252 0x10000004: "objective", 

2253 0x10000005: "processing_summary", 

2254 0x10000006: "special_scan_mode", 

2255 0x10000007: "oledb_recording_scan_type", 

2256 0x10000008: "oledb_recording_scan_mode", 

2257 0x10000009: "number_of_stacks", 

2258 0x1000000a: "lines_per_plane", 

2259 0x1000000b: "samples_per_line", 

2260 0x1000000c: "planes_per_volume", 

2261 0x1000000d: "images_width", 

2262 0x1000000e: "images_height", 

2263 0x1000000f: "images_number_planes", 

2264 0x10000010: "images_number_stacks", 

2265 0x10000011: "images_number_channels", 

2266 0x10000012: "linscan_xy_size", 

2267 0x10000013: "scan_direction", 

2268 0x10000014: "time_series", 

2269 0x10000015: "original_scan_data", 

2270 0x10000016: "zoom_x", 

2271 0x10000017: "zoom_y", 

2272 0x10000018: "zoom_z", 

2273 0x10000019: "sample_0x", 

2274 0x1000001a: "sample_0y", 

2275 0x1000001b: "sample_0z", 

2276 0x1000001c: "sample_spacing", 

2277 0x1000001d: "line_spacing", 

2278 0x1000001e: "plane_spacing", 

2279 0x1000001f: "plane_width", 

2280 0x10000020: "plane_height", 

2281 0x10000021: "volume_depth", 

2282 0x10000023: "nutation", 

2283 0x10000034: "rotation", 

2284 0x10000035: "precession", 

2285 0x10000036: "sample_0time", 

2286 0x10000037: "start_scan_trigger_in", 

2287 0x10000038: "start_scan_trigger_out", 

2288 0x10000039: "start_scan_event", 

2289 0x10000040: "start_scan_time", 

2290 0x10000041: "stop_scan_trigger_in", 

2291 0x10000042: "stop_scan_trigger_out", 

2292 0x10000043: "stop_scan_event", 

2293 0x10000044: "stop_scan_time", 

2294 0x10000045: "use_rois", 

2295 0x10000046: "use_reduced_memory_rois", 

2296 0x10000047: "user", 

2297 0x10000048: "use_bccorrection", 

2298 0x10000049: "position_bccorrection1", 

2299 0x10000050: "position_bccorrection2", 

2300 0x10000051: "interpolation_y", 

2301 0x10000052: "camera_binning", 

2302 0x10000053: "camera_supersampling", 

2303 0x10000054: "camera_frame_width", 

2304 0x10000055: "camera_frame_height", 

2305 0x10000056: "camera_offset_x", 

2306 0x10000057: "camera_offset_y", 

2307 # lasers 

2308 0x50000001: "name", 

2309 0x50000002: "acquire", 

2310 0x50000003: "power", 

2311 # tracks 

2312 0x40000001: "multiplex_type", 

2313 0x40000002: "multiplex_order", 

2314 0x40000003: "sampling_mode", 

2315 0x40000004: "sampling_method", 

2316 0x40000005: "sampling_number", 

2317 0x40000006: "acquire", 

2318 0x40000007: "sample_observation_time", 

2319 0x4000000b: "time_between_stacks", 

2320 0x4000000c: "name", 

2321 0x4000000d: "collimator1_name", 

2322 0x4000000e: "collimator1_position", 

2323 0x4000000f: "collimator2_name", 

2324 0x40000010: "collimator2_position", 

2325 0x40000011: "is_bleach_track", 

2326 0x40000012: "is_bleach_after_scan_number", 

2327 0x40000013: "bleach_scan_number", 

2328 0x40000014: "trigger_in", 

2329 0x40000015: "trigger_out", 

2330 0x40000016: "is_ratio_track", 

2331 0x40000017: "bleach_count", 

2332 0x40000018: "spi_center_wavelength", 

2333 0x40000019: "pixel_time", 

2334 0x40000021: "condensor_frontlens", 

2335 0x40000023: "field_stop_value", 

2336 0x40000024: "id_condensor_aperture", 

2337 0x40000025: "condensor_aperture", 

2338 0x40000026: "id_condensor_revolver", 

2339 0x40000027: "condensor_filter", 

2340 0x40000028: "id_transmission_filter1", 

2341 0x40000029: "id_transmission1", 

2342 0x40000030: "id_transmission_filter2", 

2343 0x40000031: "id_transmission2", 

2344 0x40000032: "repeat_bleach", 

2345 0x40000033: "enable_spot_bleach_pos", 

2346 0x40000034: "spot_bleach_posx", 

2347 0x40000035: "spot_bleach_posy", 

2348 0x40000036: "spot_bleach_posz", 

2349 0x40000037: "id_tubelens", 

2350 0x40000038: "id_tubelens_position", 

2351 0x40000039: "transmitted_light", 

2352 0x4000003a: "reflected_light", 

2353 0x4000003b: "simultan_grab_and_bleach", 

2354 0x4000003c: "bleach_pixel_time", 

2355 # detection_channels 

2356 0x70000001: "integration_mode", 

2357 0x70000002: "special_mode", 

2358 0x70000003: "detector_gain_first", 

2359 0x70000004: "detector_gain_last", 

2360 0x70000005: "amplifier_gain_first", 

2361 0x70000006: "amplifier_gain_last", 

2362 0x70000007: "amplifier_offs_first", 

2363 0x70000008: "amplifier_offs_last", 

2364 0x70000009: "pinhole_diameter", 

2365 0x7000000a: "counting_trigger", 

2366 0x7000000b: "acquire", 

2367 0x7000000c: "point_detector_name", 

2368 0x7000000d: "amplifier_name", 

2369 0x7000000e: "pinhole_name", 

2370 0x7000000f: "filter_set_name", 

2371 0x70000010: "filter_name", 

2372 0x70000013: "integrator_name", 

2373 0x70000014: "detection_channel_name", 

2374 0x70000015: "detection_detector_gain_bc1", 

2375 0x70000016: "detection_detector_gain_bc2", 

2376 0x70000017: "detection_amplifier_gain_bc1", 

2377 0x70000018: "detection_amplifier_gain_bc2", 

2378 0x70000019: "detection_amplifier_offset_bc1", 

2379 0x70000020: "detection_amplifier_offset_bc2", 

2380 0x70000021: "detection_spectral_scan_channels", 

2381 0x70000022: "detection_spi_wavelength_start", 

2382 0x70000023: "detection_spi_wavelength_stop", 

2383 0x70000026: "detection_dye_name", 

2384 0x70000027: "detection_dye_folder", 

2385 # illumination_channels 

2386 0x90000001: "name", 

2387 0x90000002: "power", 

2388 0x90000003: "wavelength", 

2389 0x90000004: "aquire", 

2390 0x90000005: "detchannel_name", 

2391 0x90000006: "power_bc1", 

2392 0x90000007: "power_bc2", 

2393 # beam_splitters 

2394 0xb0000001: "filter_set", 

2395 0xb0000002: "filter", 

2396 0xb0000003: "name", 

2397 # data_channels 

2398 0xd0000001: "name", 

2399 0xd0000003: "acquire", 

2400 0xd0000004: "color", 

2401 0xd0000005: "sample_type", 

2402 0xd0000006: "bits_per_sample", 

2403 0xd0000007: "ratio_type", 

2404 0xd0000008: "ratio_track1", 

2405 0xd0000009: "ratio_track2", 

2406 0xd000000a: "ratio_channel1", 

2407 0xd000000b: "ratio_channel2", 

2408 0xd000000c: "ratio_const1", 

2409 0xd000000d: "ratio_const2", 

2410 0xd000000e: "ratio_const3", 

2411 0xd000000f: "ratio_const4", 

2412 0xd0000010: "ratio_const5", 

2413 0xd0000011: "ratio_const6", 

2414 0xd0000012: "ratio_first_images1", 

2415 0xd0000013: "ratio_first_images2", 

2416 0xd0000014: "dye_name", 

2417 0xd0000015: "dye_folder", 

2418 0xd0000016: "spectrum", 

2419 0xd0000017: "acquire", 

2420 # markers 

2421 0x14000001: "name", 

2422 0x14000002: "description", 

2423 0x14000003: "trigger_in", 

2424 0x14000004: "trigger_out", 

2425 # timers 

2426 0x12000001: "name", 

2427 0x12000002: "description", 

2428 0x12000003: "interval", 

2429 0x12000004: "trigger_in", 

2430 0x12000005: "trigger_out", 

2431 0x12000006: "activation_time", 

2432 0x12000007: "activation_number"} 

2433 

2434# Map TIFF tag code to attribute name, default value, type, count, validator 

2435TIFF_TAGS = { 

2436 254: ('new_subfile_type', 0, 4, 1, TIFF_SUBFILE_TYPES()), 

2437 255: ('subfile_type', None, 3, 1, 

2438 {0: 'undefined', 1: 'image', 2: 'reduced_image', 3: 'page'}), 

2439 256: ('image_width', None, 4, 1, None), 

2440 257: ('image_length', None, 4, 1, None), 

2441 258: ('bits_per_sample', 1, 3, 1, None), 

2442 259: ('compression', 1, 3, 1, TIFF_COMPESSIONS), 

2443 262: ('photometric', None, 3, 1, TIFF_PHOTOMETRICS), 

2444 266: ('fill_order', 1, 3, 1, {1: 'msb2lsb', 2: 'lsb2msb'}), 

2445 269: ('document_name', None, 2, None, None), 

2446 270: ('image_description', None, 2, None, None), 

2447 271: ('make', None, 2, None, None), 

2448 272: ('model', None, 2, None, None), 

2449 273: ('strip_offsets', None, 4, None, None), 

2450 274: ('orientation', 1, 3, 1, TIFF_ORIENTATIONS), 

2451 277: ('samples_per_pixel', 1, 3, 1, None), 

2452 278: ('rows_per_strip', 2**32-1, 4, 1, None), 

2453 279: ('strip_byte_counts', None, 4, None, None), 

2454 #280: ('min_sample_value', 0, 3, None, None), 

2455 #281: ('max_sample_value', None, 3, None, None), # 2**bits_per_sample 

2456 282: ('x_resolution', None, 5, 1, None), 

2457 283: ('y_resolution', None, 5, 1, None), 

2458 284: ('planar_configuration', 1, 3, 1, {1: 'contig', 2: 'separate'}), 

2459 285: ('page_name', None, 2, None, None), 

2460 296: ('resolution_unit', 2, 4, 1, {1: 'none', 2: 'inch', 3: 'centimeter'}), 

2461 305: ('software', None, 2, None, None), 

2462 306: ('datetime', None, 2, None, None), 

2463 315: ('artist', None, 2, None, None), 

2464 316: ('host_computer', None, 2, None, None), 

2465 317: ('predictor', 1, 3, 1, {1: None, 2: 'horizontal'}), 

2466 320: ('color_map', None, 3, None, None), 

2467 322: ('tile_width', None, 4, 1, None), 

2468 323: ('tile_length', None, 4, 1, None), 

2469 324: ('tile_offsets', None, 4, None, None), 

2470 325: ('tile_byte_counts', None, 4, None, None), 

2471 338: ('extra_samples', None, 3, None, 

2472 {0: 'unspecified', 1: 'assocalpha', 2: 'unassalpha'}), 

2473 339: ('sample_format', 1, 3, 1, TIFF_SAMPLE_FORMATS), 

2474 530: ('ycbcr_subsampling', 1, 3, 2, None), 

2475 531: ('ycbcr_positioning', 1, 3, 1, None), 

2476 #37510: ('user_comment', None, None, None, None), 

2477 33432: ('copyright', None, 1, None, None), 

2478 32997: ('image_depth', None, 4, 1, None), 

2479 32998: ('tile_depth', None, 4, 1, None), 

2480 34665: ('exif_ifd', None, None, 1, None), 

2481 34853: ('gps_ifd', None, None, 1, None), 

2482 42112: ('gdal_metadata', None, 2, None, None)} 

2483 

2484# Map custom TIFF tag codes to attribute names and import functions 

2485CUSTOM_TAGS = { 

2486 700: ('xmp', read_bytes), 

2487 34377: ('photoshop', read_numpy), 

2488 33723: ('iptc', read_bytes), 

2489 34675: ('icc_profile', read_numpy), 

2490 33628: ('mm_uic1', read_mm_uic1), 

2491 33629: ('mm_uic2', read_mm_uic2), 

2492 33630: ('mm_uic3', read_mm_uic3), 

2493 33631: ('mm_uic4', read_mm_uic4), 

2494 34361: ('mm_header', read_mm_header), 

2495 34362: ('mm_stamp', read_mm_stamp), 

2496 34386: ('mm_user_block', read_bytes), 

2497 34412: ('cz_lsm_info', read_cz_lsm_info), 

2498 43314: ('nih_image_header', read_nih_image_header)} 

2499 

2500# Max line length of printed output 

2501PRINT_LINE_LEN = 79 

2502 

2503 

2504def imshow(data, title=None, vmin=0, vmax=None, cmap=None, 

2505 bitspersample=None, photometric='rgb', interpolation='nearest', 

2506 dpi=96, figure=None, subplot=111, maxdim=4096, **kwargs): 

2507 """Plot n-dimensional images using matplotlib.pyplot. 

2508 

2509 Return figure, subplot and plot axis. 

2510 Requires pyplot already imported ``from matplotlib import pyplot``. 

2511 

2512 Parameters 

2513 ---------- 

2514 bitspersample : int or None 

2515 Number of bits per channel in integer RGB images. 

2516 photometric : {'miniswhite', 'minisblack', 'rgb', or 'palette'} 

2517 The color space of the image data. 

2518 title : str 

2519 Window and subplot title. 

2520 figure : matplotlib.figure.Figure (optional). 

2521 Matplotlib to use for plotting. 

2522 subplot : int 

2523 A matplotlib.pyplot.subplot axis. 

2524 maxdim : int 

2525 maximum image size in any dimension. 

2526 kwargs : optional 

2527 Arguments for matplotlib.pyplot.imshow. 

2528 

2529 """ 

2530 #if photometric not in ('miniswhite', 'minisblack', 'rgb', 'palette'): 

2531 # raise ValueError("Can't handle %s photometrics" % photometric) 

2532 isrgb = photometric in ('rgb', 'palette') 

2533 data = numpy.atleast_2d(data.squeeze()) 

2534 data = data[(slice(0, maxdim), ) * len(data.shape)] 

2535 

2536 dims = data.ndim 

2537 if dims < 2: 

2538 raise ValueError("not an image") 

2539 elif dims == 2: 

2540 dims = 0 

2541 isrgb = False 

2542 else: 

2543 if (isrgb and data.shape[-3] in (3, 4)): 

2544 data = numpy.swapaxes(data, -3, -2) 

2545 data = numpy.swapaxes(data, -2, -1) 

2546 elif (not isrgb and data.shape[-1] in (3, 4)): 

2547 data = numpy.swapaxes(data, -3, -1) 

2548 data = numpy.swapaxes(data, -2, -1) 

2549 isrgb = isrgb and data.shape[-1] in (3, 4) 

2550 dims -= 3 if isrgb else 2 

2551 

2552 if photometric == 'palette': 

2553 datamax = data.max() 

2554 if datamax > 255: 

2555 data >>= 8 # possible precision loss 

2556 data = data.astype('B') 

2557 elif data.dtype.kind in 'ui': 

2558 if not isrgb or bitspersample is None: 

2559 bitspersample = int(math.ceil(math.log(data.max(), 2))) 

2560 elif not isinstance(bitspersample, int): 

2561 # bitspersample can be tuple, e.g. (5, 6, 5) 

2562 bitspersample = data.dtype.itemsize * 8 

2563 datamax = 2**bitspersample 

2564 if isrgb: 

2565 if bitspersample < 8: 

2566 data <<= 8 - bitspersample 

2567 elif bitspersample > 8: 

2568 data >>= bitspersample - 8 # precision loss 

2569 data = data.astype('B') 

2570 elif data.dtype.kind == 'f': 

2571 datamax = data.max() 

2572 if isrgb and datamax > 1.0: 

2573 if data.dtype.char == 'd': 

2574 data = data.astype('f') 

2575 data /= datamax 

2576 elif data.dtype.kind == 'b': 

2577 datamax = 1 

2578 

2579 if vmax is None: 

2580 vmax = datamax 

2581 if vmin is None: 

2582 if data.dtype.kind != 'f': 

2583 vmin = 0 

2584 

2585 pyplot = sys.modules['matplotlib.pyplot'] 

2586 

2587 if figure is None: 

2588 pyplot.rc('font', family='sans-serif', weight='normal', size=8) 

2589 figure = pyplot.figure(dpi=dpi, figsize=(10.3, 6.3), frameon=True, 

2590 facecolor='1.0', edgecolor='w') 

2591 try: 

2592 figure.canvas.manager.window.title(title) 

2593 except Exception: 

2594 pass 

2595 pyplot.subplots_adjust(bottom=0.03*(dims+2), top=0.9, 

2596 left=0.1, right=0.95, hspace=0.05, wspace=0.0) 

2597 subplot = pyplot.subplot(subplot) 

2598 

2599 if title: 

2600 pyplot.title(title, size=11) 

2601 

2602 if cmap is None: 

2603 if photometric == 'miniswhite': 

2604 cmap = 'gray_r' if vmin == 0 else 'coolwarm_r' 

2605 else: 

2606 cmap = 'gray' if vmin == 0 else 'coolwarm' 

2607 

2608 image = pyplot.imshow(data[(0, ) * dims].squeeze(), vmin=vmin, vmax=vmax, 

2609 cmap=cmap, interpolation=interpolation, **kwargs) 

2610 

2611 if not isrgb: 

2612 pyplot.colorbar() # panchor=(0.55, 0.5), fraction=0.05 

2613 

2614 def format_coord(x, y): 

2615 # callback function to format coordinate display in toolbar 

2616 x = int(x + 0.5) 

2617 y = int(y + 0.5) 

2618 try: 

2619 if dims: 

2620 return "%s @ %s [%4i, %4i]" % (cur_ax_dat[1][y, x], 

2621 current, x, y) 

2622 else: 

2623 return "%s @ [%4i, %4i]" % (data[y, x], x, y) 

2624 except IndexError: 

2625 return "" 

2626 

2627 pyplot.gca().format_coord = format_coord 

2628 

2629 if dims: 

2630 current = list((0, ) * dims) 

2631 cur_ax_dat = [0, data[tuple(current)].squeeze()] 

2632 sliders = [pyplot.Slider( 

2633 pyplot.axes([0.125, 0.03*(axis+1), 0.725, 0.025]), 

2634 'Dimension %i' % axis, 0, data.shape[axis]-1, 0, facecolor='0.5', 

2635 valfmt='%%.0f [%i]' % data.shape[axis]) for axis in range(dims)] 

2636 for slider in sliders: 

2637 slider.drawon = False 

2638 

2639 def set_image(current, sliders=sliders, data=data): 

2640 # change image and redraw canvas 

2641 cur_ax_dat[1] = data[tuple(current)].squeeze() 

2642 image.set_data(cur_ax_dat[1]) 

2643 for ctrl, index in zip(sliders, current): 

2644 ctrl.eventson = False 

2645 ctrl.set_val(index) 

2646 ctrl.eventson = True 

2647 figure.canvas.draw() 

2648 

2649 def on_changed(index, axis, data=data, current=current): 

2650 # callback function for slider change event 

2651 index = int(round(index)) 

2652 cur_ax_dat[0] = axis 

2653 if index == current[axis]: 

2654 return 

2655 if index >= data.shape[axis]: 

2656 index = 0 

2657 elif index < 0: 

2658 index = data.shape[axis] - 1 

2659 current[axis] = index 

2660 set_image(current) 

2661 

2662 def on_keypressed(event, data=data, current=current): 

2663 # callback function for key press event 

2664 key = event.key 

2665 axis = cur_ax_dat[0] 

2666 if str(key) in '0123456789': 

2667 on_changed(key, axis) 

2668 elif key == 'right': 

2669 on_changed(current[axis] + 1, axis) 

2670 elif key == 'left': 

2671 on_changed(current[axis] - 1, axis) 

2672 elif key == 'up': 

2673 cur_ax_dat[0] = 0 if axis == len(data.shape)-1 else axis + 1 

2674 elif key == 'down': 

2675 cur_ax_dat[0] = len(data.shape)-1 if axis == 0 else axis - 1 

2676 elif key == 'end': 

2677 on_changed(data.shape[axis] - 1, axis) 

2678 elif key == 'home': 

2679 on_changed(0, axis) 

2680 

2681 figure.canvas.mpl_connect('key_press_event', on_keypressed) 

2682 for axis, ctrl in enumerate(sliders): 

2683 ctrl.on_changed(lambda k, a=axis: on_changed(k, a)) 

2684 

2685 return figure, subplot, image 

2686 

2687 

2688def _app_show(): 

2689 """Block the GUI. For use as skimage plugin.""" 

2690 pyplot = sys.modules['matplotlib.pyplot'] 

2691 pyplot.show() 

2692 

2693 

2694def main(argv=None): 

2695 """Command line usage main function.""" 

2696 if float(sys.version[0:3]) < 2.6: 

2697 print("This script requires Python version 2.6 or better.") 

2698 print("This is Python version %s" % sys.version) 

2699 return 0 

2700 if argv is None: 

2701 argv = sys.argv 

2702 

2703 import re 

2704 import optparse 

2705 

2706 search_doc = lambda r, d: re.search(r, __doc__).group(1) if __doc__ else d 

2707 parser = optparse.OptionParser( 

2708 usage="usage: %prog [options] path", 

2709 description=search_doc("\n\n([^|]*?)\n\n", ''), 

2710 version="%%prog %s" % search_doc(":Version: (.*)", "Unknown")) 

2711 opt = parser.add_option 

2712 opt('-p', '--page', dest='page', type='int', default=-1, 

2713 help="display single page") 

2714 opt('-s', '--series', dest='series', type='int', default=-1, 

2715 help="display series of pages of same shape") 

2716 opt('--noplot', dest='noplot', action='store_true', default=False, 

2717 help="don't display images") 

2718 opt('--interpol', dest='interpol', metavar='INTERPOL', default='bilinear', 

2719 help="image interpolation method") 

2720 opt('--dpi', dest='dpi', type='int', default=96, 

2721 help="set plot resolution") 

2722 opt('--debug', dest='debug', action='store_true', default=False, 

2723 help="raise exception on failures") 

2724 opt('--test', dest='test', action='store_true', default=False, 

2725 help="try read all images in path") 

2726 opt('--doctest', dest='doctest', action='store_true', default=False, 

2727 help="runs the internal tests") 

2728 opt('-v', '--verbose', dest='verbose', action='store_true', default=True) 

2729 opt('-q', '--quiet', dest='verbose', action='store_false') 

2730 

2731 settings, path = parser.parse_args() 

2732 path = ' '.join(path) 

2733 

2734 if settings.doctest: 

2735 import doctest 

2736 doctest.testmod() 

2737 return 0 

2738 if not path: 

2739 parser.error("No file specified") 

2740 if settings.test: 

2741 test_tifffile(path, settings.verbose) 

2742 return 0 

2743 

2744 print("Reading file structure...", end=' ') 

2745 start = time.time() 

2746 try: 

2747 tif = TIFFfile(path) 

2748 except Exception as e: 

2749 if settings.debug: 

2750 raise 

2751 else: 

2752 print("\n", e) 

2753 sys.exit(0) 

2754 print("%.3f ms" % ((time.time()-start) * 1e3)) 

2755 

2756 if tif.is_ome: 

2757 settings.norgb = True 

2758 

2759 images = [(None, tif[0 if settings.page < 0 else settings.page])] 

2760 if not settings.noplot: 

2761 print("Reading image data... ", end=' ') 

2762 notnone = lambda x: next(i for i in x if i is not None) 

2763 start = time.time() 

2764 try: 

2765 if settings.page >= 0: 

2766 images = [(tif.asarray(key=settings.page), 

2767 tif[settings.page])] 

2768 elif settings.series >= 0: 

2769 images = [(tif.asarray(series=settings.series), 

2770 notnone(tif.series[settings.series].pages))] 

2771 else: 

2772 images = [] 

2773 for i, s in enumerate(tif.series): 

2774 try: 

2775 images.append( 

2776 (tif.asarray(series=i), notnone(s.pages))) 

2777 except ValueError as e: 

2778 images.append((None, notnone(s.pages))) 

2779 if settings.debug: 

2780 raise 

2781 else: 

2782 print("\n* series %i failed: %s... " % (i, e), 

2783 end='') 

2784 print("%.3f ms" % ((time.time()-start) * 1e3)) 

2785 except Exception as e: 

2786 if settings.debug: 

2787 raise 

2788 else: 

2789 print(e) 

2790 tif.close() 

2791 

2792 print("\nTIFF file:", tif) 

2793 print() 

2794 for i, s in enumerate(tif.series): 

2795 print("Series %i" % i) 

2796 print(s) 

2797 print() 

2798 for i, page in images: 

2799 print(page) 

2800 print(page.tags) 

2801 if page.is_palette: 

2802 print("\nColor Map:", page.color_map.shape, page.color_map.dtype) 

2803 for attr in ('cz_lsm_info', 'cz_lsm_scan_information', 

2804 'mm_uic_tags', 'mm_header', 'nih_image_header'): 

2805 if hasattr(page, attr): 

2806 print("", attr.upper(), Record(getattr(page, attr)), sep="\n") 

2807 print() 

2808 

2809 if images and not settings.noplot: 

2810 try: 

2811 import matplotlib 

2812 # matplotlib.use('TkAgg') 

2813 from matplotlib import pyplot 

2814 except ImportError as e: 

2815 warnings.warn("failed to import matplotlib.\n%s" % e) 

2816 else: 

2817 for img, page in images: 

2818 if img is None: 

2819 continue 

2820 vmin, vmax = None, None 

2821 if page.is_stk: 

2822 try: 

2823 vmin = page.mm_uic_tags['min_scale'] 

2824 vmax = page.mm_uic_tags['max_scale'] 

2825 except KeyError: 

2826 pass 

2827 else: 

2828 if vmax <= vmin: 

2829 vmin, vmax = None, None 

2830 title = "%s\n %s" % (str(tif), str(page)) 

2831 imshow(img, title=title, vmin=vmin, vmax=vmax, 

2832 bitspersample=page.bits_per_sample, 

2833 photometric=page.photometric, 

2834 interpolation=settings.interpol, 

2835 dpi=settings.dpi) 

2836 pyplot.show() 

2837 

2838 

2839__version__ = '2012.07.05' 

2840__docformat__ = 'restructuredtext en' 

2841 

2842#if __name__ == "__main__": 

2843# sys.exit(main())