diff --git a/sources/ometiff/large_image_source_ometiff/__init__.py b/sources/ometiff/large_image_source_ometiff/__init__.py index f7900ae91..430a600b0 100644 --- a/sources/ometiff/large_image_source_ometiff/__init__.py +++ b/sources/ometiff/large_image_source_ometiff/__init__.py @@ -79,6 +79,10 @@ class OMETiffFileTileSource(TiffFileTileSource, metaclass=LruCacheMetaclass): 'ome': SourcePriority.PREFERRED, } + # The expect number of pixels that would need to be read to read the worst- + # case tile. + _maxUntiledChunk = 512 * 1024 * 1024 + def __init__(self, path, **kwargs): """ Initialize the tile class. See the base class for other available @@ -123,6 +127,12 @@ def __init__(self, path, **kwargs): TiledTiffDirectory(largeImagePath, 0, mustBeTiled=None) if entry else None for entry in self._omeLevels] + self._checkForInefficientDirectories(warn=False) + _maxChunk = min(base.imageWidth, base.tileWidth * self._skippedLevels ** 2) * \ + min(base.imageHeight, base.tileHeight * self._skippedLevels ** 2) + if _maxChunk > self._maxUntiledChunk: + raise TileSourceError( + 'Untiled image is too large to access with the OME Tiff source') self.tileWidth = base.tileWidth self.tileHeight = base.tileHeight self.levels = len(self._tiffDirectories) @@ -133,6 +143,7 @@ def __init__(self, path, **kwargs): # images as associated images. This would require enumerating tiff # directories not mentioned by the ome list. self._associatedImages = {} + self._checkForInefficientDirectories() def _checkForOMEZLoop(self, largeImagePath): """ diff --git a/sources/tiff/large_image_source_tiff/__init__.py b/sources/tiff/large_image_source_tiff/__init__.py index fdb0aba91..e23d2d202 100644 --- a/sources/tiff/large_image_source_tiff/__init__.py +++ b/sources/tiff/large_image_source_tiff/__init__.py @@ -143,6 +143,7 @@ def __init__(self, path, **kwargs): self.levels = len(self._tiffDirectories) self.sizeX = highest.imageWidth self.sizeY = highest.imageHeight + self._checkForInefficientDirectories() def _scanDirectories(self): largeImagePath = self._largeImagePath @@ -314,16 +315,25 @@ def _initWithTiffTools(self): # noqa if frames[0]['dirs'][idx] is not None else None for idx in range(self.levels - 1)] self._tiffDirectories.append(dir0) + self._checkForInefficientDirectories() + return True + + def _checkForInefficientDirectories(self, warn=True): + """ + Raise a warning for inefficient files. + + :param warn: if True and inefficient, emit a warning. + """ 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)) self._skippedLevels = maxMissing if maxMissing >= self._maxSkippedLevels: - config.getConfig('logger').warning( - 'Tiff image is missing many lower resolution levels (%d). ' - 'It will be inefficient to read lower resolution tiles.', maxMissing) + if warn: + config.getConfig('logger').warning( + 'Tiff image is missing many lower resolution levels (%d). ' + 'It will be inefficient to read lower resolution tiles.', maxMissing) self._inefficientWarning = True - return True def _reorient_numpy_image(self, image, orientation): """ @@ -631,8 +641,8 @@ def getTileFromEmptyDirectory(self, x, y, z, **kwargs): while z - basez > self._maxSkippedLevels: z -= self._maxSkippedLevels scale = int(scale / 2 ** self._maxSkippedLevels) - tile = PIL.Image.new( - 'RGBA', (self.tileWidth * scale, self.tileHeight * scale)) + tile = PIL.Image.new('RGBA', ( + min(self.sizeX, self.tileWidth * scale), min(self.sizeY, self.tileHeight * scale))) maxX = 2.0 ** (z + 1 - self.levels) * self.sizeX / self.tileWidth maxY = 2.0 ** (z + 1 - self.levels) * self.sizeY / self.tileHeight for newX in range(scale):