large-image-source-openslide 1.30.3.dev35__py3-none-any.whl → 1.33.6a165__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.
@@ -20,6 +20,7 @@ import os
20
20
  from importlib.metadata import PackageNotFoundError
21
21
  from importlib.metadata import version as _importlib_version
22
22
 
23
+ import numpy as np
23
24
  import openslide
24
25
  import PIL
25
26
  import tifftools
@@ -47,12 +48,13 @@ class OpenslideFileTileSource(FileTileSource, metaclass=LruCacheMetaclass):
47
48
  extensions = {
48
49
  None: SourcePriority.MEDIUM,
49
50
  'bif': SourcePriority.LOW, # Ventana
50
- 'dcm': SourcePriority.LOW, # DICOM
51
+ 'czi': SourcePriority.PREFERRED,
52
+ 'dcm': SourcePriority.MEDIUM, # DICOM
51
53
  'ini': SourcePriority.LOW, # Part of mrxs
52
54
  'mrxs': SourcePriority.PREFERRED, # MIRAX
53
55
  'ndpi': SourcePriority.PREFERRED, # Hamamatsu
54
56
  'scn': SourcePriority.LOW, # Leica
55
- 'svs': SourcePriority.PREFERRED,
57
+ 'svs': SourcePriority.HIGH,
56
58
  'svslide': SourcePriority.PREFERRED,
57
59
  'tif': SourcePriority.MEDIUM,
58
60
  'tiff': SourcePriority.MEDIUM,
@@ -61,6 +63,8 @@ class OpenslideFileTileSource(FileTileSource, metaclass=LruCacheMetaclass):
61
63
  }
62
64
  mimeTypes = {
63
65
  None: SourcePriority.FALLBACK,
66
+ 'image/czi': SourcePriority.PREFERRED,
67
+ 'application/dicom': SourcePriority.MEDIUM,
64
68
  'image/mirax': SourcePriority.PREFERRED, # MIRAX
65
69
  'image/tiff': SourcePriority.MEDIUM,
66
70
  'image/x-tiff': SourcePriority.MEDIUM,
@@ -94,6 +98,13 @@ class OpenslideFileTileSource(FileTileSource, metaclass=LruCacheMetaclass):
94
98
  tifftools.Tag.ICCProfile.value]['data']]
95
99
  except Exception:
96
100
  pass
101
+ if hasattr(self, '_tiffinfo'):
102
+ for ifd in self._tiffinfo['ifds']:
103
+ if (tifftools.Tag.NDPI_FOCAL_PLANE.value in ifd['tags'] and
104
+ ifd['tags'][tifftools.Tag.NDPI_FOCAL_PLANE.value]['data'][0] != 0):
105
+ msg = ('File will not be opened via OpenSlide; '
106
+ 'non-zero focal planes would be missed.')
107
+ raise TileSourceError(msg)
97
108
 
98
109
  svsAvailableLevels = self._getAvailableLevels(self._largeImagePath)
99
110
  if not len(svsAvailableLevels):
@@ -128,19 +139,22 @@ class OpenslideFileTileSource(FileTileSource, metaclass=LruCacheMetaclass):
128
139
  # load an appropriate area and scale it to the tile size later.
129
140
  maxSize = 16384 # This should probably be based on available memory
130
141
  for level in range(self.levels):
131
- levelW = max(1, self.sizeX / 2 ** (self.levels - 1 - level))
132
- levelH = max(1, self.sizeY / 2 ** (self.levels - 1 - level))
142
+ levelpow = 2 ** (self.levels - 1 - level)
143
+ levelW = max(1, self.sizeX / levelpow)
144
+ levelH = max(1, self.sizeY / levelpow)
133
145
  # bestlevel and scale will be the picked svs level and the scale
134
146
  # between that level and what we really wanted. We expect scale to
135
147
  # always be a positive integer power of two.
136
148
  bestlevel = svsAvailableLevels[0]['level']
137
149
  scale = 1
