Пример #1
0
 def _getLargeImagePath(self):
     # If self.mayHaveAdjacentFiles is True, we try to use the girder
     # mount where companion files appear next to each other.
     largeImageFileId = self.item['largeImage']['fileId']
     largeImageFile = File().load(largeImageFileId, force=True)
     try:
         largeImagePath = None
         if (self.mayHaveAdjacentFiles(largeImageFile)
                 and hasattr(File(), 'getGirderMountFilePath')):
             try:
                 if (largeImageFile.get('imported')
                         and File().getLocalFilePath(largeImageFile)
                         == largeImageFile['path']):
                     largeImagePath = largeImageFile['path']
             except Exception:
                 pass
             if not largeImagePath:
                 try:
                     largeImagePath = File().getGirderMountFilePath(
                         largeImageFile)
                 except FilePathException:
                     pass
         if not largeImagePath:
             try:
                 largeImagePath = File().getLocalFilePath(largeImageFile)
             except AttributeError as e:
                 raise TileSourceException(
                     'No local file path for this file: %s' % e.args[0])
         return largeImagePath
     except (TileSourceAssetstoreException, FilePathException):
         raise
     except (KeyError, ValidationException, TileSourceException) as e:
         raise TileSourceException('No large image file in this item: %s' %
                                   e.args[0])
Пример #2
0
 def getTile(self, x, y, z, pilImageAllowed=False, **kwargs):
     if z < 0:
         raise TileSourceException('z layer does not exist')
     try:
         svslevel = self._svslevels[z]
     except IndexError:
         raise TileSourceException('z layer does not exist')
     # When we read a region from the SVS, we have to ask for it in the
     # SVS level 0 coordinate system.  Our x and y is in tile space at the
     # specifed z level, so the offset in SVS level 0 coordinates has to be
     # scaled by the tile size and by the z level.
     scale = 2**(self.levels - 1 - z)
     offsetx = x * self.tileWidth * scale
     if not (0 <= offsetx < self.sizeX):
         raise TileSourceException('x is outside layer')
     offsety = y * self.tileHeight * scale
     if not (0 <= offsety < self.sizeY):
         raise TileSourceException('y is outside layer')
     # We ask to read an area that will cover the tile at the z level.  The
     # scale we computed in the __init__ process for this svs level tells
     # how much larger a region we need to read.
     try:
         tile = self._openslide.read_region(
             (offsetx, offsety), svslevel['svslevel'],
             (self.tileWidth * svslevel['scale'],
              self.tileHeight * svslevel['scale']))
     except openslide.lowlevel.OpenSlideError as exc:
         raise TileSourceException('Failed to get OpenSlide region (%r).' %
                                   exc)
     # Always scale to the svs level 0 tile size.
     if svslevel['scale'] != 1:
         tile = tile.resize((self.tileWidth, self.tileHeight),
                            PIL.Image.LANCZOS)
     return self._outputTile(tile, 'PIL', x, y, z, pilImageAllowed,
                             **kwargs)
Пример #3
0
 def getTile(self, x, y, z, pilImageAllowed=False, numpyAllowed=False,
             sparseFallback=False, **kwargs):
     self._xyzInRange(x, y, z)
     try:
         allowStyle = True
         if self._tiffDirectories[z] is None:
             try:
                 tile = self.getTileFromEmptyDirectory(x, y, z, **kwargs)
             except Exception:
                 if sparseFallback:
                     raise IOTiffException('Missing z level %d' % z)
                 else:
                     raise
             allowStyle = False
             format = TILE_FORMAT_PIL
         else:
             tile = self._tiffDirectories[z].getTile(x, y)
             format = 'JPEG'
         if isinstance(tile, PIL.Image.Image):
             format = TILE_FORMAT_PIL
         if isinstance(tile, numpy.ndarray):
             format = TILE_FORMAT_NUMPY
         return self._outputTile(tile, format, x, y, z, pilImageAllowed,
                                 numpyAllowed, applyStyle=allowStyle, **kwargs)
     except IndexError:
         raise TileSourceException('z layer does not exist')
     except InvalidOperationTiffException as e:
         raise TileSourceException(e.args[0])
     except IOTiffException as e:
         return self.getTileIOTiffException(
             x, y, z, pilImageAllowed=pilImageAllowed,
             numpyAllowed=numpyAllowed, sparseFallback=sparseFallback,
             exception=e, **kwargs)
Пример #4
0
 def getTile(self,
             x,
             y,
             z,
             pilImageAllowed=False,
             sparseFallback=False,
             **kwargs):
     if z < 0:
         raise TileSourceException('z layer does not exist')
     try:
         if self._tiffDirectories[z] is None:
             if sparseFallback:
                 raise IOTiffException('Missing z level %d' % z)
             tile = self.getTileFromEmptyDirectory(x, y, z, **kwargs)
             format = TILE_FORMAT_PIL
         else:
             tile = self._tiffDirectories[z].getTile(x, y)
             format = 'JPEG'
         if PIL and isinstance(tile, PIL.Image.Image):
             format = TILE_FORMAT_PIL
         return self._outputTile(tile, format, x, y, z, pilImageAllowed,
                                 **kwargs)
     except IndexError:
         raise TileSourceException('z layer does not exist')
     except InvalidOperationTiffException as e:
         raise TileSourceException(e.args[0])
     except IOTiffException as e:
         return self.getTileIOTiffException(x,
                                            y,
                                            z,
                                            pilImageAllowed=pilImageAllowed,
                                            sparseFallback=sparseFallback,
                                            exception=e,
                                            **kwargs)
