def __init__(self, path, **kwargs): """ Initialize the tile class. See the base class for other available parameters. :param path: a filesystem path for the tile source. """ super(TiffFileTileSource, self).__init__(path, **kwargs) largeImagePath = self._getLargeImagePath() try: alldir = self._scanDirectories() except (ValidationTiffException, TiffException) as exc: alldir = [] lastException = exc # If there are no tiled images, raise an exception. if not len(alldir): msg = "File %s didn't meet requirements for tile source: %s" % ( largeImagePath, lastException) config.getConfig('logger').debug(msg) raise TileSourceException(msg) # Sort the known directories by image area (width * height). Given # equal area, sort by the level. alldir.sort() # The highest resolution image is our preferred image highest = alldir[-1][-1] directories = {} # Discard any images that use a different tiling scheme than our # preferred image for tdir in alldir: td = tdir[-1] level = tdir[2] if (td.tileWidth != highest.tileWidth or td.tileHeight != highest.tileHeight): if not len(self._associatedImages): self._addAssociatedImage(largeImagePath, tdir[-2], True, highest) continue # If a layer's image is not a multiple of the tile size, it should # be near a power of two of the highest resolution image. if (((td.imageWidth % td.tileWidth) and not nearPowerOfTwo(td.imageWidth, highest.imageWidth)) or ((td.imageHeight % td.tileHeight) and not nearPowerOfTwo(td.imageHeight, highest.imageHeight))): continue directories[level] = td if not len(directories) or (len(directories) < 2 and max(directories.keys()) + 1 > 4): raise TileSourceException( 'Tiff image must have at least two levels.') # Sort the directories so that the highest resolution is the last one; # if a level is missing, put a None value in its place. self._tiffDirectories = [directories.get(key) for key in range(max(directories.keys()) + 1)] self.tileWidth = highest.tileWidth self.tileHeight = highest.tileHeight self.levels = len(self._tiffDirectories) self.sizeX = highest.imageWidth self.sizeY = highest.imageHeight
def testNearPowerOfTwo(): assert nearPowerOfTwo(45808, 11456) assert nearPowerOfTwo(45808, 11450) assert not nearPowerOfTwo(45808, 11200) assert nearPowerOfTwo(45808, 11400) assert not nearPowerOfTwo(45808, 11400, 0.005) assert nearPowerOfTwo(45808, 11500) assert not nearPowerOfTwo(45808, 11500, 0.005)
def _checkSeries(self, rdr): firstPossibleAssoc = self._getSeriesStarts(rdr) self._metadata['seriesAssociatedImages'] = {} for seriesNum in range(firstPossibleAssoc, self._metadata['seriesCount']): if any((seriesNum in series['series']) for series in self._metadata['frameSeries']): continue rdr.setSeries(seriesNum) info = { 'sizeX': rdr.getSizeX(), 'sizeY': rdr.getSizeY(), } if (info['sizeX'] < self._associatedImageMaxSize and info['sizeY'] < self._associatedImageMaxSize): # TODO: Figure out better names for associated images. Can # we tell if any of them are the macro or label image? info['seriesNum'] = seriesNum self._metadata['seriesAssociatedImages']['image%d' % seriesNum] = info validate = None for frame in self._metadata['frameSeries']: for level in range(len(frame['series'])): rdr.setSeries(frame['series'][level]) info = { 'sizeX': rdr.getSizeX(), 'sizeY': rdr.getSizeY(), } if not level: frame.update(info) self._metadata['sizeX'] = max(self._metadata['sizeX'], frame['sizeX']) self._metadata['sizeY'] = max(self._metadata['sizeY'], frame['sizeY']) elif validate is not False: if (not nearPowerOfTwo(frame['sizeX'], info['sizeX']) or not nearPowerOfTwo(frame['sizeY'], info['sizeY'])): frame['series'] = frame['series'][:level] validate = True if validate is None: validate = False rdr.setSeries(0) self._metadata['sizeXY'] = len(self._metadata['frameSeries'])
def _getAvailableLevels(self, path): """ Some SVS files (notably some NDPI variants) have levels that cannot be read. Get a list of levels, check that each is at least potentially readable, and return a list of these sorted highest-resolution first. :param path: the path of the SVS file. After a failure, the file is reopened to reset the error state. :returns: levels. A list of valid levels, each of which is a dictionary of level (the internal 0-based level number), width, and height. """ levels = [] svsLevelDimensions = self._openslide.level_dimensions for svslevel in range(len(svsLevelDimensions)): try: self._openslide.read_region((0, 0), svslevel, (1, 1)) level = { 'level': svslevel, 'width': svsLevelDimensions[svslevel][0], 'height': svsLevelDimensions[svslevel][1], } if level['width'] > 0 and level['height'] > 0: # add to the list so that we can sort by resolution and # then by earlier entries levels.append((level['width'] * level['height'], -len(levels), level)) except openslide.lowlevel.OpenSlideError: self._openslide = openslide.OpenSlide(path) # sort highest resolution first. levels = [ entry[-1] for entry in sorted(levels, reverse=True, key=lambda x: x[:-1]) ] # Discard levels that are not a power-of-two compared to the highest # resolution level. levels = [ entry for entry in levels if nearPowerOfTwo(levels[0]['width'], entry['width']) and nearPowerOfTwo(levels[0]['height'], entry['height']) ] return levels
def __init__(self, path, **kwargs): """ Initialize the tile class. See the base class for other available parameters. :param path: a filesystem path for the tile source. """ super(TiffFileTileSource, self).__init__(path, **kwargs) largeImagePath = self._getLargeImagePath() lastException = None # Associated images are smallish TIFF images that have an image # description and are not tiled. They have their own TIFF directory. # Individual TIFF images can also have images embedded into their # directory as tags (this is a vendor-specific method of adding more # images into a file) -- those are stored in the individual # directories' _embeddedImages field. self._associatedImages = {} # Query all know directories in the tif file. Only keep track of # directories that contain tiled images. alldir = [] for directoryNum in itertools.count(): # pragma: no branch try: td = TiledTiffDirectory(largeImagePath, directoryNum) except ValidationTiffException as exc: lastException = exc self._addAssociatedImage(largeImagePath, directoryNum) continue except TiffException as exc: if not lastException: lastException = exc break if not td.tileWidth or not td.tileHeight: continue # Calculate the tile level, where 0 is a single tile, 1 is up to a # set of 2x2 tiles, 2 is 4x4, etc. level = int( math.ceil( math.log( max( float(td.imageWidth) / td.tileWidth, float(td.imageHeight) / td.tileHeight)) / math.log(2))) if level < 0: continue # Store information for sorting with the directory. alldir.append((level > 0, td.tileWidth * td.tileHeight, level, td.imageWidth * td.imageHeight, directoryNum, td)) # If there are no tiled images, raise an exception. if not len(alldir): msg = "File %s didn't meet requirements for tile source: %s" % ( largeImagePath, lastException) config.getConfig('logger').debug(msg) raise TileSourceException(msg) # Sort the known directories by image area (width * height). Given # equal area, sort by the level. alldir.sort() # The highest resolution image is our preferred image highest = alldir[-1][-1] directories = {} # Discard any images that use a different tiling scheme than our # preferred image for tdir in alldir: td = tdir[-1] level = tdir[2] if (td.tileWidth != highest.tileWidth or td.tileHeight != highest.tileHeight): if not len(self._associatedImages): self._addAssociatedImage(largeImagePath, tdir[-2], True, highest) continue # If a layer's image is not a multiple of the tile size, it should # be near a power of two of the highest resolution image. if (((td.imageWidth % td.tileWidth) and not nearPowerOfTwo(td.imageWidth, highest.imageWidth)) or ((td.imageHeight % td.tileHeight) and not nearPowerOfTwo(td.imageHeight, highest.imageHeight))): continue directories[level] = td if not len(directories) or (len(directories) < 2 and max(directories.keys()) + 1 > 4): raise TileSourceException( 'Tiff image must have at least two levels.') # Sort the directories so that the highest resolution is the last one; # if a level is missing, put a None value in its place. self._tiffDirectories = [ directories.get(key) for key in range(max(directories.keys()) + 1) ] self.tileWidth = highest.tileWidth self.tileHeight = highest.tileHeight self.levels = len(self._tiffDirectories) self.sizeX = highest.imageWidth self.sizeY = highest.imageHeight