150
+ svsscale = 0
138
151
  for svslevel in range(len(svsAvailableLevels)):
139
152
  if (svsAvailableLevels[svslevel]['width'] < levelW - 1 or
140
153
  svsAvailableLevels[svslevel]['height'] < levelH - 1):
141
154
  break
142
155
  bestlevel = svsAvailableLevels[svslevel]['level']
143
156
  scale = int(round(svsAvailableLevels[svslevel]['width'] / levelW))
157
+ svsscale = svsAvailableLevels[svslevel].get('downsample', 0)
144
158
  # If there are no tiles at a particular level, we have to read a
145
159
  # larger area of a higher resolution level. If such an area would
146
160
  # be excessively large, we could have memory issues, so raise an
@@ -154,6 +168,7 @@ class OpenslideFileTileSource(FileTileSource, metaclass=LruCacheMetaclass):
154
168
  self._svslevels.append({
155
169
  'svslevel': bestlevel,
156
170
  'scale': scale,
171
+ 'svsscale': ((svsscale / levelpow) if svsscale else 1) * scale,
157
172
  })
158
173
  self._bounds = None
159
174
  try:
@@ -179,6 +194,12 @@ class OpenslideFileTileSource(FileTileSource, metaclass=LruCacheMetaclass):
179
194
  self._svslevels = self._svslevels[prevlevels - self.levels:]
180
195
  except Exception:
181
196
  pass
197
+ try:
198
+ self._background = tuple(int(
199
+ self._openslide.properties['openslide.background-color']
200
+ [i * 2:i * 2 + 2], 16) for i in range(3))
201
+ except Exception:
202
+ self._background = None
182
203
  self._populatedLevels = len({l['svslevel'] for l in self._svslevels})
183
204
 
184
205
  def _getTileSize(self):
