large-image-source-tifffile 1.27.5.dev6__py3-none-any.whl → 1.30.7.dev10__py3-none-any.whl

Sign up to get free protection for your applications and to get access to all the features.
@@ -7,7 +7,6 @@ from importlib.metadata import PackageNotFoundError
7
7
  from importlib.metadata import version as _importlib_version
8
8
 
9
9
  import numpy as np
10
- import zarr
11
10
 
12
11
  import large_image
13
12
  from large_image.cache_util import LruCacheMetaclass, methodcache
@@ -16,6 +15,7 @@ from large_image.exceptions import TileSourceError, TileSourceFileNotFoundError
16
15
  from large_image.tilesource import FileTileSource
17
16
 
18
17
  tifffile = None
18
+ zarr = None
19
19
 
20
20
  try:
21
21
  __version__ = _importlib_version(__name__)
@@ -37,6 +37,7 @@ def _lazyImport():
37
37
  module initialization because it is slow.
38
38
  """
39
39
  global tifffile
40
+ global zarr
40
41
 
41
42
  if tifffile is None:
42
43
  try:
@@ -55,6 +56,8 @@ def _lazyImport():
55
56
  logging.getLogger('tifffile.tifffile').addHandler(checkForMissingDataHandler())
56
57
  logging.getLogger('tifffile').setLevel(logging.WARNING)
57
58
  logging.getLogger('tifffile').addHandler(checkForMissingDataHandler())
59
+ if zarr is None:
60
+ import zarr
58
61
 
59
62
 
60
63
  def et_findall(tag, text):
@@ -109,6 +112,7 @@ class TifffileFileTileSource(FileTileSource, metaclass=LruCacheMetaclass):
109
112
  self._largeImagePath = str(self._getLargeImagePath())
110
113
 
111
114
  _lazyImport()
115
+ self.addKnownExtensions()
112
116
  try:
113
117
  self._tf = tifffile.TiffFile(self._largeImagePath)
114
118
  except Exception:
@@ -179,7 +183,7 @@ class TifffileFileTileSource(FileTileSource, metaclass=LruCacheMetaclass):
179
183
  ex = 'no maximum series'
180
184
  try:
181
185
  for idx, s in enumerate(self._tf.series):
182
- samples = np.prod(s.shape)
186
+ samples = math.prod(s.shape)
183
187
  if samples > maxsamples and 'X' in s.axes and 'Y' in s.axes:
184
188
  maxseries = idx
185
189
  maxsamples = samples
@@ -228,7 +232,7 @@ class TifffileFileTileSource(FileTileSource, metaclass=LruCacheMetaclass):
228
232
  'sizeX': s.shape[s.axes.index('X')], 'sizeY': s.shape[s.axes.index('Y')]})
229
233
  self.sizeX = max(self.sizeX, s.shape[s.axes.index('X')])
230
234
  self.sizeY = max(self.sizeY, s.shape[s.axes.index('Y')])
231
- self._framecount = len(self._series) * np.prod(tuple(
235
+ self._framecount = len(self._series) * math.prod(tuple(
232
236
  1 if base.axes[sidx] in 'YXS' else v for sidx, v in enumerate(base.shape)))
233
237
  self._basis = {}
234
238
  basis = 1
@@ -255,14 +259,17 @@ class TifffileFileTileSource(FileTileSource, metaclass=LruCacheMetaclass):
255
259
  for p in self._tf.pages:
256
260
  if (p not in pagesInSeries and getattr(p, 'keyframe', None) is not None and
257
261
  p.hash not in hashes and not len(set(p.axes) - set('YXS'))):
258
- id = 'image_%s' % p.index
259
- entry = {'page': p.index}
260
- entry['width'] = p.shape[p.axes.index('X')]
261
- entry['height'] = p.shape[p.axes.index('Y')]
262
- if (id not in self._associatedImages and
263
- max(entry['width'], entry['height']) <= self._maxAssociatedImageSize and
264
- max(entry['width'], entry['height']) >= self._minAssociatedImageSize):
265
- self._associatedImages[id] = entry
262
+ try:
263
+ id = 'image_%s' % p.index
264
+ entry = {'page': p.index}
265
+ entry['width'] = p.shape[p.axes.index('X')]
266
+ entry['height'] = p.shape[p.axes.index('Y')]
267
+ if (id not in self._associatedImages and
268
+ max(entry['width'], entry['height']) <= self._maxAssociatedImageSize and
269
+ max(entry['width'], entry['height']) >= self._minAssociatedImageSize):
270
+ self._associatedImages[id] = entry
271
+ except Exception:
272
+ pass
266
273
  for sidx, s in enumerate(self._tf.series):
267
274
  if sidx not in self._series and not len(set(s.axes) - set('YXS')):
268
275
  id = 'series_%d' % sidx
@@ -285,6 +292,79 @@ class TifffileFileTileSource(FileTileSource, metaclass=LruCacheMetaclass):
285
292
  except Exception:
286
293
  pass
287
294
 
295
+ def _handle_indica(self):
296
+ import xml.etree.ElementTree
297
+
298
+ import large_image.tilesource.utilities
299
+
300
+ try:
301
+ root = xml.etree.ElementTree.fromstring(self._tf.pages[0].description)
302
+ self._xml = large_image.tilesource.utilities.etreeToDict(root)
303
+ self._channels = [c['name'] for c in
304
+ self._xml['indica']['image']['channels']['channel']]
305
+ if len(self._basis) == 1 and 'I' in self._basis:
306
+ self._basis['C'] = self._basis.pop('I')
307
+ self._associatedImages.clear()
308
+ except Exception:
309
+ pass
310
+
311
+ def _handle_ome(self):
312
+ """
313
+ For OME Tiff, if we didn't parse the mangification elsewhere, try to
314
+ parse it here.
315
+ """
316
+ import xml.etree.ElementTree
317
+
318
+ import large_image.tilesource.utilities
319
+
320
+ _omeUnitsToMeters = {
321
+ 'Ym': 1e24,
322
+ 'Zm': 1e21,
323
+ 'Em': 1e18,
324
+ 'Pm': 1e15,
325
+ 'Tm': 1e12,
326
+ 'Gm': 1e9,
327
+ 'Mm': 1e6,
328
+ 'km': 1e3,
329
+ 'hm': 1e2,
330
+ 'dam': 1e1,
331
+ 'm': 1,
332
+ 'dm': 1e-1,
333
+ 'cm': 1e-2,
334
+ 'mm': 1e-3,
335
+ '\u00b5m': 1e-6,
336
+ 'nm': 1e-9,
337
+ 'pm': 1e-12,
338
+ 'fm': 1e-15,
339
+ 'am': 1e-18,
340
+ 'zm': 1e-21,
341
+ 'ym': 1e-24,
342
+ '\u00c5': 1e-10,
343
+ }
344
+
345
+ try:
346
+ root = xml.etree.ElementTree.fromstring(self._tf.pages[0].description)
347
+ self._xml = large_image.tilesource.utilities.etreeToDict(root)
348
+ except Exception:
349
+ return
350
+ try:
351
+ try:
352
+ base = self._xml['OME']['Image'][0]['Pixels']
353
+ except Exception:
354
+ base = self._xml['OME']['Image']['Pixels']
355
+ if self._mm_x is None and 'PhysicalSizeX' in base:
356
+ self._mm_x = (
357
+ float(base['PhysicalSizeX']) * 1e3 *
358
+ _omeUnitsToMeters[base.get('PhysicalSizeXUnit', '\u00b5m')])
359
+ if self._mm_y is None and 'PhysicalSizeY' in base:
360
+ self._mm_y = (
361
+ float(base['PhysicalSizeY']) * 1e3 *
362
+ _omeUnitsToMeters[base.get('PhysicalSizeYUnit', '\u00b5m')])
363
+ self._mm_x = self._mm_x or self._mm_y
364
+ self._mm_y = self._mm_y or self._mm_x
365
+ except Exception:
366
+ pass
367
+
288
368
  def _handle_scn(self): # noqa
289
369
  """
