def __init__(self, sliceShape, data2scene=QTransform(),
                 blockSize=512, overlap=0, overlap_draw=1e-3,
                 name="Unnamed Tiling"):
        """
        Args:
            sliceShape -- (width, height)
            data2scene -- QTransform from data to image coordinates (default:
                          identity transform)
            blockSize  -- base tile size: blockSize x blockSize (default 256)
            overlap    -- overlap between tiles positive number prevents rendering
                          artifacts between tiles for certain zoom levels (default 1)
        """
        self.blockSize = blockSize
        self.overlap = overlap
        self._patchAccessor = PatchAccessor(sliceShape[0],
                                            sliceShape[1],
                                            blockSize=self.blockSize)
        self._overlap_draw = overlap_draw
        self._overlap = overlap

        numPatches = self._patchAccessor.patchCount

        self.imageRectFs = [None]*numPatches
        self.dataRectFs  = [None]*numPatches
        self.tileRectFs  = [None]*numPatches
        self.imageRects  = [None]*numPatches
        self.dataRects   = [None]*numPatches
        self.tileRects   = [None]*numPatches
        self.sliceShape  = sliceShape
        self.name = name
        self.data2scene = data2scene
Exemple #2
0
    def __init__(self,
                 sliceShape,
                 data2scene=QTransform(),
                 blockSize=256,
                 overlap=0,
                 overlap_draw=1e-3,
                 name="Unnamed Tiling"):
        self.blockSize = blockSize
        self.overlap = overlap
        self._patchAccessor = PatchAccessor(sliceShape[0],
                                            sliceShape[1],
                                            blockSize=self.blockSize)
        self._overlap_draw = overlap_draw
        self._overlap = overlap

        numPatches = self._patchAccessor.patchCount

        self.imageRectFs = [None] * numPatches
        self.dataRectFs = [None] * numPatches
        self.tileRectFs = [None] * numPatches
        self.imageRects = [None] * numPatches
        self.dataRects = [None] * numPatches
        self.tileRects = [None] * numPatches
        self.sliceShape = sliceShape
        self.name = name
        self.data2scene = data2scene
Exemple #3
0
    def __init__(self, sliceShape, data2scene=QTransform(),
                 blockSize=256, overlap=0, overlap_draw=1e-3,
                 name="Unnamed Tiling"):
        """
        Args:
            sliceShape -- (width, height)
            data2scene -- QTransform from data to image coordinates (default:
                          identity transform)
            blockSize  -- base tile size: blockSize x blockSize (default 256)
            overlap    -- overlap between tiles positive number prevents rendering
                          artifacts between tiles for certain zoom levels (default 1)
        """
        self.blockSize = blockSize
        self.overlap = overlap
        self._patchAccessor = PatchAccessor(sliceShape[0],
                                            sliceShape[1],
                                            blockSize=self.blockSize)
        self._overlap_draw = overlap_draw
        self._overlap = overlap

        numPatches = self._patchAccessor.patchCount

        self.imageRectFs = [None]*numPatches
        self.dataRectFs  = [None]*numPatches
        self.tileRectFs  = [None]*numPatches
        self.imageRects  = [None]*numPatches
        self.dataRects   = [None]*numPatches
        self.tileRects   = [None]*numPatches
        self.sliceShape  = sliceShape
        self.name = name
        self.data2scene = data2scene