Пример #5
0
 def getTile(self, x, y, z, pilImageAllowed=False, sparseFallback=False,
             **kwargs):
     if (z < 0 or z >= len(self._omeLevels) or self._omeLevels[z] is None or
             kwargs.get('frame') in (None, 0, '0', '')):
         return super(OMETiffFileTileSource, self).getTile(
             x, y, z, pilImageAllowed=pilImageAllowed, sparseFallback=sparseFallback, **kwargs)
     frame = int(kwargs['frame'])
     if frame < 0 or frame >= len(self._omebase['TiffData']):
         raise TileSourceException('Frame does not exist')
     dirnum = int(self._omeLevels[z]['TiffData'][frame]['IFD'])
     if dirnum in self._directoryCache:
         dir = self._directoryCache[dirnum]
     else:
         if len(self._directoryCache) >= self._directoryCacheMaxSize:
             self._directoryCache = {}
         dir = TiledTiffDirectory(self._getLargeImagePath(), dirnum)
         self._directoryCache[dirnum] = dir
     try:
         tile = dir.getTile(x, y)
         format = 'JPEG'
         if PIL and isinstance(tile, PIL.Image.Image):
             format = TILE_FORMAT_PIL
         return self._outputTile(tile, format, x, y, z, pilImageAllowed,
                                 **kwargs)
     except InvalidOperationTiffException as e:
         raise TileSourceException(e.args[0])
     except IOTiffException as e:
         return self.getTileIOTiffException(
             x, y, z, pilImageAllowed=pilImageAllowed,
             sparseFallback=sparseFallback, exception=e, **kwargs)
Пример #6
0
    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
Пример #7
0
    def _colorizerFromStyle(self, style):
        """
        Add a specified style to a mapnik raster symbolizer.

        :param style: a style object.
        :returns: a mapnik raster colorizer.
        """
        try:
            scheme = style.get('scheme', 'linear')
            mapnik_scheme = getattr(mapnik,
                                    'COLORIZER_{}'.format(scheme.upper()))
        except AttributeError:
            mapnik_scheme = mapnik.COLORIZER_DISCRETE
            raise TileSourceException(
                'Scheme has to be either "discrete" or "linear".')
        colorizer = mapnik.RasterColorizer(mapnik_scheme,
                                           mapnik.Color(0, 0, 0, 0))
        bandInfo = self.getOneBandInformation(style['band'])
        minimum = style.get('min', 0)
        maximum = style.get('max', 255)
        minimum = bandInfo[minimum] if minimum in ('min', 'max') else minimum
        maximum = bandInfo[maximum] if maximum in ('min', 'max') else maximum
        if minimum == 'auto':
            if not (0 <= bandInfo['min'] <= 255
                    and 1 <= bandInfo['max'] <= 255):
                minimum = bandInfo['min']
            else:
                minimum = 0
        if maximum == 'auto':
            if not (0 <= bandInfo['min'] <= 255
                    and 1 <= bandInfo['max'] <= 255):
                maximum = bandInfo['max']
            else:
                maximum = 255
        if style.get('palette') == 'colortable':
            for value, color in enumerate(bandInfo['colortable']):
                colorizer.add_stop(value, mapnik.Color(*color))
        else:
            colors = style.get('palette', ['#000000', '#ffffff'])
            if not isinstance(colors, list):
                colors = self.getHexColors(colors)
            else:
                colors = [
                    color if isinstance(color, six.binary_type) else
                    color.encode('utf8') for color in colors
                ]
            if len(colors) < 2:
                raise TileSourceException(
                    'A palette must have at least 2 colors.')
            values = self.interpolateMinMax(minimum, maximum, len(colors))
            for value, color in sorted(zip(values, colors)):
                try:
                    colorizer.add_stop(value, mapnik.Color(color))
                except RuntimeError:
                    raise TileSourceException(
                        'Mapnik failed to parse color %r.' % color)

        return colorizer
Пример #8
0
    def __init__(self, path, maxSize=None, **kwargs):
        """
        Initialize the tile class.  See the base class for other available
        parameters.

        :param path: the associated file path.
        :param maxSize: either a number or an object with {'width': (width),
            'height': height} in pixels.  If None, the default max size is
            used.
        """
        super().__init__(path, **kwargs)

        self._maxSize = maxSize
        if isinstance(maxSize, str):
            try:
                maxSize = json.loads(maxSize)
            except Exception:
                raise TileSourceException(
                    'maxSize must be None, an integer, a dictionary, or a '
                    'JSON string that converts to one of those.')
        self.maxSize = maxSize

        largeImagePath = self._getLargeImagePath()

        # Some formats shouldn't be read this way, even if they could.  For
        # instances, mirax (mrxs) files look like JPEGs, but opening them as
        # such misses most of the data.
        if os.path.splitext(largeImagePath)[1] in ('.mrxs', ):
            raise TileSourceException('File cannot be opened via PIL.')
        try:
            self._pilImage = PIL.Image.open(largeImagePath)
        except OSError:
            raise TileSourceException('File cannot be opened via PIL.')
        # If this is encoded as a 32-bit integer or a 32-bit float, convert it
        # to an 8-bit integer.  This expects the source value to either have a
        # maximum of 1, 2^8-1, 2^16-1, 2^24-1, or 2^32-1, and scales it to
        # [0, 255]
        pilImageMode = self._pilImage.mode.split(';')[0]
        if pilImageMode in ('I', 'F'):
            imgdata = numpy.asarray(self._pilImage)
            maxval = 256**math.ceil(math.log(numpy.max(imgdata) + 1, 256)) - 1
            self._pilImage = PIL.Image.fromarray(
                numpy.uint8(numpy.multiply(imgdata, 255.0 / maxval)))
        self.sizeX = self._pilImage.width
        self.sizeY = self._pilImage.height
        # We have just one tile which is the entire image.
        self.tileWidth = self.sizeX
        self.tileHeight = self.sizeY
        self.levels = 1
        # Throw an exception if too big
        if self.tileWidth <= 0 or self.tileHeight <= 0:
            raise TileSourceException('PIL tile size is invalid.')
        maxWidth, maxHeight = getMaxSize(maxSize, self.defaultMaxSize())
        if self.tileWidth > maxWidth or self.tileHeight > maxHeight:
            raise TileSourceException('PIL tile size is too large.')
