large-image-source-tifffile 1.28.3.dev2__py3-none-any.whl → 1.30.7.dev12__py3-none-any.whl
Sign up to get free protection for your applications and to get access to all the features.
- large_image_source_tifffile/__init__.py +161 -12
- {large_image_source_tifffile-1.28.3.dev2.dist-info → large_image_source_tifffile-1.30.7.dev12.dist-info}/METADATA +18 -4
- large_image_source_tifffile-1.30.7.dev12.dist-info/RECORD +8 -0
- {large_image_source_tifffile-1.28.3.dev2.dist-info → large_image_source_tifffile-1.30.7.dev12.dist-info}/WHEEL +1 -1
- large_image_source_tifffile-1.28.3.dev2.dist-info/RECORD +0 -8
- {large_image_source_tifffile-1.28.3.dev2.dist-info → large_image_source_tifffile-1.30.7.dev12.dist-info}/LICENSE +0 -0
- {large_image_source_tifffile-1.28.3.dev2.dist-info → large_image_source_tifffile-1.30.7.dev12.dist-info}/entry_points.txt +0 -0
- {large_image_source_tifffile-1.28.3.dev2.dist-info → large_image_source_tifffile-1.30.7.dev12.dist-info}/top_level.txt +0 -0
@@ -5,9 +5,9 @@ import os
|
|
5
5
|
import threading
|
6
6
|
from importlib.metadata import PackageNotFoundError
|
7
7
|
from importlib.metadata import version as _importlib_version
|
8
|
+
from pathlib import Path
|
8
9
|
|
9
10
|
import numpy as np
|
10
|
-
import zarr
|
11
11
|
|
12
12
|
import large_image
|
13
13
|
from large_image.cache_util import LruCacheMetaclass, methodcache
|
@@ -16,6 +16,7 @@ from large_image.exceptions import TileSourceError, TileSourceFileNotFoundError
|
|
16
16
|
from large_image.tilesource import FileTileSource
|
17
17
|
|
18
18
|
tifffile = None
|
19
|
+
zarr = None
|
19
20
|
|
20
21
|
try:
|
21
22
|
__version__ = _importlib_version(__name__)
|
@@ -37,6 +38,7 @@ def _lazyImport():
|
|
37
38
|
module initialization because it is slow.
|
38
39
|
"""
|
39
40
|
global tifffile
|
41
|
+
global zarr
|
40
42
|
|
41
43
|
if tifffile is None:
|
42
44
|
try:
|
@@ -55,6 +57,8 @@ def _lazyImport():
|
|
55
57
|
logging.getLogger('tifffile.tifffile').addHandler(checkForMissingDataHandler())
|
56
58
|
logging.getLogger('tifffile').setLevel(logging.WARNING)
|
57
59
|
logging.getLogger('tifffile').addHandler(checkForMissingDataHandler())
|
60
|
+
if zarr is None:
|
61
|
+
import zarr
|
58
62
|
|
59
63
|
|
60
64
|
def et_findall(tag, text):
|
@@ -80,6 +84,7 @@ class TifffileFileTileSource(FileTileSource, metaclass=LruCacheMetaclass):
|
|
80
84
|
'scn': SourcePriority.PREFERRED,
|
81
85
|
'tif': SourcePriority.LOW,
|
82
86
|
'tiff': SourcePriority.LOW,
|
87
|
+
'ome': SourcePriority.HIGHER,
|
83
88
|
}
|
84
89
|
mimeTypes = {
|
85
90
|
None: SourcePriority.FALLBACK,
|
@@ -117,6 +122,7 @@ class TifffileFileTileSource(FileTileSource, metaclass=LruCacheMetaclass):
|
|
117
122
|
raise TileSourceFileNotFoundError(self._largeImagePath) from None
|
118
123
|
msg = 'File cannot be opened via tifffile.'
|
119
124
|
raise TileSourceError(msg)
|
125
|
+
self._checkForOmeBinaryonly()
|
120
126
|
maxseries, maxsamples = self._biggestSeries()
|
121
127
|
self.tileWidth = self.tileHeight = self._tileSize
|
122
128
|
s = self._tf.series[maxseries]
|
@@ -124,6 +130,9 @@ class TifffileFileTileSource(FileTileSource, metaclass=LruCacheMetaclass):
|
|
124
130
|
if len(s.levels) == 1:
|
125
131
|
self.tileWidth = self.tileHeight = self._singleTileSize
|
126
132
|
page = s.pages[0]
|
133
|
+
if not hasattr(page, 'tags'):
|
134
|
+
msg = 'File will not be opened via tifffile.'
|
135
|
+
raise TileSourceError(msg)
|
127
136
|
if ('TileWidth' in page.tags and
|
128
137
|
self._minTileSize <= page.tags['TileWidth'].value <= self._maxTileSize):
|
129
138
|
self.tileWidth = page.tags['TileWidth'].value
|
@@ -134,6 +143,10 @@ class TifffileFileTileSource(FileTileSource, metaclass=LruCacheMetaclass):
|
|
134
143
|
self._iccprofiles = [page.tags['InterColorProfile'].value]
|
135
144
|
self.sizeX = s.shape[s.axes.index('X')]
|
136
145
|
self.sizeY = s.shape[s.axes.index('Y')]
|
146
|
+
while (self.tileWidth // 2 >= self.sizeX and self.tileHeight // 2 >= self.sizeY and
|
147
|
+
min(self.tileWidth, self.tileHeight) // 2 >= self._minTileSize):
|
148
|
+
self.tileWidth //= 2
|
149
|
+
self.tileHeight //= 2
|
137
150
|
self._mm_x = self._mm_y = None
|
138
151
|
try:
|
139
152
|
unit = {2: 25.4, 3: 10}[page.tags['ResolutionUnit'].value.real]
|
@@ -167,6 +180,35 @@ class TifffileFileTileSource(FileTileSource, metaclass=LruCacheMetaclass):
|
|
167
180
|
msg = 'File cannot be opened via tifffile: axes and shape do not match access pattern.'
|
168
181
|
raise TileSourceError(msg)
|
169
182
|
|
183
|
+
def _checkForOmeBinaryonly(self):
|
184
|
+
from xml.etree import ElementTree as etree
|
185
|
+
|
186
|
+
omexml = getattr(self._tf, 'ome_metadata', None)
|
187
|
+
if not omexml:
|
188
|
+
return
|
189
|
+
try:
|
190
|
+
root = etree.fromstring(omexml)
|
191
|
+
except Exception:
|
192
|
+
return
|
193
|
+
metadatafile = None
|
194
|
+
for element in root:
|
195
|
+
if element.tag.endswith('BinaryOnly'):
|
196
|
+
metadatafile = element.attrib.get('MetadataFile', '')
|
197
|
+
if not metadatafile:
|
198
|
+
return
|
199
|
+
path = Path(self._largeImagePath).parent / metadatafile
|
200
|
+
if not path.is_file():
|
201
|
+
return
|
202
|
+
try:
|
203
|
+
newxml = path.open('r').read()
|
204
|
+
except Exception:
|
205
|
+
return
|
206
|
+
try:
|
207
|
+
root = etree.fromstring(newxml)
|
208
|
+
except Exception:
|
209
|
+
return
|
210
|
+
self._tf._omexml = newxml
|
211
|
+
|
170
212
|
def _biggestSeries(self):
|
171
213
|
"""
|
172
214
|
Find the series with the most pixels. Use all series that have the
|
@@ -180,7 +222,7 @@ class TifffileFileTileSource(FileTileSource, metaclass=LruCacheMetaclass):
|
|
180
222
|
ex = 'no maximum series'
|
181
223
|
try:
|
182
224
|
for idx, s in enumerate(self._tf.series):
|
183
|
-
samples =
|
225
|
+
samples = math.prod(s.shape)
|
184
226
|
if samples > maxsamples and 'X' in s.axes and 'Y' in s.axes:
|
185
227
|
maxseries = idx
|
186
228
|
maxsamples = samples
|
@@ -229,7 +271,7 @@ class TifffileFileTileSource(FileTileSource, metaclass=LruCacheMetaclass):
|
|
229
271
|
'sizeX': s.shape[s.axes.index('X')], 'sizeY': s.shape[s.axes.index('Y')]})
|
230
272
|
self.sizeX = max(self.sizeX, s.shape[s.axes.index('X')])
|
231
273
|
self.sizeY = max(self.sizeY, s.shape[s.axes.index('Y')])
|
232
|
-
self._framecount = len(self._series) *
|
274
|
+
self._framecount = len(self._series) * math.prod(tuple(
|
233
275
|
1 if base.axes[sidx] in 'YXS' else v for sidx, v in enumerate(base.shape)))
|
234
276
|
self._basis = {}
|
235
277
|
basis = 1
|
@@ -256,14 +298,17 @@ class TifffileFileTileSource(FileTileSource, metaclass=LruCacheMetaclass):
|
|
256
298
|
for p in self._tf.pages:
|
257
299
|
if (p not in pagesInSeries and getattr(p, 'keyframe', None) is not None and
|
258
300
|
p.hash not in hashes and not len(set(p.axes) - set('YXS'))):
|
259
|
-
|
260
|
-
|
261
|
-
|
262
|
-
|
263
|
-
|
264
|
-
|
265
|
-
|
266
|
-
|
301
|
+
try:
|
302
|
+
id = 'image_%s' % p.index
|
303
|
+
entry = {'page': p.index}
|
304
|
+
entry['width'] = p.shape[p.axes.index('X')]
|
305
|
+
entry['height'] = p.shape[p.axes.index('Y')]
|
306
|
+
if (id not in self._associatedImages and
|
307
|
+
max(entry['width'], entry['height']) <= self._maxAssociatedImageSize and
|
308
|
+
max(entry['width'], entry['height']) >= self._minAssociatedImageSize):
|
309
|
+
self._associatedImages[id] = entry
|
310
|
+
except Exception:
|
311
|
+
pass
|
267
312
|
for sidx, s in enumerate(self._tf.series):
|
268
313
|
if sidx not in self._series and not len(set(s.axes) - set('YXS')):
|
269
314
|
id = 'series_%d' % sidx
|
@@ -286,6 +331,79 @@ class TifffileFileTileSource(FileTileSource, metaclass=LruCacheMetaclass):
|
|
286
331
|
except Exception:
|
287
332
|
pass
|
288
333
|
|
334
|
+
def _handle_indica(self):
|
335
|
+
import xml.etree.ElementTree
|
336
|
+
|
337
|
+
import large_image.tilesource.utilities
|
338
|
+
|
339
|
+
try:
|
340
|
+
root = xml.etree.ElementTree.fromstring(self._tf.pages[0].description)
|
341
|
+
self._xml = large_image.tilesource.utilities.etreeToDict(root)
|
342
|
+
self._channels = [c['name'] for c in
|
343
|
+
self._xml['indica']['image']['channels']['channel']]
|
344
|
+
if len(self._basis) == 1 and 'I' in self._basis:
|
345
|
+
self._basis['C'] = self._basis.pop('I')
|
346
|
+
self._associatedImages.clear()
|
347
|
+
except Exception:
|
348
|
+
pass
|
349
|
+
|
350
|
+
def _handle_ome(self):
|
351
|
+
"""
|
352
|
+
For OME Tiff, if we didn't parse the mangification elsewhere, try to
|
353
|
+
parse it here.
|
354
|
+
"""
|
355
|
+
import xml.etree.ElementTree
|
356
|
+
|
357
|
+
import large_image.tilesource.utilities
|
358
|
+
|
359
|
+
_omeUnitsToMeters = {
|
360
|
+
'Ym': 1e24,
|
361
|
+
'Zm': 1e21,
|
362
|
+
'Em': 1e18,
|
363
|
+
'Pm': 1e15,
|
364
|
+
'Tm': 1e12,
|
365
|
+
'Gm': 1e9,
|
366
|
+
'Mm': 1e6,
|
367
|
+
'km': 1e3,
|
368
|
+
'hm': 1e2,
|
369
|
+
'dam': 1e1,
|
370
|
+
'm': 1,
|
371
|
+
'dm': 1e-1,
|
372
|
+
'cm': 1e-2,
|
373
|
+
'mm': 1e-3,
|
374
|
+
'\u00b5m': 1e-6,
|
375
|
+
'nm': 1e-9,
|
376
|
+
'pm': 1e-12,
|
377
|
+
'fm': 1e-15,
|
378
|
+
'am': 1e-18,
|
379
|
+
'zm': 1e-21,
|
380
|
+
'ym': 1e-24,
|
381
|
+
'\u00c5': 1e-10,
|
382
|
+
}
|
383
|
+
|
384
|
+
try:
|
385
|
+
root = xml.etree.ElementTree.fromstring(self._tf.pages[0].description)
|
386
|
+
self._xml = large_image.tilesource.utilities.etreeToDict(root)
|
387
|
+
except Exception:
|
388
|
+
return
|
389
|
+
try:
|
390
|
+
try:
|
391
|
+
base = self._xml['OME']['Image'][0]['Pixels']
|
392
|
+
except Exception:
|
393
|
+
base = self._xml['OME']['Image']['Pixels']
|
394
|
+
if self._mm_x is None and 'PhysicalSizeX' in base:
|
395
|
+
self._mm_x = (
|
396
|
+
float(base['PhysicalSizeX']) * 1e3 *
|
397
|
+
_omeUnitsToMeters[base.get('PhysicalSizeXUnit', '\u00b5m')])
|
398
|
+
if self._mm_y is None and 'PhysicalSizeY' in base:
|
399
|
+
self._mm_y = (
|
400
|
+
float(base['PhysicalSizeY']) * 1e3 *
|
401
|
+
_omeUnitsToMeters[base.get('PhysicalSizeYUnit', '\u00b5m')])
|
402
|
+
self._mm_x = self._mm_x or self._mm_y
|
403
|
+
self._mm_y = self._mm_y or self._mm_x
|
404
|
+
except Exception:
|
405
|
+
pass
|
406
|
+
|
289
407
|
def _handle_scn(self): # noqa
|
290
408
|
"""
|
291
409
|
For SCN files, parse the xml and possibly adjust how associated images
|
@@ -351,6 +469,20 @@ class TifffileFileTileSource(FileTileSource, metaclass=LruCacheMetaclass):
|
|
351
469
|
except Exception:
|
352
470
|
pass
|
353
471
|
|
472
|
+
def _handle_internal_ndpi(self, intmeta):
|
473
|
+
try:
|
474
|
+
ndpi = intmeta.pop('65449')
|
475
|
+
intmeta['ndpi'] = {}
|
476
|
+
for line in ndpi.replace('\r', '\n').split('\n'):
|
477
|
+
if '=' in line:
|
478
|
+
key, value = line.split('=', 1)
|
479
|
+
key = key.strip()
|
480
|
+
value = value.strip()
|
481
|
+
if key and key not in intmeta['ndpi'] and value:
|
482
|
+
intmeta['ndpi'][key] = value
|
483
|
+
except Exception:
|
484
|
+
pass
|
485
|
+
|
354
486
|
def getNativeMagnification(self):
|
355
487
|
"""
|
356
488
|
Get the magnification at a particular level.
|
@@ -420,6 +552,10 @@ class TifffileFileTileSource(FileTileSource, metaclass=LruCacheMetaclass):
|
|
420
552
|
json.dumps(result[key][subkey])
|
421
553
|
except Exception:
|
422
554
|
del result[key][subkey]
|
555
|
+
for key in dir(self._tf):
|
556
|
+
if (key.startswith('is_') and hasattr(self, '_handle_internal_' + key[3:]) and
|
557
|
+
getattr(self._tf, key)):
|
558
|
+
getattr(self, '_handle_internal_' + key[3:])(result)
|
423
559
|
if hasattr(self, '_xml') and 'xml' not in result:
|
424
560
|
result.pop('ImageDescription', None)
|
425
561
|
result['xml'] = self._xml
|
@@ -490,6 +626,17 @@ class TifffileFileTileSource(FileTileSource, metaclass=LruCacheMetaclass):
|
|
490
626
|
self._nonempty_levels_list[frame] = nonempty
|
491
627
|
return nonempty
|
492
628
|
|
629
|
+
def getPreferredLevel(self, level):
|
630
|
+
"""
|
631
|
+
Given a desired level (0 is minimum resolution, self.levels - 1 is max
|
632
|
+
resolution), return the level that contains actual data that is no
|
633
|
+
lower resolution.
|
634
|
+
|
635
|
+
:param level: desired level
|
636
|
+
:returns level: a level with actual data that is no lower resolution.
|
637
|
+
"""
|
638
|
+
return max(0, min(level, self.levels - 1))
|
639
|
+
|
493
640
|
def _getZarrArray(self, series, sidx):
|
494
641
|
with self._zarrlock:
|
495
642
|
if sidx not in self._zarrcache:
|
@@ -533,7 +680,7 @@ class TifffileFileTileSource(FileTileSource, metaclass=LruCacheMetaclass):
|
|
533
680
|
else:
|
534
681
|
bza = za
|
535
682
|
if step > 2 ** self._maxSkippedLevels:
|
536
|
-
tile = self._getTileFromEmptyLevel(x, y, z, **kwargs)
|
683
|
+
tile, _format = self._getTileFromEmptyLevel(x, y, z, **kwargs)
|
537
684
|
tile = large_image.tilesource.base._imageToNumpy(tile)[0]
|
538
685
|
else:
|
539
686
|
sel = []
|
@@ -549,6 +696,8 @@ class TifffileFileTileSource(FileTileSource, metaclass=LruCacheMetaclass):
|
|
549
696
|
sel.append(slice(series.shape[aidx]))
|
550
697
|
baxis += 'S'
|
551
698
|
else:
|
699
|
+
if axis not in self._basis and axis == 'I':
|
700
|
+
axis = 'C'
|
552
701
|
sel.append((frame // self._basis[axis][0]) % self._basis[axis][2])
|
553
702
|
tile = bza[tuple(sel)]
|
554
703
|
# rotate
|
@@ -1,6 +1,6 @@
|
|
1
|
-
Metadata-Version: 2.
|
1
|
+
Metadata-Version: 2.2
|
2
2
|
Name: large-image-source-tifffile
|
3
|
-
Version: 1.
|
3
|
+
Version: 1.30.7.dev12
|
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
|
22
|
+
Requires-Dist: large-image>=1.30.7.dev12
|
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
|
27
|
+
Requires-Dist: girder-large-image>=1.30.7.dev12; 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=WsyPJKR2Iij1V7maAfv4-m1xEhEUHLlRsO3qOK9IIWc,30244
|
2
|
+
large_image_source_tifffile/girder_source.py,sha256=8YsxpSK_UzbAcpjn6fIMlgKye2FchkB8_HTSQV0FpRA,1064
|
3
|
+
large_image_source_tifffile-1.30.7.dev12.dist-info/LICENSE,sha256=psuoW8kuDP96RQsdhzwOqi6fyWv0ct8CR6Jr7He_P_k,10173
|
4
|
+
large_image_source_tifffile-1.30.7.dev12.dist-info/METADATA,sha256=DwwJpYFWMtn8Tp7_mYPoplHzoqmMpoZ43B9xK7nZuZc,1405
|
5
|
+
large_image_source_tifffile-1.30.7.dev12.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
|
6
|
+
large_image_source_tifffile-1.30.7.dev12.dist-info/entry_points.txt,sha256=aILCN5f7-Zj8-WRXcABxCQvqJv9VLTvqRhZ_7HEyokc,190
|
7
|
+
large_image_source_tifffile-1.30.7.dev12.dist-info/top_level.txt,sha256=feOAsix2Rzy6mjy2Ua-N0-L6tlKsgvLdRhO6Hd8_ZFs,28
|
8
|
+
large_image_source_tifffile-1.30.7.dev12.dist-info/RECORD,,
|
@@ -1,8 +0,0 @@
|
|
1
|
-
large_image_source_tifffile/__init__.py,sha256=7hREWGbhyYOcz94l_vxMAOTUSJKtJY37TEIZdkvamNw,25001
|
2
|
-
large_image_source_tifffile/girder_source.py,sha256=8YsxpSK_UzbAcpjn6fIMlgKye2FchkB8_HTSQV0FpRA,1064
|
3
|
-
large_image_source_tifffile-1.28.3.dev2.dist-info/LICENSE,sha256=psuoW8kuDP96RQsdhzwOqi6fyWv0ct8CR6Jr7He_P_k,10173
|
4
|
-
large_image_source_tifffile-1.28.3.dev2.dist-info/METADATA,sha256=2XRfComEY1H1fO7m6luWIfzukgRCnPmPJw6RVkiDpZw,1061
|
5
|
-
large_image_source_tifffile-1.28.3.dev2.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
|
6
|
-
large_image_source_tifffile-1.28.3.dev2.dist-info/entry_points.txt,sha256=aILCN5f7-Zj8-WRXcABxCQvqJv9VLTvqRhZ_7HEyokc,190
|
7
|
-
large_image_source_tifffile-1.28.3.dev2.dist-info/top_level.txt,sha256=feOAsix2Rzy6mjy2Ua-N0-L6tlKsgvLdRhO6Hd8_ZFs,28
|
8
|
-
large_image_source_tifffile-1.28.3.dev2.dist-info/RECORD,,
|
File without changes
|
File without changes
|