Exemple #4
0
    def __init__(self, sliceShape, data2scene=QTransform(), blockSize=256, overlap=1):
        self.blockSize = blockSize
        self.overlap = 1
        patchAccessor = PatchAccessor(sliceShape[0], sliceShape[1], blockSize=self.blockSize)

        self.imageRectFs = []
        self.tileRectFs = []
        self.imageRects  = []
        self.tileRects  = []
        self.sliceShape = sliceShape

        for patchNr in range(patchAccessor.patchCount):
            #the patch accessor uses the data coordinate system
            #
            #because the patch is drawn on the screen, its holds coordinates
            #corresponding to Qt's QGraphicsScene's system, which need to be
            #converted to scene coordinates
            
            #the image rectangle includes an overlap margin
            imageRectF = data2scene.mapRect(patchAccessor.patchRectF(patchNr, self.overlap))
            
            #the patch rectangle has no overlap
            patchRectF = data2scene.mapRect(patchAccessor.patchRectF(patchNr, 0))

            patchRect  = QRect(round(patchRectF.x()),     round(patchRectF.y()), \
                               round(patchRectF.width()), round(patchRectF.height()))
        
            #the image rectangles of neighboring patches can overlap slightly, to account
            #for inaccuracies in sub-pixel rendering of many ImagePatch objects
            imageRect   = QRect(round(imageRectF.x()),     round(imageRectF.y()), \
                                round(imageRectF.width()), round(imageRectF.height()))

            self.imageRectFs.append(imageRectF)
            self.tileRectFs.append(patchRectF)
            self.imageRects.append(imageRect)
            self.tileRects.append(patchRect)
 def shape(self, shape2D):
     assert len(shape2D) == 2
     self.setSceneRect(0,0, *shape2D)
     
     del self._renderThread
     del self.imagePatches
     
     self._patchAccessor = PatchAccessor(self.shape[1], self.shape[0], blockSize=self.blockSize)
     self.imagePatches = [[] for i in range(self._patchAccessor.patchCount)]
         
     self._renderThread = ImageSceneRenderThread(self.imagePatches, self.stackedImageSources, parent=self)
     self._renderThread.start()
     self._renderThread.patchAvailable.connect(self._schedulePatchRedraw)
     
     self._initializePatches()
Exemple #6
0
    def __init__(self, sliceShape, data2scene=QTransform(),
                 blockSize=256, overlap=0, overlap_draw=1e-3,
                 name="Unnamed Tiling"):
        self.blockSize = blockSize
        self.overlap = overlap
        self._patchAccessor = PatchAccessor(sliceShape[0],
                                            sliceShape[1],
                                            blockSize=self.blockSize)
        self._overlap_draw = overlap_draw
        self._overlap = overlap

        numPatches = self._patchAccessor.patchCount

        self.imageRectFs = [None]*numPatches
        self.dataRectFs  = [None]*numPatches
        self.tileRectFs  = [None]*numPatches
        self.imageRects  = [None]*numPatches
        self.dataRects   = [None]*numPatches
        self.tileRects   = [None]*numPatches
        self.sliceShape  = sliceShape
        self.name = name
        self.data2scene = data2scene