290
370
  For SCN files, parse the xml and possibly adjust how associated images
@@ -350,6 +430,20 @@ class TifffileFileTileSource(FileTileSource, metaclass=LruCacheMetaclass):
350
430
  except Exception:
351
431
  pass
352
432
 
433
+ def _handle_internal_ndpi(self, intmeta):
434
+ try:
435
+ ndpi = intmeta.pop('65449')
436
+ intmeta['ndpi'] = {}
437
+ for line in ndpi.replace('\r', '\n').split('\n'):
438
+ if '=' in line:
439
+ key, value = line.split('=', 1)
440
+ key = key.strip()
441
+ value = value.strip()
442
+ if key and key not in intmeta['ndpi'] and value:
443
+ intmeta['ndpi'][key] = value
444
+ except Exception:
445
+ pass
446
+
353
447
  def getNativeMagnification(self):
354
448
  """
355
449
  Get the magnification at a particular level.
@@ -419,6 +513,10 @@ class TifffileFileTileSource(FileTileSource, metaclass=LruCacheMetaclass):
419
513
  json.dumps(result[key][subkey])
420
514
  except Exception:
421
515
  del result[key][subkey]
516
+ for key in dir(self._tf):
517
+ if (key.startswith('is_') and hasattr(self, '_handle_internal_' + key[3:]) and
518
+ getattr(self._tf, key)):
519
+ getattr(self, '_handle_internal_' + key[3:])(result)
422
520
  if hasattr(self, '_xml') and 'xml' not in result:
423
521
  result.pop('ImageDescription', None)
424
522
  result['xml'] = self._xml
@@ -473,19 +571,12 @@ class TifffileFileTileSource(FileTileSource, metaclass=LruCacheMetaclass):
473
571
  sidx = frame // self._basis['P'][0]
474
572
  else:
475
573
  sidx = 0
476
- series = self._tf.series[self._series[sidx]]
477
574
  nonempty = [None] * self.levels
478
575
  nonempty[self.levels - 1] = True
576
+ series = self._tf.series[self._series[sidx]]
577
+ za, hasgbs = self._getZarrArray(series, sidx)
479
578
  xidx = series.axes.index('X')
480
579
  yidx = series.axes.index('Y')
481
- with self._zarrlock:
482
- if sidx not in self._zarrcache:
483
- if len(self._zarrcache) > 10:
484
- self._zarrcache = {}
485
- za = zarr.open(series.aszarr(), mode='r')
486
- hasgbs = hasattr(za[0], 'get_basic_selection')
487
- self._zarrcache[sidx] = (za, hasgbs)
488
- za, hasgbs = self._zarrcache[sidx]
489
580
  for ll in range(1, len(series.levels)):
490
581
  scale = round(math.log(max(za[0].shape[xidx] / za[ll].shape[xidx],
491
582
  za[0].shape[yidx] / za[ll].shape[yidx])) / math.log(2))
@@ -496,6 +587,30 @@ class TifffileFileTileSource(FileTileSource, metaclass=LruCacheMetaclass):
496
587
  self._nonempty_levels_list[frame] = nonempty
497
588
  return nonempty
498
589
 
590
+ def getPreferredLevel(self, level):
591
+ """
592
+ Given a desired level (0 is minimum resolution, self.levels - 1 is max
593
+ resolution), return the level that contains actual data that is no
594
+ lower resolution.
595
+
596
+ :param level: desired level
597
+ :returns level: a level with actual data that is no lower resolution.
598
+ """
599
+ return max(0, min(level, self.levels - 1))
600
+
601
+ def _getZarrArray(self, series, sidx):
602
+ with self._zarrlock:
603
+ if sidx not in self._zarrcache:
604
+ if len(self._zarrcache) > 10:
605
+ self._zarrcache = {}
606
+ za = zarr.open(series.aszarr(), mode='r')
607
+ hasgbs = hasattr(za[0], 'get_basic_selection')
608
+ if not hasgbs and math.prod(series.shape) < 256 * 1024 ** 2:
609
+ za = series.asarray()
610
+ self._zarrcache[sidx] = (za, hasgbs)
611
+ za, hasgbs = self._zarrcache[sidx]
612
+ return za, hasgbs
613
+
499
614
  @methodcache()
500
615
  def getTile(self, x, y, z, pilImageAllowed=False, numpyAllowed=False, **kwargs):
501
616
  frame = self._getFrame(**kwargs)
@@ -506,14 +621,7 @@ class TifffileFileTileSource(FileTileSource, metaclass=LruCacheMetaclass):
506
621
  else:
507
622
  sidx = 0
508
623
  series = self._tf.series[self._series[sidx]]
509
- with self._zarrlock:
510
- if sidx not in self._zarrcache:
511
- if len(self._zarrcache) > 10:
512
- self._zarrcache = {}
513
- za = zarr.open(series.aszarr(), mode='r')
514
- hasgbs = hasattr(za[0], 'get_basic_selection')
515
- self._zarrcache[sidx] = (za, hasgbs)
516
- za, hasgbs = self._zarrcache[sidx]
624
+ za, hasgbs = self._getZarrArray(series, sidx)
517
625
  xidx = series.axes.index('X')
518
626
  yidx = series.axes.index('Y')
519
627
  if hasgbs:
@@ -533,7 +641,7 @@ class TifffileFileTileSource(FileTileSource, metaclass=LruCacheMetaclass):
533
641
  else:
534
642
  bza = za
535
643
  if step > 2 ** self._maxSkippedLevels:
536
- tile = self._getTileFromEmptyLevel(x, y, z, **kwargs)
644
+ tile, _format = self._getTileFromEmptyLevel(x, y, z, **kwargs)
537
645
  tile = large_image.tilesource.base._imageToNumpy(tile)[0]
538
646
  else:
539
647
  sel = []
@@ -549,6 +657,8 @@ class TifffileFileTileSource(FileTileSource, metaclass=LruCacheMetaclass):
549
657
  sel.append(slice(series.shape[aidx]))
550
658
  baxis += 'S'
551
659
  else:
660
+ if axis not in self._basis and axis == 'I':
661
+ axis = 'C'
552
662
  sel.append((frame // self._basis[axis][0]) % self._basis[axis][2])
553
663
  tile = bza[tuple(sel)]
554
664
  # rotate
@@ -558,6 +668,16 @@ class TifffileFileTileSource(FileTileSource, metaclass=LruCacheMetaclass):
558
668
  return self._outputTile(tile, TILE_FORMAT_NUMPY, x, y, z,
559
669
  pilImageAllowed, numpyAllowed, **kwargs)
560
670
 
671
+ @classmethod
672
+ def addKnownExtensions(cls):
673
+ if not hasattr(cls, '_addedExtensions'):
674
+ _lazyImport()
675
+ cls._addedExtensions = True
676
+ cls.extensions = cls.extensions.copy()
677
+ for ext in tifffile.TIFF.FILE_EXTENSIONS:
678
+ if ext not in cls.extensions:
679
+ cls.extensions[ext] = SourcePriority.IMPLICIT
680
+
561
681
 
562
682
  def open(*args, **kwargs):
563
683
  """
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.1
1
+ Metadata-Version: 2.2
2
2
  Name: large-image-source-tifffile
