From 253a2283caf1032bc718b677708273eca21b2511 Mon Sep 17 00:00:00 2001 From: David Manthey Date: Wed, 7 Jun 2023 17:08:32 -0400 Subject: [PATCH] Report actual populated image levels This adds an unofficial value to some tile sources of _populatedLevels, which, if set, reports how many actual image levels are used by the source. Not all sources can report this; some additional ones probably could. This is primarily useful in determining why some files are slow. --- CHANGELOG.md | 1 + girder/girder_large_image/models/image_item.py | 2 ++ sources/bioformats/large_image_source_bioformats/__init__.py | 2 ++ sources/dicom/large_image_source_dicom/__init__.py | 1 + sources/openjpeg/large_image_source_openjpeg/__init__.py | 1 + sources/openslide/large_image_source_openslide/__init__.py | 1 + sources/tiff/large_image_source_tiff/__init__.py | 1 + sources/tifffile/large_image_source_tifffile/__init__.py | 1 + 8 files changed, 10 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6dda7f396..8ae960b0d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ ### Improvements - Better DICOM multi-level detection ([#1196](../../pull/1196)) +- Added an internal field to report populated tile levels in some sources ([#1197](../../pull/1197)) ### Changes - Change how extensions and fallback priorities interact ([#1192](../../pull/1192)) diff --git a/girder/girder_large_image/models/image_item.py b/girder/girder_large_image/models/image_item.py index 58b921407..98062e479 100644 --- a/girder/girder_large_image/models/image_item.py +++ b/girder/girder_large_image/models/image_item.py @@ -270,6 +270,8 @@ def getInternalMetadata(self, item, **kwargs): if tileSource.getICCProfiles(onlyInfo=True): result['iccprofiles'] = tileSource.getICCProfiles(onlyInfo=True) result['tilesource'] = tileSource.name + if hasattr(tileSource, '_populatedLevels'): + result['populatedLevels'] = tileSource._populatedLevels return result def getTile(self, item, x, y, z, mayRedirect=False, **kwargs): diff --git a/sources/bioformats/large_image_source_bioformats/__init__.py b/sources/bioformats/large_image_source_bioformats/__init__.py index b3a7994d1..f9e7e13f7 100644 --- a/sources/bioformats/large_image_source_bioformats/__init__.py +++ b/sources/bioformats/large_image_source_bioformats/__init__.py @@ -266,6 +266,8 @@ def __init__(self, path, **kwargs): # noqa self.getTile(0, 0, self.levels - 1) except Exception as exc: raise TileSourceError('Bioformats cannot read a tile: %r' % exc) + self._populatedLevels = len([ + v for v in self._metadata['frameSeries'][0]['series'] if v is not None]) def __del__(self): if getattr(self, '_bioimage', None) is not None: diff --git a/sources/dicom/large_image_source_dicom/__init__.py b/sources/dicom/large_image_source_dicom/__init__.py index d4d9ed40d..34c93e7a3 100644 --- a/sources/dicom/large_image_source_dicom/__init__.py +++ b/sources/dicom/large_image_source_dicom/__init__.py @@ -138,6 +138,7 @@ def __init__(self, path, **kwargs): self.tileHeight = min(max(self.tileHeight, self._minTileSize), self._maxTileSize) self.levels = int(max(1, math.ceil(math.log( max(self.sizeX / self.tileWidth, self.sizeY / self.tileHeight)) / math.log(2)) + 1)) + self._populatedLevels = len(self._dicom.levels) def __del__(self): if getattr(self, '_dicom', None) is not None: diff --git a/sources/openjpeg/large_image_source_openjpeg/__init__.py b/sources/openjpeg/large_image_source_openjpeg/__init__.py index 06ae15180..7cb69c549 100644 --- a/sources/openjpeg/large_image_source_openjpeg/__init__.py +++ b/sources/openjpeg/large_image_source_openjpeg/__init__.py @@ -128,6 +128,7 @@ def __init__(self, path, **kwargs): self.sizeX, self.sizeY)) / self.tileWidth) / math.log(2))) + 1 self._minlevel = self.levels - self._openjpeg.codestream.segment[2].num_res - 1 self._getAssociatedImages() + self._populatedLevels = self.levels - self._minlevel def _getAssociatedImages(self): """ diff --git a/sources/openslide/large_image_source_openslide/__init__.py b/sources/openslide/large_image_source_openslide/__init__.py index 385494837..d169076ab 100644 --- a/sources/openslide/large_image_source_openslide/__init__.py +++ b/sources/openslide/large_image_source_openslide/__init__.py @@ -160,6 +160,7 @@ def __init__(self, path, **kwargs): # noqa 'svslevel': bestlevel, 'scale': scale }) + self._populatedLevels = len({l['svslevel'] for l in self._svslevels}) def _getTileSize(self): """ diff --git a/sources/tiff/large_image_source_tiff/__init__.py b/sources/tiff/large_image_source_tiff/__init__.py index b423f6dc7..ace70480b 100644 --- a/sources/tiff/large_image_source_tiff/__init__.py +++ b/sources/tiff/large_image_source_tiff/__init__.py @@ -369,6 +369,7 @@ def _checkForInefficientDirectories(self, warn=True): :param warn: if True and inefficient, emit a warning. """ + self._populatedLevels = len([v for v in self._tiffDirectories if v is not None]) missing = [v is None for v in self._tiffDirectories] maxMissing = max(0 if not v else missing.index(False, idx) - idx for idx, v in enumerate(missing)) diff --git a/sources/tifffile/large_image_source_tifffile/__init__.py b/sources/tifffile/large_image_source_tifffile/__init__.py index 9548abef7..b671b54c5 100644 --- a/sources/tifffile/large_image_source_tifffile/__init__.py +++ b/sources/tifffile/large_image_source_tifffile/__init__.py @@ -137,6 +137,7 @@ def __init__(self, path, **kwargs): if (key.startswith('is_') and hasattr(self, '_handle_' + key[3:]) and getattr(self._tf, key)): getattr(self, '_handle_' + key[3:])() + self._populatedLevels = len(self._baseSeries.levels) def _biggestSeries(self): """