Exemple #7
0
class Tiling(object):
    '''Tiling.__init__()

    Arguments:
    sliceShape -- (width, height)
    data2scene -- QTransform from data to image coordinates (default:
                  identity transform)
    blockSize  -- base tile size: blockSize x blockSize (default 256)
    overlap    -- overlap between tiles positive number prevents rendering
                  artifacts between tiles for certain zoom levels (default 1)

    '''

    def __init__(self, sliceShape, data2scene=QTransform(),
                 blockSize=256, overlap=0, overlap_draw=1e-3,
                 name="Unnamed Tiling"):
        self.blockSize = blockSize
        self.overlap = overlap
        self._patchAccessor = PatchAccessor(sliceShape[0],
                                            sliceShape[1],
                                            blockSize=self.blockSize)
        self._overlap_draw = overlap_draw
        self._overlap = overlap

        numPatches = self._patchAccessor.patchCount

        self.imageRectFs = [None]*numPatches
        self.dataRectFs  = [None]*numPatches
        self.tileRectFs  = [None]*numPatches
        self.imageRects  = [None]*numPatches
        self.dataRects   = [None]*numPatches
        self.tileRects   = [None]*numPatches
        self.sliceShape  = sliceShape
        self.name = name
        self.data2scene = data2scene

    @property
    def data2scene(self):
        return self._data2scene

    @data2scene.setter
    def data2scene(self, data2scene):
        self._data2scene = data2scene
        self.scene2data, isInvertible = data2scene.inverted()
        assert isInvertible

        for patchNr in range(self._patchAccessor.patchCount):
            # the patch accessor uses the data coordinate system.
            # because the patch is drawn on the screen, its holds coordinates
            # corresponding to Qt's QGraphicsScene's system, which need to be
            # converted to scene coordinates

            # the image rectangle includes an overlap margin
            imageRectF = data2scene.mapRect(self._patchAccessor.patchRectF(patchNr, self.overlap))

            # the patch rectangle has per default no overlap
            patchRectF = data2scene.mapRect(self._patchAccessor.patchRectF(patchNr, 0))

            # add a little overlap when the overlap_draw setting is
            # activated
            if self._overlap_draw != 0:
                patchRectF = QRectF(patchRectF.x() - self._overlap_draw,
                                    patchRectF.y() - self._overlap_draw,
                                    patchRectF.width() + 2 * self._overlap_draw,
                                    patchRectF.height() + 2 * self._overlap_draw)

            patchRect = QRect(round(patchRectF.x()),
                              round(patchRectF.y()),
                              round(patchRectF.width()),
                              round(patchRectF.height()))

            # the image rectangles of neighboring patches can overlap
            # slightly, to account for inaccuracies in sub-pixel
            # rendering of many ImagePatch objects
            imageRect = QRect(round(imageRectF.x()),
                              round(imageRectF.y()),
                              round(imageRectF.width()),
                              round(imageRectF.height()))

            self.imageRectFs[patchNr] = imageRectF
            self.dataRectFs[ patchNr] = imageRectF
            self.tileRectFs[ patchNr] = patchRectF
            self.imageRects[ patchNr] = imageRect
            self.tileRects[  patchNr] = patchRect


    def boundingRectF(self):
        if self.tileRectFs:
            p = self.tileRectFs[-1]
            br = QRectF(0,0, p.x()+p.width(), p.y()+p.height())
        else:
            br = QRectF(0,0,0,0)
        return br

    def containsF(self, point):
        for i, p in enumerate(self.tileRectFs):
            if p.contains(point):
                return i

    def intersected(self, sceneRect):
        if not sceneRect.isValid():
            return range(len(self.tileRects))

        # Patch accessor uses data coordinates
        rect = self.data2scene.inverted()[0].mapRect(sceneRect)
        patchNumbers = self._patchAccessor.getPatchesForRect(
                            rect.topLeft().x(), rect.topLeft().y(),
                            rect.bottomRight().x(), rect.bottomRight().y() )
        return patchNumbers

    def __len__(self):
        return len(self.imageRectFs)