3
- Version: 1.27.5.dev6
3
+ Version: 1.30.7.dev10
4
4
  Summary: A tifffile tilesource for large_image.
5
5
  Home-page: https://github.com/girder/large_image
6
6
  Author: Kitware, Inc.
@@ -15,14 +15,28 @@ Classifier: Programming Language :: Python :: 3.9
15
15
  Classifier: Programming Language :: Python :: 3.10
16
16
  Classifier: Programming Language :: Python :: 3.11
17
17
  Classifier: Programming Language :: Python :: 3.12
18
+ Classifier: Programming Language :: Python :: 3.13
18
19
  Requires-Python: >=3.8
20
+ Description-Content-Type: text/x-rst
19
21
  License-File: LICENSE
20
- Requires-Dist: large-image >=1.27.5.dev6
22
+ Requires-Dist: large-image>=1.30.7.dev10
21
23
  Requires-Dist: dask[array]
22
24
  Requires-Dist: tifffile[all]
23
25
  Requires-Dist: zarr
24
26
  Provides-Extra: girder
25
- Requires-Dist: girder-large-image >=1.27.5.dev6 ; extra == 'girder'
27
+ Requires-Dist: girder-large-image>=1.30.7.dev10; extra == "girder"
28
+ Dynamic: author
29
+ Dynamic: author-email
30
+ Dynamic: classifier
31
+ Dynamic: description
32
+ Dynamic: description-content-type
33
+ Dynamic: home-page
34
+ Dynamic: keywords
35
+ Dynamic: license
36
+ Dynamic: provides-extra
37
+ Dynamic: requires-dist
38
+ Dynamic: requires-python
39
+ Dynamic: summary
26
40
 
