large-image-source-openslide 1.30.7.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,
@@ -135,19 +139,22 @@ class OpenslideFileTileSource(FileTileSource, metaclass=LruCacheMetaclass):
135
139
  # load an appropriate area and scale it to the tile size later.
136
140
  maxSize = 16384 # This should probably be based on available memory
137
141
  for level in range(self.levels):
138
- levelW = max(1, self.sizeX / 2 ** (self.levels - 1 - level))
139
- 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)
140
145
  # bestlevel and scale will be the picked svs level and the scale
141
146
  # between that level and what we really wanted. We expect scale to
142
147
  # always be a positive integer power of two.
143
148
  bestlevel = svsAvailableLevels[0]['level']
144
149
  scale = 1
150
+ svsscale = 0
145
151
  for svslevel in range(len(svsAvailableLevels)):
146
152
  if (svsAvailableLevels[svslevel]['width'] < levelW - 1 or
147
153
  svsAvailableLevels[svslevel]['height'] < levelH - 1):
148
154
  break
149
155
  bestlevel = svsAvailableLevels[svslevel]['level']
150
156
  scale = int(round(svsAvailableLevels[svslevel]['width'] / levelW))
157
+ svsscale = svsAvailableLevels[svslevel].get('downsample', 0)
151
158
  # If there are no tiles at a particular level, we have to read a
152
159
  # larger area of a higher resolution level. If such an area would
153
160
  # be excessively large, we could have memory issues, so raise an
@@ -161,6 +168,7 @@ class OpenslideFileTileSource(FileTileSource, metaclass=LruCacheMetaclass):
161
168
  self._svslevels.append({
162
169
  'svslevel': bestlevel,
163
170
  'scale': scale,
171
+ 'svsscale': ((svsscale / levelpow) if svsscale else 1) * scale,
164
172
  })
165
173
  self._bounds = None
166
174
  try:
@@ -186,6 +194,12 @@ class OpenslideFileTileSource(FileTileSource, metaclass=LruCacheMetaclass):
186
194
  self._svslevels = self._svslevels[prevlevels - self.levels:]
187
195
  except Exception:
188
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
189
203
  self._populatedLevels = len({l['svslevel'] for l in self._svslevels})
190
204
 
191
205
  def _getTileSize(self):
@@ -235,6 +249,7 @@ class OpenslideFileTileSource(FileTileSource, metaclass=LruCacheMetaclass):
235
249
  """
236
250
  levels = []
237
251
  svsLevelDimensions = self._openslide.level_dimensions
252
+
238
253
  for svslevel in range(len(svsLevelDimensions)):
239
254
  try:
240
255
  self._openslide.read_region((0, 0), svslevel, (1, 1))
@@ -243,6 +258,10 @@ class OpenslideFileTileSource(FileTileSource, metaclass=LruCacheMetaclass):
243
258
  'width': svsLevelDimensions[svslevel][0],
244
259
  'height': svsLevelDimensions[svslevel][1],
245
260
  }
261
+ try:
262
+ level['downsample'] = self._openslide.level_downsamples[svslevel]
263
+ except Exception:
264
+ pass
246
265
  if level['width'] > 0 and level['height'] > 0:
247
266
  # add to the list so that we can sort by resolution and
248
267
  # then by earlier entries
@@ -334,12 +353,31 @@ class OpenslideFileTileSource(FileTileSource, metaclass=LruCacheMetaclass):
334
353
  tile, format = self._getTileFromEmptyLevel(x, y, z, **kwargs)
335
354
  else:
336
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']))
337
376
  while retries > 0:
338
377
  try:
339
378
  tile = self._openslide.read_region(
340
379
  (offsetx, offsety), svslevel['svslevel'],
341
- (self.tileWidth * svslevel['scale'],
342
- self.tileHeight * svslevel['scale']))
380
+ (svsTileWidth, svsTileHeight))
343
381
  format = TILE_FORMAT_PIL
344
382
  break
345
383
  except openslide.lowlevel.OpenSlideError as exc:
@@ -356,6 +394,10 @@ class OpenslideFileTileSource(FileTileSource, metaclass=LruCacheMetaclass):
356
394
  retries -= 1
357
395
  if retries <= 0:
358
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')
359
401
  # Always scale to the svs level 0 tile size.
360
402
  if svslevel['scale'] != 1:
361
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.2
1
+ Metadata-Version: 2.4
2
2
  Name: large-image-source-openslide
3
- Version: 1.30.7.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.7.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,7 @@ 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.7.dev35; extra == "girder"
30
+ Requires-Dist: girder-large-image>=1.33.6.a165; extra == "girder"
32
31
  Dynamic: author
33
32
  Dynamic: author-email
34
33
  Dynamic: classifier
@@ -37,6 +36,7 @@ Dynamic: description-content-type
37
36
  Dynamic: home-page
38
37
  Dynamic: keywords
39
38
  Dynamic: license
39
+ Dynamic: license-file
40
40
  Dynamic: provides-extra
41
41
  Dynamic: requires-dist
42
42
  Dynamic: requires-python
@@ -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.8.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=O1-E-D7ZdY6Ta_ngId_HV0uqqxAbuY_J9fKTNVOFulE,20495
2
- large_image_source_openslide/girder_source.py,sha256=ntLgOinO7xcO85wFbjx6XEBmTgEtx5cuPZ_K6wXZzqA,1199
3
- large_image_source_openslide-1.30.7.dev35.dist-info/LICENSE,sha256=psuoW8kuDP96RQsdhzwOqi6fyWv0ct8CR6Jr7He_P_k,10173
4
- large_image_source_openslide-1.30.7.dev35.dist-info/METADATA,sha256=mJwXTkcefJJxNLpCpEAYPsu_nSw1dQVsPKHwaZtXleo,1858
5
- large_image_source_openslide-1.30.7.dev35.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
6
- large_image_source_openslide-1.30.7.dev35.dist-info/entry_points.txt,sha256=4Z5cf63yeesvHxiR9W1dY3rfz5_V4Ck2xYhPUp4A1qA,196
7
- large_image_source_openslide-1.30.7.dev35.dist-info/top_level.txt,sha256=wqaQQDWQl9a_l9s6n07tTxfjeEXePtHBhz1np6aUwQE,29
8
- large_image_source_openslide-1.30.7.dev35.dist-info/RECORD,,