class Tiling(object):
    """
    Describes the geometry of a tiling, for easy access
    to patch rects, overall shape, tile size, and data2scene transform.
    """

    def __init__(self, sliceShape, data2scene=QTransform(),
                 blockSize=512, overlap=0, overlap_draw=1e-3,
                 name="Unnamed Tiling"):
        """
        Args:
            sliceShape -- (width, height)
            data2scene -- QTransform from data to image coordinates (default:
                          identity transform)
            blockSize  -- base tile size: blockSize x blockSize (default 256)
            overlap    -- overlap between tiles positive number prevents rendering
                          artifacts between tiles for certain zoom levels (default 1)
        """
        self.blockSize = blockSize
        self.overlap = overlap
        self._patchAccessor = PatchAccessor(sliceShape[0],
                                            sliceShape[1],
                                            blockSize=self.blockSize)
        self._overlap_draw = overlap_draw
        self._overlap = overlap

        numPatches = self._patchAccessor.patchCount

        self.imageRectFs = [None]*numPatches
        self.dataRectFs  = [None]*numPatches
        self.tileRectFs  = [None]*numPatches
        self.imageRects  = [None]*numPatches
        self.dataRects   = [None]*numPatches
        self.tileRects   = [None]*numPatches
        self.sliceShape  = sliceShape
        self.name = name
        self.data2scene = data2scene

    @property
    def data2scene(self):
        return self._data2scene

    @data2scene.setter
    def data2scene(self, data2scene):
        self._data2scene = data2scene
        self.scene2data, isInvertible = data2scene.inverted()
        assert isInvertible

        for patchNr in range(self._patchAccessor.patchCount):
            # the patch accessor uses the data coordinate system.
            # because the patch is drawn on the screen, its holds coordinates
            # corresponding to Qt's QGraphicsScene's system, which need to be
            # converted to scene coordinates

            # the image rectangle includes an overlap margin
            imageRectF = data2scene.mapRect(self._patchAccessor.patchRectF(patchNr, self.overlap))

            # the patch rectangle has per default no overlap
            patchRectF = data2scene.mapRect(self._patchAccessor.patchRectF(patchNr, 0))

            # add a little overlap when the overlap_draw setting is
            # activated
            if self._overlap_draw != 0:
                patchRectF = QRectF(patchRectF.x() - self._overlap_draw,
                                    patchRectF.y() - self._overlap_draw,
                                    patchRectF.width() + 2 * self._overlap_draw,
                                    patchRectF.height() + 2 * self._overlap_draw)

            patchRect = QRect(round(patchRectF.x()),
                              round(patchRectF.y()),
                              round(patchRectF.width()),
                              round(patchRectF.height()))

            # the image rectangles of neighboring patches can overlap
            # slightly, to account for inaccuracies in sub-pixel
            # rendering of many ImagePatch objects
            imageRect = QRect(round(imageRectF.x()),
                              round(imageRectF.y()),
                              round(imageRectF.width()),
                              round(imageRectF.height()))

            self.imageRectFs[patchNr] = imageRectF
            self.dataRectFs[ patchNr] = imageRectF
            self.tileRectFs[ patchNr] = patchRectF
            self.imageRects[ patchNr] = imageRect
            self.tileRects[  patchNr] = patchRect


    def boundingRectF(self):
        if self.tileRectFs:
            p = self.tileRectFs[-1]
            br = QRectF(0,0, p.x()+p.width(), p.y()+p.height())
        else:
            br = QRectF(0,0,0,0)
        return br

    def containsF(self, point):
        for i, p in enumerate(self.tileRectFs):
            if p.contains(point):
                return i

    def intersected(self, sceneRect):
        if not sceneRect.isValid():
            return range(len(self.tileRects))

        # Patch accessor uses data coordinates
        rect = self.data2scene.inverted()[0].mapRect(sceneRect)
        patchNumbers = self._patchAccessor.getPatchesForRect(
                            rect.topLeft().x(), rect.topLeft().y(),
                            rect.bottomRight().x(), rect.bottomRight().y() )
        return patchNumbers

    def __len__(self):
        return len(self.imageRectFs)