27
41
  A tifffile tilesource for large_image.
28
42
 
@@ -0,0 +1,8 @@
1
+ large_image_source_tifffile/__init__.py,sha256=vU1bFrCyklocQjzwiwiAITwLm0sfk-TnyDlIAwxqFeA,28899
2
+ large_image_source_tifffile/girder_source.py,sha256=8YsxpSK_UzbAcpjn6fIMlgKye2FchkB8_HTSQV0FpRA,1064
3
+ large_image_source_tifffile-1.30.7.dev10.dist-info/LICENSE,sha256=psuoW8kuDP96RQsdhzwOqi6fyWv0ct8CR6Jr7He_P_k,10173
4
+ large_image_source_tifffile-1.30.7.dev10.dist-info/METADATA,sha256=ESkktkSCBeCvqOXMH5oFMgUnce9s94mAc3CkHO2o-V4,1405
5
+ large_image_source_tifffile-1.30.7.dev10.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
6
+ large_image_source_tifffile-1.30.7.dev10.dist-info/entry_points.txt,sha256=aILCN5f7-Zj8-WRXcABxCQvqJv9VLTvqRhZ_7HEyokc,190
7
+ large_image_source_tifffile-1.30.7.dev10.dist-info/top_level.txt,sha256=feOAsix2Rzy6mjy2Ua-N0-L6tlKsgvLdRhO6Hd8_ZFs,28
8
+ large_image_source_tifffile-1.30.7.dev10.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: bdist_wheel (0.43.0)
2
+ Generator: setuptools (75.8.0)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5
 