Пример #9
0
 def _parseOMEInfo(self):  # noqa
     if isinstance(self._omeinfo['Image'], dict):
         self._omeinfo['Image'] = [self._omeinfo['Image']]
     for img in self._omeinfo['Image']:
         if isinstance(img['Pixels'].get('TiffData'), dict):
             img['Pixels']['TiffData'] = [img['Pixels']['TiffData']]
         if isinstance(img['Pixels'].get('Plane'), dict):
             img['Pixels']['Plane'] = [img['Pixels']['Plane']]
         if isinstance(img['Pixels'].get('Channels'), dict):
             img['Pixels']['Channels'] = [img['Pixels']['Channels']]
     try:
         self._omebase = self._omeinfo['Image'][0]['Pixels']
         if isinstance(self._omebase.get('Plane'), dict):
             self._omebase['Plane'] = [self._omebase['Plane']]
         if ((not len(self._omebase['TiffData'])
              or len(self._omebase['TiffData']) == 1)
                 and (len(self._omebase.get('Plane', []))
                      or len(self._omebase.get('Channel', [])))):
             if not len(self._omebase['TiffData']
                        ) or self._omebase['TiffData'][0] == {}:
                 self._omebase['TiffData'] = self._omebase.get(
                     'Plane', self._omebase.get('Channel'))
             elif (int(self._omebase['TiffData'][0].get(
                     'PlaneCount', 0)) == len(
                         self._omebase.get('Plane',
                                           self._omebase.get('Channel',
                                                             [])))):
                 planes = copy.deepcopy(
                     self._omebase.get('Plane',
                                       self._omebase.get('Channel')))
                 for idx, plane in enumerate(planes):
                     plane['IFD'] = plane.get(
                         'IFD',
                         int(self._omebase['TiffData'][0].get('IFD', 0)) +
                         idx)
                 self._omebase['TiffData'] = planes
         if isinstance(self._omebase['TiffData'], dict):
             self._omebase['TiffData'] = [self._omebase['TiffData']]
         if len({
                 entry.get('UUID', {}).get('FileName', '')
                 for entry in self._omebase['TiffData']
         }) > 1:
             raise TileSourceException('OME Tiff references multiple files')
         if (len(self._omebase['TiffData']) != int(self._omebase['SizeC']) *
                 int(self._omebase['SizeT']) * int(self._omebase['SizeZ'])
                 or len(self._omebase['TiffData']) != len(
                     self._omebase.get('Plane',
                                       self._omebase['TiffData']))):
             raise TileSourceException(
                 'OME Tiff contains frames that contain multiple planes')
     except (KeyError, ValueError, IndexError):
         print('B')
         raise TileSourceException(
             'OME Tiff does not contain an expected record')
