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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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,,