@@ -228,6 +249,7 @@ class OpenslideFileTileSource(FileTileSource, metaclass=LruCacheMetaclass):
228
249
  """
229
250
  levels = []
230
251
  svsLevelDimensions = self._openslide.level_dimensions
252
+
231
253
  for svslevel in range(len(svsLevelDimensions)):
232
254
  try:
233
255
  self._openslide.read_region((0, 0), svslevel, (1, 1))
@@ -236,6 +258,10 @@ class OpenslideFileTileSource(FileTileSource, metaclass=LruCacheMetaclass):
236
258
  'width': svsLevelDimensions[svslevel][0],
237
259
  'height': svsLevelDimensions[svslevel][1],
238
260
  }
261
+ try:
262
+ level['downsample'] = self._openslide.level_downsamples[svslevel]
263
+ except Exception:
264
+ pass
239
265
  if level['width'] > 0 and level['height'] > 0:
240
266
  # add to the list so that we can sort by resolution and
241
267
  # then by earlier entries
@@ -327,12 +353,31 @@ class OpenslideFileTileSource(FileTileSource, metaclass=LruCacheMetaclass):
327
353
  tile, format = self._getTileFromEmptyLevel(x, y, z, **kwargs)
328
354
  else:
329
355
  retries = 3
356
+ svsTileWidth = self.tileWidth * svslevel['scale']
357
+ svsTileHeight = self.tileHeight * svslevel['scale']
358
+ # Peculiarly, openslide has a "downsample" factor which isn't the
359
+ # power of 2 one would expect. This is computed based on the
360
+ # actual dimensions of levels, but since higher-resolution levels
361
+ # are not fully populated at the right and bottom, this ends up
362
+ # with not the actual downsampling, but some slightly higher number
363
+ # (e.g., 16.0018 rather than 16). Internally, when asking for a
364
+ # region for anything other than the maximum resolution lever, the
365
+ # openslide library is passed coordinates in what _seems_ to be
366
+ # base image coordinates, but is actually inflated by the ratio of
367
+ # their downsample value and the actual downsample value (e.g.,
368
+ # 16.0018 / 16). We multiple our values by this ratio so when
369
+ # openslide misapplies its downsampling we get the region we
370
+ # actually want
371
+ if svslevel['svsscale'] != 1:
372
+ offsetx = int(round(offsetx * svslevel['svsscale']))
373
+ offsety = int(round(offsety * svslevel['svsscale']))
374
+ svsTileWidth = int(round(svsTileWidth * svslevel['svsscale']))
375
+ svsTileHeight = int(round(svsTileHeight * svslevel['svsscale']))
330
376
  while retries > 0:
331
377
  try:
332
378
  tile = self._openslide.read_region(
333
379
  (offsetx, offsety), svslevel['svslevel'],
334
- (self.tileWidth * svslevel['scale'],
335
- self.tileHeight * svslevel['scale']))
380
+ (svsTileWidth, svsTileHeight))
336
381
  format = TILE_FORMAT_PIL
337
382
  break
338
383
  except openslide.lowlevel.OpenSlideError as exc:
@@ -349,6 +394,10 @@ class OpenslideFileTileSource(FileTileSource, metaclass=LruCacheMetaclass):
349
394
  retries -= 1
350
395
  if retries <= 0:
351
396
  raise TileSourceError(msg)
397
+ if tile.mode == 'RGBA' and self._background:
398
+ tile = np.array(tile)
399
+ tile[tile[:, :, -1] == 0, :3] = self._background
400
+ tile = PIL.Image.fromarray(tile, 'RGBA')
352
401
  # Always scale to the svs level 0 tile size.
353
402
  if svslevel['scale'] != 1:
354
403
  tile = tile.resize((self.tileWidth, self.tileHeight),
@@ -28,5 +28,5 @@ class OpenslideGirderTileSource(OpenslideFileTileSource, GirderTileSource):
28
28
  cacheName = 'tilesource'
29
29
  name = 'openslide'
30
30
 
31
- extensionsWithAdjacentFiles = {'mrxs'}
32
- mimeTypesWithAdjacentFiles = {'image/mirax'}
31
+ extensionsWithAdjacentFiles = {'mrxs', 'dcm', 'dicom'}
32
+ mimeTypesWithAdjacentFiles = {'image/mirax', 'application/dicom'}
@@ -1,25 +1,24 @@
1
- Metadata-Version: 2.1
1
+ Metadata-Version: 2.4
2
2
  Name: large-image-source-openslide
3
- Version: 1.30.3.dev35
3
+ Version: 1.33.6a165
4
4
  Summary: An Openslide tilesource for large_image.
5
5
  Home-page: https://github.com/girder/large_image
6
6
  Author: Kitware, Inc.
7
7
  Author-email: kitware@kitware.com
8
- License: Apache Software License 2.0
8
+ License: Apache-2.0
9
9
  Keywords: large_image,tile source
10
10
  Classifier: Development Status :: 5 - Production/Stable
11
- Classifier: License :: OSI Approved :: Apache Software License
12
11
  Classifier: Programming Language :: Python :: 3
13
- Classifier: Programming Language :: Python :: 3.8
14
12
  Classifier: Programming Language :: Python :: 3.9
15
13
  Classifier: Programming Language :: Python :: 3.10
16
14
  Classifier: Programming Language :: Python :: 3.11
17
15
  Classifier: Programming Language :: Python :: 3.12
18
16
  Classifier: Programming Language :: Python :: 3.13
19
- Requires-Python: >=3.8
17
+ Classifier: Programming Language :: Python :: 3.14
18
+ Requires-Python: >=3.9
20
19
  Description-Content-Type: text/x-rst
21
20
  License-File: LICENSE
22
- Requires-Dist: large-image>=1.30.3.dev35
21
+ Requires-Dist: large-image>=1.33.6.a165
23
22
  Requires-Dist: openslide-python>=1.4.1
24
23
  Requires-Dist: openslide-bin; platform_system == "Linux" and platform_machine == "x86_64"
25
24
  Requires-Dist: openslide-bin; platform_system == "Linux" and platform_machine == "aarch64"
@@ -28,7 +27,20 @@ Requires-Dist: openslide-bin; platform_system == "Darwin" and platform_machine =
28
27
  Requires-Dist: openslide-bin; platform_system == "Darwin" and platform_machine == "x86_64"
29
28
  Requires-Dist: tifftools>=1.2.0
30
29
  Provides-Extra: girder
31
- Requires-Dist: girder-large-image>=1.30.3.dev35; extra == "girder"
30
+ Requires-Dist: girder-large-image>=1.33.6.a165; extra == "girder"
31
+ Dynamic: author
32
+ Dynamic: author-email
33
+ Dynamic: classifier
34
+ Dynamic: description
35
+ Dynamic: description-content-type
36
+ Dynamic: home-page
37
+ Dynamic: keywords
38
+ Dynamic: license
39
+ Dynamic: license-file
40
+ Dynamic: provides-extra
41
+ Dynamic: requires-dist
42
+ Dynamic: requires-python
43
+ Dynamic: summary
32
44
 
33
45
  An Openslide tilesource for large_image.
34
46
 
@@ -0,0 +1,8 @@
1
+ large_image_source_openslide/__init__.py,sha256=CEfTufsAco3V87GHdRtJz7KbIuLrgu1vGyc5-dtOXd4,22822
2
+ large_image_source_openslide/girder_source.py,sha256=wPa7xBoJ5-PKHMVy9joUNSUHTN6HCZKyR45lhR0SsRc,1236
3
+ large_image_source_openslide-1.33.6a165.dist-info/licenses/LICENSE,sha256=psuoW8kuDP96RQsdhzwOqi6fyWv0ct8CR6Jr7He_P_k,10173
4
+ large_image_source_openslide-1.33.6a165.dist-info/METADATA,sha256=XApXY6AO8gL5WbeS8nvvWYjrUSQQEgGc9Qe2avit_mM,1797
5
+ large_image_source_openslide-1.33.6a165.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
6
+ large_image_source_openslide-1.33.6a165.dist-info/entry_points.txt,sha256=4Z5cf63yeesvHxiR9W1dY3rfz5_V4Ck2xYhPUp4A1qA,196
7
+ large_image_source_openslide-1.33.6a165.dist-info/top_level.txt,sha256=wqaQQDWQl9a_l9s6n07tTxfjeEXePtHBhz1np6aUwQE,29
8
+ large_image_source_openslide-1.33.6a165.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (75.6.0)
2
+ Generator: setuptools (80.9.0)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5
 
@@ -1,8 +0,0 @@
1
- large_image_source_openslide/__init__.py,sha256=Ql2nXz2XuIySbrEse0gf-oRBmKD0ugQztLAVjOj5UF0,20056
2
- large_image_source_openslide/girder_source.py,sha256=ntLgOinO7xcO85wFbjx6XEBmTgEtx5cuPZ_K6wXZzqA,1199
3
- large_image_source_openslide-1.30.3.dev35.dist-info/LICENSE,sha256=psuoW8kuDP96RQsdhzwOqi6fyWv0ct8CR6Jr7He_P_k,10173
4
- large_image_source_openslide-1.30.3.dev35.dist-info/METADATA,sha256=DXzCXqED-xYQ4VqsLViWr4t6bTLfZiTIZzw9I-TyeNk,1602
5
- large_image_source_openslide-1.30.3.dev35.dist-info/WHEEL,sha256=PZUExdf71Ui_so67QXpySuHtCi3-J3wvF4ORK6k_S8U,91
6
- large_image_source_openslide-1.30.3.dev35.dist-info/entry_points.txt,sha256=4Z5cf63yeesvHxiR9W1dY3rfz5_V4Ck2xYhPUp4A1qA,196
7
- large_image_source_openslide-1.30.3.dev35.dist-info/top_level.txt,sha256=wqaQQDWQl9a_l9s6n07tTxfjeEXePtHBhz1np6aUwQE,29
8
- large_image_source_openslide-1.30.3.dev35.dist-info/RECORD,,