Пример #10
0
    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.
        """
        # Note this is the super of the parent class, not of this class.
        super(TiffFileTileSource, self).__init__(path, **kwargs)

        largeImagePath = self._getLargeImagePath()
        self._largeImagePath = largeImagePath

        try:
            base = TiledTiffDirectory(largeImagePath, 0, mustBeTiled=None)
        except TiffException:
            raise TileSourceException('Not a recognized OME Tiff')
        info = getattr(base, '_description_record', None)
        if not info or not info.get('OME'):
            raise TileSourceException('Not an OME Tiff')
        self._omeinfo = info['OME']
        self._checkForOMEZLoop(largeImagePath)
        self._parseOMEInfo()
        omeimages = [
            entry['Pixels'] for entry in self._omeinfo['Image'] if
            len(entry['Pixels']['TiffData']) == len(self._omebase['TiffData'])]
        levels = [max(0, int(math.ceil(math.log(max(
            float(entry['SizeX']) / base.tileWidth,
            float(entry['SizeY']) / base.tileHeight)) / math.log(2))))
            for entry in omeimages]
        omebylevel = dict(zip(levels, omeimages))
        self._omeLevels = [omebylevel.get(key) for key in range(max(omebylevel.keys()) + 1)]
        if base._tiffInfo.get('istiled'):
            self._tiffDirectories = [
                TiledTiffDirectory(largeImagePath, int(entry['TiffData'][0].get('IFD', 0)))
                if entry else None
                for entry in self._omeLevels]
        else:
            self._tiffDirectories = [
                TiledTiffDirectory(largeImagePath, 0, mustBeTiled=None)
                if entry else None
                for entry in self._omeLevels]
        self.tileWidth = base.tileWidth
        self.tileHeight = base.tileHeight
        self.levels = len(self._tiffDirectories)
        self.sizeX = base.imageWidth
        self.sizeY = base.imageHeight

        # We can get the embedded images, but we don't currently use non-tiled
        # images as associated images.  This would require enumerating tiff
        # directories not mentioned by the ome list.
        self._associatedImages = {}
Пример #11
0
    def _levelFromIfd(self, ifd, baseifd):
        """
        Get the level based on information in an ifd and on the full-resolution
        0-frame ifd.  An exception is raised if the ifd does not seem to
        represent a possible level.

        :param ifd: an ifd record returned from tifftools.
        :param baseifd: the ifd record of the full-resolution frame 0.
        :returns: the level, where self.levels - 1 is full resolution and 0 is
            the lowest resolution.
        """
        sizeX = ifd['tags'][tifftools.Tag.ImageWidth.value]['data'][0]
        sizeY = ifd['tags'][tifftools.Tag.ImageLength.value]['data'][0]
        tileWidth = baseifd['tags'][tifftools.Tag.TileWidth.value]['data'][0]
        tileHeight = baseifd['tags'][tifftools.Tag.TileLength.value]['data'][0]
        for tag in {
                tifftools.Tag.SamplesPerPixel.value,
                tifftools.Tag.BitsPerSample.value,
                tifftools.Tag.PlanarConfig.value,
                tifftools.Tag.Photometric.value,
                tifftools.Tag.Orientation.value,
                tifftools.Tag.Compression.value,
                tifftools.Tag.TileWidth.value,
                tifftools.Tag.TileLength.value,
        }:
            if ((tag in ifd['tags'] and tag not in baseifd['tags'])
                    or (tag not in ifd['tags'] and tag in baseifd['tags']) or
                (tag in ifd['tags'] and
                 ifd['tags'][tag]['data'] != baseifd['tags'][tag]['data'])):
                raise TileSourceException('IFD does not match first IFD.')
        sizes = [(self.sizeX, self.sizeY)]
        for level in range(self.levels - 1, -1, -1):
            if (sizeX, sizeY) in sizes:
                return level
            altsizes = []
            for w, h in sizes:
                w2f = int(math.floor(w / 2))
                h2f = int(math.floor(h / 2))
                w2c = int(math.ceil(w / 2))
                h2c = int(math.ceil(h / 2))
                w2t = int(math.floor(
                    (w / 2 + tileWidth - 1) / tileWidth)) * tileWidth
                h2t = int(math.floor(
                    (h / 2 + tileHeight - 1) / tileHeight)) * tileHeight
                for w2, h2 in [(w2f, h2f), (w2f, h2c), (w2c, h2f), (w2c, h2c),
                               (w2t, h2t)]:
                    if (w2, h2) not in altsizes:
                        altsizes.append((w2, h2))
            sizes = altsizes
        raise TileSourceException(
            'IFD size is not a power of two smaller than first IFD.')
Пример #12
0
    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().__init__(path, **kwargs)

        largeImagePath = self._getLargeImagePath()

        self._largeImagePath = largeImagePath
        self._pixelInfo = {}
        try:
            self._openjpeg = glymur.Jp2k(largeImagePath)
        except glymur.jp2box.InvalidJp2kError:
            raise TileSourceException(
                'File cannot be opened via Glymur and OpenJPEG.')
        glymur.set_option('lib.num_threads', multiprocessing.cpu_count())
        self._openjpegHandles = queue.LifoQueue()
        for _ in range(self._maxOpenHandles - 1):
            self._openjpegHandles.put(None)
        self._openjpegHandles.put(self._openjpeg)
        try:
            self.sizeY, self.sizeX = self._openjpeg.shape[:2]
        except IndexError:
            raise TileSourceException(
                'File cannot be opened via Glymur and OpenJPEG.')
        self.levels = int(self._openjpeg.codestream.segment[2].num_res) + 1
        self._minlevel = 0
        self.tileWidth = self.tileHeight = 2**int(
            math.ceil(
                max(
                    math.log(float(self.sizeX)) / math.log(2) - self.levels +
                    1,
                    math.log(float(self.sizeY)) / math.log(2) - self.levels +
                    1)))
        # Small and large tiles are both inefficient.  Large tiles don't work
        # with some viewers (leaflet and Slide Atlas, for instance)
        if self.tileWidth < self._minTileSize or self.tileWidth > self._maxTileSize:
            self.tileWidth = self.tileHeight = min(
                self._maxTileSize, max(self._minTileSize, self.tileWidth))
            self.levels = int(
                math.ceil(
                    math.log(
                        float(max(self.sizeX, self.sizeY)) / self.tileWidth) /
                    math.log(2))) + 1
            self._minlevel = self.levels - self._openjpeg.codestream.segment[
                2].num_res - 1
        self._getAssociatedImages()
Пример #13
0
 def getTile(self, x, y, z, pilImageAllowed=False, mayRedirect=False, **kwargs):
     if z != 0:
         raise TileSourceException('z layer does not exist')
     if x != 0:
         raise TileSourceException('x is outside layer')
     if y != 0:
         raise TileSourceException('y is outside layer')
     if (mayRedirect and not pilImageAllowed and
             cherrypy.request and
             self._pilFormatMatches(self._pilImage, mayRedirect, **kwargs)):
         url = '%s/api/v1/file/%s/download' % (
             cherrypy.request.base, self.item['largeImage']['fileId'])
         raise cherrypy.HTTPRedirect(url)
     return self._outputTile(self._pilImage, 'PIL', x, y, z,
                             pilImageAllowed, **kwargs)
Пример #14
0
 def getTile(self,
             x,
             y,
             z,
             pilImageAllowed=False,
             mayRedirect=False,
             **kwargs):
     if z != 0:
         raise TileSourceException('z layer does not exist')
     if x != 0:
         raise TileSourceException('x is outside layer')
     if y != 0:
         raise TileSourceException('y is outside layer')
     return self._outputTile(self._pilImage, 'PIL', x, y, z,
                             pilImageAllowed, **kwargs)
Пример #15
0
    def getTile(self, x, y, z, *args, **kwargs):
        widthCount = 2 ** z

        if not (0 <= x < float(self.sizeX) / self.tileWidth * 2 ** (
                z - self.maxLevel)):
            raise TileSourceException('x is outside layer')
        if not (0 <= y < float(self.sizeY) / self.tileHeight * 2 ** (
                z - self.maxLevel)):
            raise TileSourceException('y is outside layer')
        if not (self.minLevel <= z <= self.maxLevel):
            raise TileSourceException('z layer does not exist')

        xFraction = float(x) / (widthCount - 1) if z != 0 else 0
        yFraction = float(y) / (widthCount - 1) if z != 0 else 0

        backgroundColor = colorsys.hsv_to_rgb(
            h=(0.9 * xFraction),
            s=(0.3 + (0.7 * yFraction)),
            v=(0.3 + (0.7 * yFraction)),
        )
        rgbColor = tuple(int(val * 255) for val in backgroundColor)

        image = Image.new(
            mode='RGB',
            size=(self.tileWidth, self.tileHeight),
            color=(rgbColor if not self.fractal else (255, 255, 255))
        )
        imageDraw = ImageDraw.Draw(image)

        if self.fractal:
            self.fractalTile(image, x, y, widthCount, rgbColor)

        try:
            # the font size should fill the whole tile
            imageDrawFont = ImageFont.truetype(
                font='/usr/share/fonts/truetype/dejavu/DejaVuSansMono.ttf',
                size=int(0.15 * min(self.tileWidth, self.tileHeight))
            )
        except IOError:
            imageDrawFont = ImageFont.load_default()
        imageDraw.multiline_text(
            xy=(10, 10),
            text='x=%d\ny=%d\nz=%d' % (x, y, z),
            fill=(0, 0, 0),
            font=imageDrawFont
        )
        _counters['tiles'] += 1
        return self._outputTile(image, TILE_FORMAT_PIL, x, y, z, **kwargs)
Пример #16
0
 def getTileIOTiffException(self,
                            x,
                            y,
                            z,
                            pilImageAllowed=False,
                            sparseFallback=False,
                            exception=None,
                            **kwargs):
     if sparseFallback and z and PIL:
         noedge = kwargs.copy()
         noedge.pop('edge', None)
         image = self.getTile(x / 2,
                              y / 2,
                              z - 1,
                              pilImageAllowed=True,
                              sparseFallback=sparseFallback,
                              edge=False,
                              **noedge)
         if not isinstance(image, PIL.Image.Image):
             image = PIL.Image.open(BytesIO(image))
         image = image.crop(
             (self.tileWidth / 2 if x % 2 else 0,
              self.tileHeight / 2 if y % 2 else 0,
              self.tileWidth if x % 2 else self.tileWidth / 2,
              self.tileHeight if y % 2 else self.tileHeight / 2))
         image = image.resize((self.tileWidth, self.tileHeight))
         return self._outputTile(image, 'PIL', x, y, z, pilImageAllowed,
                                 **kwargs)
     raise TileSourceException('Internal I/O failure: %s' %
                               exception.args[0])
Пример #17
0
 def getTile(self,
             x,
             y,
             z,
             pilImageAllowed=False,
             numpyAllowed=False,
             **kwargs):
     self._xyzInRange(x, y, z)
     svslevel = self._svslevels[z]
     # When we read a region from the SVS, we have to ask for it in the
     # SVS level 0 coordinate system.  Our x and y is in tile space at the
     # specifed z level, so the offset in SVS level 0 coordinates has to be
     # scaled by the tile size and by the z level.
     scale = 2**(self.levels - 1 - z)
     offsetx = x * self.tileWidth * scale
     offsety = y * self.tileHeight * scale
     # We ask to read an area that will cover the tile at the z level.  The
     # scale we computed in the __init__ process for this svs level tells
     # how much larger a region we need to read.
     try:
         tile = self._openslide.read_region(
             (offsetx, offsety), svslevel['svslevel'],
             (self.tileWidth * svslevel['scale'],
              self.tileHeight * svslevel['scale']))
     except openslide.lowlevel.OpenSlideError as exc:
         raise TileSourceException('Failed to get OpenSlide region (%r).' %
                                   exc)
     # Always scale to the svs level 0 tile size.
     if svslevel['scale'] != 1:
         tile = tile.resize((self.tileWidth, self.tileHeight),
                            PIL.Image.LANCZOS)
     return self._outputTile(tile, TILE_FORMAT_PIL, x, y, z,
                             pilImageAllowed, numpyAllowed, **kwargs)
Пример #18
0
 def getTile(self,
             x,
             y,
             z,
             pilImageAllowed=False,
             numpyAllowed=False,
             **kwargs):
     self._xyzInRange(x, y, z)
     step = int(2**(self.levels - 1 - z))
     x0 = x * step * self.tileWidth
     x1 = min((x + 1) * step * self.tileWidth, self.sizeX)
     y0 = y * step * self.tileHeight
     y1 = min((y + 1) * step * self.tileHeight, self.sizeY)
     frame = kwargs.get('frame')
     frame = int(frame) if frame else 0
     if frame < 0 or frame >= len(self._nd2):
         raise TileSourceException('Frame does not exist')
     with self._tileLock:
         if frame in self._recentFrames:
             tileframe = self._recentFrames[frame]
         else:
             tileframe = self._nd2[frame]
             self._recentFrames[frame] = tileframe
         tile = tileframe[y0:y1:step, x0:x1:step].copy()
     return self._outputTile(tile, TILE_FORMAT_NUMPY, x, y, z,
                             pilImageAllowed, numpyAllowed, **kwargs)
Пример #19
0
    def _getAssociatedImage(self, imageKey):
        """
        Get an associated image in PIL format.

        :param imageKey: the key of the associated image.
        :return: the image in PIL format or None.
        """
        info = self._metadata['seriesAssociatedImages'].get(imageKey)
        if info is None:
            return
        series = info['seriesNum']
        with self._tileLock:
            try:
                javabridge.attach()
                image = self._bioimage.read(
                    series=series,
                    rescale=False,  # return internal data types
                    XYWH=(0, 0, info['sizeX'], info['sizeY']))
            except javabridge.JavaException as exc:
                es = javabridge.to_string(exc.throwable)
                raise TileSourceException('Failed to get Bioformat series (%s, %r).' % (es, (
                    series, info['sizeX'], info['sizeY'])))
            finally:
                if javabridge.get_env():
                    javabridge.detach()
        return large_image.tilesource.base._imageToPIL(image)
Пример #20
0
 def _getLargeImagePath(self):
     # If self.mayHaveAdjacentFiles is True, we try to use the girder
     # mount where companion files appear next to each other.
     try:
         largeImageFileId = self.item['largeImage']['fileId']
         if not hasattr(self, 'mayHaveAdjacentFiles'):
             # The item has adjacent files if there are any files that
             # are not the large image file or an original file it
             # was derived from.  This is always the case if there are 3
             # or more files.
             fileIds = [
                 str(file['_id'])
                 for file in Item().childFiles(self.item, limit=3)
             ]
             knownIds = [str(largeImageFileId)]
             if 'originalId' in self.item['largeImage']:
                 knownIds.append(str(self.item['largeImage']['originalId']))
             self.mayHaveAdjacentFiles = (len(fileIds) >= 3
                                          or fileIds[0] not in knownIds
                                          or fileIds[-1] not in knownIds)
         largeImageFile = File().load(largeImageFileId, force=True)
         if (any(ext in KnownExtensionsWithAdjacentFiles
                 for ext in largeImageFile['exts'])
                 or largeImageFile.get('mimeType')
                 in KnownMimeTypesWithAdjacentFiles):
             self.mayHaveAdjacentFiles = True
         largeImagePath = None
         if self.mayHaveAdjacentFiles and hasattr(File(),
                                                  'getGirderMountFilePath'):
             try:
                 largeImagePath = File().getGirderMountFilePath(
                     largeImageFile)
             except FilePathException:
                 pass
         if not largeImagePath:
             try:
                 largeImagePath = File().getLocalFilePath(largeImageFile)
             except AttributeError as e:
                 raise TileSourceException(
                     'No local file path for this file: %s' % e.args[0])
         return largeImagePath
     except (TileSourceAssetstoreException, FilePathException):
         raise
     except (KeyError, ValidationException, TileSourceException) as e:
         raise TileSourceException('No large image file in this item: %s' %
                                   e.args[0])
Пример #21
0
 def _initWithProjection(self, unitsPerPixel=None):
     """
     Initialize aspects of the class when a projection is set.
     """
     inProj = self._proj4Proj(InitPrefix + 'epsg:4326')
     # Since we already converted to bytes decoding is safe here
     outProj = self._proj4Proj(self.projection)
     if outProj.crs.is_geographic:
         raise TileSourceException(
             'Projection must not be geographic (it needs to use linear '
             'units, not longitude/latitude).')
     if unitsPerPixel:
         self.unitsAcrossLevel0 = float(unitsPerPixel) * self.tileSize
     else:
         self.unitsAcrossLevel0 = ProjUnitsAcrossLevel0.get(self.projection)
         if self.unitsAcrossLevel0 is None:
             # If unitsPerPixel is not specified, the horizontal distance
             # between -180,0 and +180,0 is used.  Some projections (such as
             # stereographic) will fail in this case; they must have a
             # unitsPerPixel specified.
             equator = pyproj.transform(inProj,
                                        outProj, [-180, 180], [0, 0],
                                        always_xy=True)
             self.unitsAcrossLevel0 = abs(equator[0][1] - equator[0][0])
             if not self.unitsAcrossLevel0:
                 raise TileSourceException(
                     'unitsPerPixel must be specified for this projection')
             if len(ProjUnitsAcrossLevel0) >= ProjUnitsAcrossLevel0_MaxSize:
                 ProjUnitsAcrossLevel0.clear()
             ProjUnitsAcrossLevel0[self.projection] = self.unitsAcrossLevel0
     # This was
     #   self.projectionOrigin = pyproj.transform(inProj, outProj, 0, 0)
     # but for consistency, it should probably always be (0, 0).  Whatever
     # renders the map would need the same offset as used here.
     self.projectionOrigin = (0, 0)
     # Calculate values for this projection
     self.levels = int(
         max(
             int(
                 math.ceil(
                     math.log(self.unitsAcrossLevel0 /
                              self.getPixelSizeInMeters() / self.tileWidth)
                     / math.log(2))) + 1, 1))
     # Report sizeX and sizeY as the whole world
     self.sizeX = 2**(self.levels - 1) * self.tileWidth
     self.sizeY = 2**(self.levels - 1) * self.tileHeight
Пример #22
0
 def getTile(self,
             x,
             y,
             z,
             pilImageAllowed=False,
             numpyAllowed=False,
             sparseFallback=False,
             **kwargs):
     frame = int(kwargs.get('frame') or 0)
     self._xyzInRange(
         x, y, z, frame,
         len(self._frames) if hasattr(self, '_frames') else None)
     if frame > 0:
         if self._frames[frame]['dirs'][z] is not None:
             dir = self._getDirFromCache(*self._frames[frame]['dirs'][z])
         else:
             dir = None
     else:
         dir = self._tiffDirectories[z]
     try:
         allowStyle = True
         if dir is None:
             try:
                 tile = self.getTileFromEmptyDirectory(x, y, z, **kwargs)
             except Exception:
                 if sparseFallback:
                     raise IOTiffException('Missing z level %d' % z)
                 else:
                     raise
             allowStyle = False
             format = TILE_FORMAT_PIL
         else:
             tile = dir.getTile(x, y)
             format = 'JPEG'
         if isinstance(tile, PIL.Image.Image):
             format = TILE_FORMAT_PIL
         if isinstance(tile, numpy.ndarray):
             format = TILE_FORMAT_NUMPY
         return self._outputTile(tile,
                                 format,
                                 x,
                                 y,
                                 z,
                                 pilImageAllowed,
                                 numpyAllowed,
                                 applyStyle=allowStyle,
                                 **kwargs)
     except InvalidOperationTiffException as e:
         raise TileSourceException(e.args[0])
     except IOTiffException as e:
         return self.getTileIOTiffException(x,
                                            y,
                                            z,
                                            pilImageAllowed=pilImageAllowed,
                                            numpyAllowed=numpyAllowed,
                                            sparseFallback=sparseFallback,
                                            exception=e,
                                            **kwargs)
Пример #23
0
 def getTile(self, x, y, z, pilImageAllowed=False, **kwargs):
     if z < 0 or z >= self.levels:
         raise TileSourceException('z layer does not exist')
     step = int(2**(self.levels - 1 - z))
     x0 = x * step * self.tileWidth
     x1 = min((x + 1) * step * self.tileWidth, self.sizeX)
     y0 = y * step * self.tileHeight
     y1 = min((y + 1) * step * self.tileHeight, self.sizeY)
     if x < 0 or x0 >= self.sizeX:
         raise TileSourceException('x is outside layer')
     if y < 0 or y0 >= self.sizeY:
         raise TileSourceException('y is outside layer')
     scale = None
     if z < self._minlevel:
         scale = int(2**(self._minlevel - z))
         step = int(2**(self.levels - 1 - self._minlevel))
     # possible open the file multiple times so multiple threads can access
     # it concurrently.
     while True:
         try:
             # A timeout prevents uniterupptable waits on some platforms
             openjpegHandle = self._openjpegHandles.get(timeout=1.0)
             break
         except queue.Empty:
             continue
     if openjpegHandle is None:
         openjpegHandle = glymur.Jp2k(self._largeImagePath)
     try:
         tile = openjpegHandle[y0:y1:step, x0:x1:step]
     finally:
         self._openjpegHandles.put(openjpegHandle)
     mode = 'L'
     if len(tile.shape) == 3:
         mode = ['L', 'LA', 'RGB', 'RGBA'][tile.shape[2] - 1]
     tile = PIL.Image.frombytes(mode, (tile.shape[1], tile.shape[0]), tile)
     if scale:
         tile = tile.resize((tile.size[0] // scale, tile.size[1] // scale),
                            PIL.Image.LANCZOS)
     if tile.size != (self.tileWidth, self.tileHeight):
         wrap = PIL.Image.new(mode, (self.tileWidth, self.tileHeight))
         wrap.paste(tile, (0, 0))
         tile = wrap
     return self._outputTile(tile, TILE_FORMAT_PIL, x, y, z,
                             pilImageAllowed, **kwargs)
Пример #24
0
 def getTile(self, x, y, z, pilImageAllowed=False, numpyAllowed=False,
             sparseFallback=False, **kwargs):
     if (z < 0 or z >= len(self._omeLevels) or (
             self._omeLevels[z] is not None and kwargs.get('frame') in (None, 0, '0', ''))):
         return super().getTile(
             x, y, z, pilImageAllowed=pilImageAllowed,
             numpyAllowed=numpyAllowed, sparseFallback=sparseFallback,
             **kwargs)
     frame = int(kwargs.get('frame') or 0)
     if frame < 0 or frame >= len(self._omebase['TiffData']):
         raise TileSourceException('Frame does not exist')
     subdir = None
     if self._omeLevels[z] is not None:
         dirnum = int(self._omeLevels[z]['TiffData'][frame].get('IFD', frame))
     else:
         dirnum = int(self._omeLevels[-1]['TiffData'][frame].get('IFD', frame))
         subdir = self.levels - 1 - z
     dir = self._getDirFromCache(dirnum, subdir)
     if subdir:
         scale = int(2 ** subdir)
         if (dir is None or
                 dir.tileWidth != self.tileWidth or dir.tileHeight != self.tileHeight or
                 abs(dir.imageWidth * scale - self.sizeX) > scale or
                 abs(dir.imageHeight * scale - self.sizeY) > scale):
             return super().getTile(
                 x, y, z, pilImageAllowed=pilImageAllowed,
                 numpyAllowed=numpyAllowed, sparseFallback=sparseFallback,
                 **kwargs)
     try:
         tile = dir.getTile(x, y)
         format = 'JPEG'
         if isinstance(tile, PIL.Image.Image):
             format = TILE_FORMAT_PIL
         if isinstance(tile, numpy.ndarray):
             format = TILE_FORMAT_NUMPY
         return self._outputTile(tile, format, x, y, z, pilImageAllowed,
                                 numpyAllowed, **kwargs)
     except InvalidOperationTiffException as e:
         raise TileSourceException(e.args[0])
     except IOTiffException as e:
         return self.getTileIOTiffException(
             x, y, z, pilImageAllowed=pilImageAllowed,
             numpyAllowed=numpyAllowed, sparseFallback=sparseFallback,
             exception=e, **kwargs)
Пример #25
0
    def getHexColors(palette):
        """
        Returns list of hex colors for a given color palette

        :returns: List of colors
        """
        try:
            return attrgetter(palette)(palettable).hex_colors
        except AttributeError:
            raise TileSourceException(
                'Palette is not a valid palettable path.')
Пример #26
0
    def _loadTileSource(cls, item, **kwargs):
        if 'largeImage' not in item:
            raise TileSourceException('No large image file in this item.')
        if item['largeImage'].get('expected'):
            raise TileSourceException('The large image file for this item is '
                                      'still pending creation.')

        sourceName = item['largeImage']['sourceName']
        try:
            # First try to use the tilesource we recorded as the preferred one.
            # This is faster than trying to find the best source each time.
            tileSource = girder_tilesource.AvailableGirderTileSources[
                sourceName](item, **kwargs)
        except TileSourceException:
            # We could try any source
            # tileSource = girder_tilesource.getGirderTileSource(item, **kwargs)
            # but, instead, log that the original source no longer works are
            # reraise the exception
            logger.warn('The original tile source for item %s is not working' %
                        item['_id'])
            raise
        return tileSource
Пример #27
0
    def interpolateMinMax(start, stop, count):
        """
        Returns interpolated values for a given
        start, stop and count

        :returns: List of interpolated values
        """
        try:
            step = (float(stop) - float(start)) / (float(count) - 1)
        except ValueError:
            raise TileSourceException(
                'Minimum and maximum values should be numbers, "auto", "min", or "max".')
        sequence = [float(start + i * step) for i in range(count)]
        return sequence
Пример #28
0
    def _bandNumber(self, band, exc=True):
        """
        Given a band number or interpretation name, return a validated band
        number.

        :param band: either -1, a positive integer, or the name of a band
            interpretation that is present in the tile source.
        :param exc: if True, raise an exception if no band matches.
        :returns: a validated band, either 1 or a positive integer, or None if
            no matching band and exceptions are not enabled.
        """
        if hasattr(self, '_netcdf') and (':' in str(band)
                                         or str(band).isdigit()):
            key = None
            if ':' in str(band):
                key, band = band.split(':', 1)
            if str(band).isdigit():
                band = int(band)
            else:
                band = 1
            if not key or key == 'default':
                key = self._netcdf.get('default', None)
                if key is None:
                    return band
            if key in self._netcdf['datasets']:
                return (key, band)
        bands = self.getBandInformation()
        if not isinstance(band, int):
            try:
                band = next(bandIdx for bandIdx in sorted(bands)
                            if band == bands[bandIdx]['interpretation'])
            except StopIteration:
                pass
        if hasattr(band, 'isdigit') and band.isdigit():
            band = int(band)
        if band != -1 and band not in bands:
            if exc:
                raise TileSourceException(
                    'Band has to be a positive integer, -1, or a band '
                    'interpretation found in the source.')
            return None
        return int(band)
Пример #29
0
    def __init__(self, path, projection=None, unitsPerPixel=None, **kwargs):
        """
        Initialize the tile class.  See the base class for other available
        parameters.

        :param path: a filesystem path for the tile source.
        :param projection: None to use pixel space, otherwise a proj4
            projection string or a case-insensitive string of the form
            'EPSG:<epsg number>'.  If a string and case-insensitively prefixed
            with 'proj4:', that prefix is removed.  For instance,
            'proj4:EPSG:3857', 'PROJ4:+init=epsg:3857', and '+init=epsg:3857',
            and 'EPSG:3857' are all equivilant.
        :param unitsPerPixel: The size of a pixel at the 0 tile size.  Ignored
            if the projection is None.  For projections, None uses the default,
            which is the distance between (-180,0) and (180,0) in EPSG:4326
            converted to the projection divided by the tile size.  Proj4
            projections that are not latlong (is_geographic is False) must
            specify unitsPerPixel.
        """
        super(GDALFileTileSource, self).__init__(path, **kwargs)
        self._logger = config.getConfig('logger')
        self._bounds = {}
        self._path = self._getLargeImagePath()
        try:
            self.dataset = gdal.Open(self._path, gdalconst.GA_ReadOnly)
        except RuntimeError:
            raise TileSourceException('File cannot be opened via GDAL')
        self._getDatasetLock = threading.RLock()
        self.tileSize = 256
        self.tileWidth = self.tileSize
        self.tileHeight = self.tileSize
        self._projection = projection
        if projection and projection.lower().startswith('epsg:'):
            projection = InitPrefix + projection.lower()
        if projection and not isinstance(projection, six.binary_type):
            projection = projection.encode('utf8')
        self.projection = projection
        try:
            with self._getDatasetLock:
                self.sourceSizeX = self.sizeX = self.dataset.RasterXSize
                self.sourceSizeY = self.sizeY = self.dataset.RasterYSize
        except AttributeError:
            raise TileSourceException('File cannot be opened via GDAL.')
        is_netcdf = self._checkNetCDF()
        try:
            scale = self.getPixelSizeInMeters()
        except RuntimeError:
            raise TileSourceException('File cannot be opened via GDAL.')
        if not scale and not is_netcdf:
            raise TileSourceException(
                'File does not have a projected scale, so will not be '
                'opened via GDAL.')
        self.sourceLevels = self.levels = int(
            max(
                0,
                math.ceil(
                    max(math.log(float(self.sizeX) / self.tileWidth),
                        math.log(float(self.sizeY) / self.tileHeight)) /
                    math.log(2))) + 1)
        self._unitsPerPixel = unitsPerPixel
        if self.projection:
            self._initWithProjection(unitsPerPixel)
        self._getTileLock = threading.Lock()
        self._setDefaultStyle()
Пример #30
0
    def _convertProjectionUnits(self, left, top, right, bottom, width, height,
                                units, **kwargs):
        """
        Given bound information and a units string that consists of a proj4
        projection (starts with `'proj4:'`, `'epsg:'`, `'+proj='` or is an
        enumerated value like `'wgs84'`), convert the bounds to either pixel or
        the class projection coordinates.

        :param left: the left edge (inclusive) of the region to process.
        :param top: the top edge (inclusive) of the region to process.
        :param right: the right edge (exclusive) of the region to process.
        :param bottom: the bottom edge (exclusive) of the region to process.
        :param width: the width of the region to process.  Ignored if both
            left and right are specified.
        :param height: the height of the region to process.  Ignores if both
            top and bottom are specified.
        :param units: either 'projection', a string starting with 'proj4:',
            'epsg:', or '+proj=' or a enumerated value like 'wgs84', or one of
            the super's values.
        :param **kwargs: optional parameters.
        :returns: left, top, right, bottom, units.  The new bounds in the
            either pixel or class projection units.
        """
        if not kwargs.get('unitsWH') or kwargs.get('unitsWH') == units:
            if left is None and right is not None and width is not None:
                left = right - width
            if right is None and left is not None and width is not None:
                right = left + width
            if top is None and bottom is not None and height is not None:
                top = bottom - height
            if bottom is None and top is not None and height is not None:
                bottom = top + height
        if (left is None and right is None) or (top is None
                                                and bottom is None):
            raise TileSourceException(
                'Cannot convert from projection unless at least one of '
                'left and right and at least one of top and bottom is '
                'specified.')
        if not self.projection:
            pleft, ptop = self.toNativePixelCoordinates(
                right if left is None else left,
                bottom if top is None else top, units)
            pright, pbottom = self.toNativePixelCoordinates(
                left if right is None else right,
                top if bottom is None else bottom, units)
            units = 'base_pixels'
        else:
            inProj = self._proj4Proj(units)
            outProj = self._proj4Proj(self.projection)
            pleft, ptop = pyproj.transform(inProj,
                                           outProj,
                                           right if left is None else left,
                                           bottom if top is None else top,
                                           always_xy=True)
            pright, pbottom = pyproj.transform(
                inProj,
                outProj,
                left if right is None else right,
                top if bottom is None else bottom,
                always_xy=True)
            units = 'projection'
        left = pleft if left is not None else None
        top = ptop if top is not None else None
        right = pright if right is not None else None
        bottom = pbottom if bottom is not None else None
        return left, top, right, bottom, units