class ImageScene2D(QGraphicsScene):
    """
    The 2D scene description of a tiled image generated by evaluating
    an overlay stack, together with a 2D cursor.
    """
    
    # base patch size: blockSize x blockSize
    blockSize = 128
    # overlap between patches 
    # positive number prevents rendering artifacts between patches for certain zoom levels
    # increases the base blockSize 
    overlap = 1 
    
    # update delay when a new patch arrives in ms
    glUpdateDelay = 10
    
    @property
    def stackedImageSources(self):
        return self._stackedImageSources
    
    @stackedImageSources.setter
    def stackedImageSources(self, s):
        self._stackedImageSources = s
        s.isDirty.connect(self._invalidateRect)
        self._initializePatches()
        #s.stackChanged.connect(self._initializePatches)
        s.stackChanged.connect(partial(self._invalidateRect, QRect()))

    @property
    def shape(self):
        return (self.sceneRect().width(), self.sceneRect().height())
    @shape.setter
    def shape(self, shape2D):
        assert len(shape2D) == 2
        self.setSceneRect(0,0, *shape2D)
        
        del self._renderThread
        del self.imagePatches
        
        self._patchAccessor = PatchAccessor(self.shape[1], self.shape[0], blockSize=self.blockSize)
        self.imagePatches = [[] for i in range(self._patchAccessor.patchCount)]
            
        self._renderThread = ImageSceneRenderThread(self.imagePatches, self.stackedImageSources, parent=self)
        self._renderThread.start()
        self._renderThread.patchAvailable.connect(self._schedulePatchRedraw)
        
        self._initializePatches()
    
    def __init__( self ):
        QGraphicsScene.__init__(self)
        self._glWidget = None
        self._useGL = False
        self._updatableTiles = []

        # tile rendering
        self.imagePatches = None
        self._renderThread = None
        self._stackedImageSources = None
        self._numLayers = 0 #current number of 'layers'
    
        def cleanup():
            self._renderThread.stop()
        self.destroyed.connect(cleanup)

    def activateOpenGL( self, qglwidget ):
        self._useGL = True
        self._glWidget = qglwidget

        glDisable(GL_DEPTH_TEST)
        glEnable(GL_TEXTURE_2D)
        glClearColor(0.0, 0.0, 0.0, 0.0);
        glClear(GL_COLOR_BUFFER_BIT)

    def deactivateOpenGL( self ):
        self._useGL = False
        self._glWidget = None
    
    def _initializePatches(self):
        if self.stackedImageSources is None or self.shape == (0.0, 0.0):
            return
        
        if len(self.stackedImageSources) != self._numLayers:
            self._numLayers = len(self.stackedImageSources)
            #add an additional layer for the final composited image patch
            for i in range(self._patchAccessor.patchCount):
                r = self._patchAccessor.patchRectF(i, self.overlap)
                patches = [ImagePatch(r) for j in range(self._numLayers+1)]
                self.imagePatches[i] = patches
    

    def _invalidateRect(self, rect = QRect()):
        if not rect.isValid():
            #everything is invalidated
            #we cancel all requests
            self._renderThread.cancelAll()
            self._updatableTiles = []
        
        if self._stackedImageSources is not None and self._numLayers != len(self._stackedImageSources):
            self._initializePatches()
        
        for i,patch in enumerate(self.imagePatches):
            if not rect.isValid() or rect.intersects(patch[self._numLayers].rect):
                ##convention: if a rect is invalid, it is infinitely large
                patch[self._numLayers].dirty = True
                self._schedulePatchRedraw(i)

    def _schedulePatchRedraw(self, patchNr):
        p = self.imagePatches[patchNr][self._numLayers]
        self._updatableTiles.append(patchNr)
        if not self._useGL:
            self.invalidate(p.rectF, QGraphicsScene.BackgroundLayer)
        else:
            QTimer.singleShot(self.glUpdateDelay, self.update)

    def drawBackgroundSoftware(self, painter, rect):
        drawnTiles = 0
        for patches in self.imagePatches:
            patch = patches[self._numLayers]
            if not patch.rectF.intersect(rect): continue
            patch.mutex.lock()
            painter.drawImage(patch.rectF.topLeft(), patch.image)
            patch.mutex.unlock()
            drawnTiles +=1
        #print "ImageView2D.drawBackgroundSoftware: drew %d of %d tiles" % (drawnTiles, len(self.imagePatches)) 
    
    def drawBackgroundGL(self, painter, rect):
        painter.beginNativePainting()
        
        #This will clear the screen, but also introduce flickering
        glClearColor(0.0, 1.0, 0.0, 1.0);
        glClear(GL_COLOR_BUFFER_BIT);
        
        #update the textures of those patches that were updated
        for t in self._updatableTiles:
            patch = self.imagePatches[t][self._numLayers]
            if patch.texture > -1:
                self._glWidget.deleteTexture(patch.texture)
            patch.texture = self._glWidget.bindTexture(patch.image)
            #see 'backingstore' example by Ariya Hidayat
            glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST)
            glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST)
            #this ensures a seamless transition between tiles
            glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE)
            glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE)
        self._updatableTiles = []
        
        drawnTiles = 0
        for patches in self.imagePatches:
            patch = patches[self._numLayers]
            if not patch.rectF.intersect(rect): continue
            patch.drawTexture()
            drawnTiles +=1

        #print "ImageView2D.drawBackgroundGL: drew %d of %d tiles" % (drawnTiles, len(self.imagePatches))
        painter.endNativePainting()

    def drawBackground(self, painter, rect):
        #Abandon previous workloads
        #FIXME FIXME
        #self._renderThread.queue.clear()
        #self._renderThread.newerDataPending.set()

        #Find all patches that intersect the given 'rect'.
        for i,patch in enumerate(self.imagePatches):
            patch = patch[self._numLayers]
            if patch.dirty and rect.intersects(patch.rectF):
                self._renderThread.requestPatch(i)
        
        if self._useGL:
            self.drawBackgroundGL(painter, rect)
        else:
            self.drawBackgroundSoftware(painter, rect)