@@ -1,8 +0,0 @@
1
- large_image_source_tifffile/__init__.py,sha256=7jHLRfFXszTDfTcD6aRylJthE03lK5FCHh4rKkHtHEw,24668
2
- large_image_source_tifffile/girder_source.py,sha256=8YsxpSK_UzbAcpjn6fIMlgKye2FchkB8_HTSQV0FpRA,1064
3
- large_image_source_tifffile-1.27.5.dev6.dist-info/LICENSE,sha256=psuoW8kuDP96RQsdhzwOqi6fyWv0ct8CR6Jr7He_P_k,10173
4
- large_image_source_tifffile-1.27.5.dev6.dist-info/METADATA,sha256=8nBvBa8UIknq6Y8vtuL_hKFRzWg9ne44VyqOsfA9M2U,1061
5
- large_image_source_tifffile-1.27.5.dev6.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
6
- large_image_source_tifffile-1.27.5.dev6.dist-info/entry_points.txt,sha256=aILCN5f7-Zj8-WRXcABxCQvqJv9VLTvqRhZ_7HEyokc,190
7
- large_image_source_tifffile-1.27.5.dev6.dist-info/top_level.txt,sha256=feOAsix2Rzy6mjy2Ua-N0-L6tlKsgvLdRhO6Hd8_ZFs,28
8
- large_image_source_tifffile-1.27.5.dev6.dist-info/RECORD,,