def testNonEmptyLayerStackModel(self): lsm = LayerStackModel() lsm.append(self.layer1) lsm.append(self.layer2) lsm.append(self.layer3) ip = ImagePump(lsm, SliceProjection()) self.assertEqual(len(lsm), 3) self.assertEqual(len(ip.stackedImageSources), 3) self.assertEqual(len(ip.syncedSliceSources), 3) self.assertEqual(len(ip.stackedImageSources.getRegisteredLayers()), 3) for layer in lsm: self.assertTrue(ip.stackedImageSources.isRegistered(layer)) lsm.deleteSelected() self.assertEqual(len(lsm), 2) self.assertEqual(len(ip.stackedImageSources), 2) self.assertEqual(len(ip.syncedSliceSources), 2) self.assertEqual(len(ip.stackedImageSources.getRegisteredLayers()), 2) for layer in lsm: self.assertTrue(ip.stackedImageSources.isRegistered(layer)) lsm.clear() self.assertEqual(len(lsm), 0) self.assertEqual(len(ip.stackedImageSources), 0) self.assertEqual(len(ip.syncedSliceSources), 0) self.assertEqual(len(ip.stackedImageSources.getRegisteredLayers()), 0)
def test_repaint_after_visible_change(self): self.model = LayerStackModel() self.o1 = Layer([]) self.o1.name = "Fancy Layer" self.o1.opacity = 0.5 self.model.append(self.o1) self.o2 = Layer([]) self.o2.name = "Some other Layer" self.o2.opacity = 0.25 self.model.append(self.o2) self.view = LayerWidget(None, self.model) self.view.show() self.view.updateGeometry() self.w = QWidget() self.lh = QHBoxLayout(self.w) self.lh.addWidget(self.view) self.w.setGeometry(100, 100, 300, 300) self.w.show() self.w.raise_() # Run the test within the GUI event loop QTimer.singleShot(500, self.impl) self.app.exec_() # Were there errors? assert not TestLayerWidget.errors, "There were GUI errors/failures. See above."
def initLayerstackModel(self): self.layerstack = LayerStackModel() self.layerWidget.init(self.layerstack) model = self.layerstack self.UpButton.clicked.connect(model.moveSelectedUp) model.canMoveSelectedUp.connect(self.UpButton.setEnabled) self.DownButton.clicked.connect(model.moveSelectedDown) model.canMoveSelectedDown.connect(self.DownButton.setEnabled) self.DeleteButton.clicked.connect(model.deleteSelected) model.canDeleteSelected.connect(self.DeleteButton.setEnabled)
def testNonEmptyLayerStackModel( self ): lsm = LayerStackModel() lsm.append(self.layer1) lsm.append(self.layer2) lsm.append(self.layer3) ip = ImagePump( lsm, SliceProjection() ) self.assertEqual( len(lsm), 3 ) self.assertEqual( len(ip.stackedImageSources), 3 ) self.assertEqual( len(ip.syncedSliceSources), 3 ) self.assertEqual( len(ip.stackedImageSources.getRegisteredLayers()), 3 ) for layer in lsm: self.assertTrue( ip.stackedImageSources.isRegistered(layer) ) lsm.deleteSelected() self.assertEqual( len(lsm), 2 ) self.assertEqual( len(ip.stackedImageSources), 2 ) self.assertEqual( len(ip.syncedSliceSources), 2 ) self.assertEqual( len(ip.stackedImageSources.getRegisteredLayers()), 2 ) for layer in lsm: self.assertTrue( ip.stackedImageSources.isRegistered(layer) ) lsm.clear() self.assertEqual( len(lsm), 0 ) self.assertEqual( len(ip.stackedImageSources), 0 ) self.assertEqual( len(ip.syncedSliceSources), 0 ) self.assertEqual( len(ip.stackedImageSources.getRegisteredLayers()), 0 )
class ImageScene2D_LazyTest(ut.TestCase): def setUp(self): self.layerstack = LayerStackModel() self.sims = StackedImageSources(self.layerstack) self.g = Graph() self.op = OpLazy(self.g) self.ds = LazyflowSource(self.op.Output) self.ss = SliceSource(self.ds, projectionAlongTZC) self.layer = GrayscaleLayer(self.ds, normalize=False) self.layerstack.append(self.layer) self.ims = imsfac.createImageSource(self.layer, [self.ss]) self.sims.register(self.layer, self.ims) self.scene = ImageScene2D(PositionModel(), (0, 0, 0), preemptive_fetch_number=0) self.scene.setCacheSize(1) self.scene.stackedImageSources = self.sims self.scene.dataShape = (30, 30) def renderScene(self, s, exportFilename=None, joinRendering=True): img = QImage(30, 30, QImage.Format_ARGB32_Premultiplied) img.fill(Qt.white) p = QPainter(img) s.render(p) # trigger a rendering of the whole scene if joinRendering: # wait for all the data to arrive s.joinRenderingAllTiles(viewport_only=False) # There is no viewport! # finally, render everything s.render(p) p.end() if exportFilename is not None: img.save(exportFilename) return byte_view(img) def testLazy(self): for i in range(3): self.op.setConstant(i) aimg = self.renderScene(self.scene, "/tmp/a_%03d.png" % i) assert numpy.all(aimg[:, :, 0] == i), "!= %d, [0,0,0]=%d" % (i, aimg[0, 0, 0]) self.op.setConstant(42) self.op.setDelay(1) aimg = self.renderScene(self.scene, joinRendering=False, exportFilename="/tmp/x_%03d.png" % i) # this should be "i", not 255 (the default background for the imagescene) assert numpy.all(aimg[:, :, 0] == i), "!= %d, [0,0,0]=%d" % (i, aimg[0, 0, 0]) # Now give the scene time to update before we change it again... self.scene.joinRenderingAllTiles(viewport_only=False)
def testRegisterAndDeregister(self): lsm = LayerStackModel() sims = StackedImageSources(lsm) self.assertEqual(len(lsm), 0) self.assertEqual(len(sims), 0) lsm.append(self.layer1) lsm.append(self.layer2) lsm.append(self.layer3) self.assertEqual(lsm.layerIndex(self.layer1), 2) self.assertEqual(lsm.layerIndex(self.layer2), 1) self.assertEqual(lsm.layerIndex(self.layer3), 0) self.assertEqual(len(lsm), 3) self.assertEqual(len(sims), 0) self.assertFalse(sims.isRegistered(self.layer2)) sims.register(self.layer2, self.ims2) self.assertTrue(sims.isRegistered(self.layer2)) self.assertEqual(len(sims), 1) self.assertEqual(sims.getImageSource(0), self.ims2) sims.register(self.layer1, self.ims1) sims.register(self.layer3, self.ims3) sims.deregister(self.layer2) self.assertTrue(sims.isRegistered(self.layer1)) self.assertFalse(sims.isRegistered(self.layer2)) self.assertTrue(sims.isRegistered(self.layer3)) self.assertEqual(len(lsm), 3) self.assertEqual(len(sims), 2) self.assertEqual(sims.getImageSource(0), self.ims3) self.assertEqual(sims.getImageSource(1), self.ims1) for i, v in enumerate(sims): if i == 0: self.assertEqual(len(v), 3) self.assertEqual(v[0], self.layer3.visible) self.assertEqual(v[1], self.layer3.opacity) self.assertEqual(v[2], self.ims3) elif i == 1: self.assertEqual(len(v), 3) self.assertEqual(v[0], self.layer1.visible) self.assertEqual(v[1], self.layer1.opacity) self.assertEqual(v[2], self.ims1) else: raise Exception("unexpected index") sims.deregister(self.layer1) sims.deregister(self.layer3) self.assertEqual(len(lsm), 3) self.assertEqual(len(sims), 0) lsm.clear()
def createWidget(self, parent): a = (numpy.random.random((1, 100, 200, 300, 1)) * 255).astype(numpy.uint8) source = ArraySource(a) layerstack = LayerStackModel() layerstack.append(GrayscaleLayer(source)) editor = VolumeEditor(layerstack, labelsink=None, parent=self) widget = VolumeEditorWidget(parent=parent) if not _has_lazyflow: widget.setEnabled(False) widget.init(editor) editor.dataShape = a.shape return widget
def createWidget(self, parent): a = (numpy.random.random((1,100,200,300,1))*255).astype(numpy.uint8) source = ArraySource(a) layerstack = LayerStackModel() layerstack.append( GrayscaleLayer( source ) ) editor = VolumeEditor(layerstack, labelsink=None, parent=self) widget = VolumeEditorWidget(parent=parent) if not _has_lazyflow: widget.setEnabled(False) widget.init(editor) editor.dataShape = a.shape return widget
class Main(QMainWindow): def __init__(self, useGL, argv): QMainWindow.__init__(self) self.initUic() def initUic(self): self.g = g = Graph() #get the absolute path of the 'ilastik' module uic.loadUi("designerElements/MainWindow.ui", self) self.actionQuit.triggered.connect(qApp.quit) def toggleDebugPatches(show): self.editor.showDebugPatches = show self.actionShowDebugPatches.toggled.connect(toggleDebugPatches) self.layerstack = LayerStackModel() readerNew = op.OpH5ReaderBigDataset(g) readerNew.inputs["Filenames"].setValue([ "scripts/CB_compressed_XY.h5", "scripts/CB_compressed_XZ.h5", "scripts/CB_compressed_YZ.h5" ]) readerNew.inputs["hdf5Path"].setValue("volume/data") datasrc = LazyflowSource(readerNew.outputs["Output"]) layer1 = GrayscaleLayer(datasrc) layer1.name = "Big Data" self.layerstack.append(layer1) shape = readerNew.outputs["Output"].meta.shape print shape self.editor = VolumeEditor(shape, self.layerstack) #self.editor.setDrawingEnabled(False) self.volumeEditorWidget.init(self.editor) model = self.editor.layerStack self.layerWidget.init(model) self.UpButton.clicked.connect(model.moveSelectedUp) model.canMoveSelectedUp.connect(self.UpButton.setEnabled) self.DownButton.clicked.connect(model.moveSelectedDown) model.canMoveSelectedDown.connect(self.DownButton.setEnabled) self.DeleteButton.clicked.connect(model.deleteSelected) model.canDeleteSelected.connect(self.DeleteButton.setEnabled)
def setUp( self ): dataShape = (1, 900, 400, 10, 1) # t,x,y,z,c data = np.indices(dataShape)[3].astype(np.uint8) # Data is labeled according to z-index self.ds1 = ArraySource( data ) self.CONSTANT = 13 self.ds2 = ConstantSource( self.CONSTANT ) self.layer1 = GrayscaleLayer( self.ds1, normalize=False ) self.layer1.visible = True self.layer1.opacity = 1.0 self.layer2 = GrayscaleLayer( self.ds2, normalize=False ) self.lsm = LayerStackModel() self.pump = ImagePump( self.lsm, SliceProjection(), sync_along=(0,1,2) )
class ImageScene2DTest( ut.TestCase ): def setUp( self ): self.app = QApplication([], False) self.layerstack = LayerStackModel() self.sims = StackedImageSources( self.layerstack ) self.GRAY = 201 self.ds = ConstantSource(self.GRAY) self.layer = GrayscaleLayer( self.ds ) self.layerstack.append(self.layer) self.ims = imsfac.createImageSource( self.layer, [self.ds] ) self.sims.register(self.layer, self.ims) self.scene = ImageScene2D(self.app) self.scene.stackedImageSources = self.sims self.scene.sceneShape = (310,290) def renderScene( self, s): img = QImage(310,290,QImage.Format_ARGB32_Premultiplied) p = QPainter(img) s.render(p) s.joinRendering() s.render(p) p.end() return byte_view(img) def testBasicImageRenderingCapability( self ): import time aimg = self.renderScene(self.scene) self.assertTrue(np.all(aimg[:,:,0:3] == self.GRAY)) self.assertTrue(np.all(aimg[:,:,3] == 255)) @ut.skipIf(os.getenv('TRAVIS'), 'fails on TRAVIS CI due to unknown reasons') def testToggleVisibilityOfOneLayer( self ): aimg = self.renderScene(self.scene) self.assertTrue(np.all(aimg[:,:,0:3] == self.GRAY)) self.assertTrue(np.all(aimg[:,:,3] == 255)) self.layer.visible = False aimg = self.renderScene(self.scene) self.assertTrue(np.all(aimg[:,:,0:3] == 255)) # all white self.assertTrue(np.all(aimg[:,:,3] == 255)) self.layer.visible = True aimg = self.renderScene(self.scene) self.assertTrue(np.all(aimg[:,:,0:3] == self.GRAY)) self.assertTrue(np.all(aimg[:,:,3] == 255))
def test_repaint_after_visible_change(self): self.model = LayerStackModel() self.o1 = Layer([]) self.o1.name = "Fancy Layer" self.o1.opacity = 0.5 self.model.append(self.o1) self.o2 = Layer([]) self.o2.name = "Some other Layer" self.o2.opacity = 0.25 self.model.append(self.o2) self.view = LayerWidget(None, self.model) self.view.show() self.view.updateGeometry() self.w = QWidget() self.lh = QHBoxLayout(self.w) self.lh.addWidget(self.view) self.w.setGeometry(100, 100, 300, 300) self.w.show() # Run the test within the GUI event loop QTimer.singleShot(500, self.impl ) self.app.exec_() # Were there errors? assert not TestLayerWidget.errors, "There were GUI errors/failures. See above."
def testStackedImageSourcesProperty(self): s = ImageScene2D(PositionModel(), (0, 3, 4), preemptive_fetch_number=0) self.assertEqual(len(s.stackedImageSources), 0) sims = StackedImageSources(LayerStackModel()) s.stackedImageSources = sims self.assertEqual(id(s.stackedImageSources), id(sims))
def setUp(self): self.layerstack = LayerStackModel() self.sims = StackedImageSources(self.layerstack) self.GRAY = 201 self.ds = ConstantSource(self.GRAY) self.layer = GrayscaleLayer(self.ds) self.layerstack.append(self.layer) self.ims = imsfac.createImageSource(self.layer, [self.ds]) self.sims.register(self.layer, self.ims) self.scene = ImageScene2D(PositionModel(), (0, 3, 4), preemptive_fetch_number=0) self.scene.stackedImageSources = self.sims self.scene.dataShape = (310, 290)
def testRegisterAndDeregister( self ): lsm = LayerStackModel() sims = StackedImageSources( lsm ) self.assertEqual( len(lsm), 0 ) self.assertEqual( len(sims), 0 ) lsm.append(self.layer1) lsm.append(self.layer2) lsm.append(self.layer3) self.assertEqual( lsm.layerIndex(self.layer1), 2 ) self.assertEqual( lsm.layerIndex(self.layer2), 1 ) self.assertEqual( lsm.layerIndex(self.layer3), 0 ) self.assertEqual( len(lsm), 3 ) self.assertEqual( len(sims), 0 ) self.assertFalse(sims.isRegistered(self.layer2)) sims.register( self.layer2, self.ims2 ) self.assertTrue(sims.isRegistered(self.layer2)) self.assertEqual( len(sims), 1 ) self.assertEqual( sims.getImageSource(0), self.ims2 ) sims.register( self.layer1, self.ims1 ) sims.register( self.layer3, self.ims3 ) sims.deregister( self.layer2 ) self.assertTrue( sims.isRegistered( self.layer1 )) self.assertFalse( sims.isRegistered( self.layer2 )) self.assertTrue( sims.isRegistered( self.layer3 )) self.assertEqual( len(lsm), 3 ) self.assertEqual( len(sims), 2 ) self.assertEqual( sims.getImageSource(0), self.ims3 ) self.assertEqual( sims.getImageSource(1), self.ims1 ) for i,v in enumerate(sims): if i == 0: self.assertEqual(len(v), 3) self.assertEqual(v[0], self.layer3.visible) self.assertEqual(v[1], self.layer3.opacity) self.assertEqual(v[2], self.ims3) elif i == 1: self.assertEqual(len(v), 3) self.assertEqual(v[0], self.layer1.visible) self.assertEqual(v[1], self.layer1.opacity) self.assertEqual(v[2], self.ims1) else: raise Exception("unexpected index") sims.deregister( self.layer1 ) sims.deregister( self.layer3 ) self.assertEqual( len(lsm), 3 ) self.assertEqual( len(sims), 0 ) lsm.clear()
def __init__(self, posModel, along, preemptive_fetch_number=5, parent=None, name="Unnamed Scene", swapped_default=False): """ * preemptive_fetch_number -- number of prefetched slices; 0 turns the feature off * swapped_default -- whether axes should be swapped by default. """ QGraphicsScene.__init__(self, parent=parent) self._along = along self._posModel = posModel # QGraphicsItems can change this if they are in a state that should temporarily forbid brushing # (For example, when the slice intersection marker is in 'draggable' state.) self.allow_brushing = True self._dataShape = (0, 0) self._dataRectItem = None #A QGraphicsRectItem (or None) self._offsetX = 0 self._offsetY = 0 self.name = name self._stackedImageSources = StackedImageSources(LayerStackModel()) self._showTileOutlines = False # FIXME: We don't show the red 'progress pies' because they look terrible. # If we could fix their timing, maybe it would be worth it. self._showTileProgress = False self._tileProvider = None self._dirtyIndicator = None self._prefetching_enabled = False self._swappedDefault = swapped_default self.reset() # BowWave preemptive caching self.setPreemptiveFetchNumber(preemptive_fetch_number) self._course = (1, 1) # (along, pos or neg direction) self._time = self._posModel.time self._channel = self._posModel.channel self._posModel.timeChanged.connect(self._onTimeChanged) self._posModel.channelChanged.connect(self._onChannelChanged) self._posModel.slicingPositionChanged.connect( self._onSlicingPositionChanged) self._allTilesCompleteEvent = threading.Event() self.dirty = False # We manually keep track of the tile-wise QGraphicsItems that # we've added to the scene in this dict, otherwise we would need # to use O(N) lookups for every tile by calling QGraphicsScene.items() self.tile_graphicsitems = defaultdict( set) # [Tile.id] -> set(QGraphicsItems)
class Main(QMainWindow): def __init__(self, useGL, argv): QMainWindow.__init__(self) self.initUic() def initUic(self): self.g=g=Graph() #get the absolute path of the 'ilastik' module uic.loadUi("designerElements/MainWindow.ui", self) self.actionQuit.triggered.connect(qApp.quit) def toggleDebugPatches(show): self.editor.showDebugPatches = show self.actionShowDebugPatches.toggled.connect(toggleDebugPatches) self.layerstack = LayerStackModel() readerNew=op.OpH5ReaderBigDataset(g) readerNew.inputs["Filenames"].setValue(["scripts/CB_compressed_XY.h5","scripts/CB_compressed_XZ.h5","scripts/CB_compressed_YZ.h5"]) readerNew.inputs["hdf5Path"].setValue("volume/data") datasrc = LazyflowSource(readerNew.outputs["Output"]) layer1 = GrayscaleLayer( datasrc ) layer1.name = "Big Data" self.layerstack.append(layer1) shape=readerNew.outputs["Output"].meta.shape print shape self.editor = VolumeEditor(shape, self.layerstack) #self.editor.setDrawingEnabled(False) self.volumeEditorWidget.init(self.editor) model = self.editor.layerStack self.layerWidget.init(model) self.UpButton.clicked.connect(model.moveSelectedUp) model.canMoveSelectedUp.connect(self.UpButton.setEnabled) self.DownButton.clicked.connect(model.moveSelectedDown) model.canMoveSelectedDown.connect(self.DownButton.setEnabled) self.DeleteButton.clicked.connect(model.deleteSelected) model.canDeleteSelected.connect(self.DeleteButton.setEnabled)
def setUp(self): self.layerstack = LayerStackModel() self.sims = StackedImageSources(self.layerstack) self.g = Graph() self.op = OpLazy(self.g) self.ds = LazyflowSource(self.op.Output) self.ss = SliceSource(self.ds, projectionAlongTZC) self.layer = GrayscaleLayer(self.ds, normalize=False) self.layerstack.append(self.layer) self.ims = imsfac.createImageSource(self.layer, [self.ss]) self.sims.register(self.layer, self.ims) self.scene = ImageScene2D(PositionModel(), (0, 0, 0), preemptive_fetch_number=0) self.scene.setCacheSize(1) self.scene.stackedImageSources = self.sims self.scene.dataShape = (30, 30)
def setUp( self ): self.GRAY1 = 60 self.ds1 = ConstantSource( self.GRAY1 ) self.GRAY2 = 120 self.ds2 = ConstantSource( self.GRAY2 ) self.GRAY3 = 190 self.ds3 = ConstantSource( self.GRAY3 ) self.layer1 = GrayscaleLayer( self.ds1, normalize = False ) self.layer1.visible = False self.layer1.opacity = 0.1 self.ims1 = GrayscaleImageSource( self.ds1, self.layer1 ) self.layer2 = GrayscaleLayer( self.ds2, normalize = False ) self.layer2.visible = True self.layer2.opacity = 0.3 self.ims2 = GrayscaleImageSource( self.ds2, self.layer2 ) self.layer3 = GrayscaleLayer( self.ds3, normalize = False ) self.layer3.visible = True self.layer3.opacity = 1.0 self.ims3 = GrayscaleImageSource( self.ds3, self.layer3 ) lsm = LayerStackModel() lsm.append(self.layer1) lsm.append(self.layer2) lsm.append(self.layer3) self.lsm = lsm sims = StackedImageSources( lsm ) sims.register( self.layer1, self.ims1 ) sims.register( self.layer2, self.ims2 ) sims.register( self.layer3, self.ims3 ) self.sims = sims
def setUp( self ): dataShape = (1, 900, 400, 10, 1) # t,x,y,z,c data = np.indices(dataShape)[3] # Data is labeled according to z-index self.ds1 = ArraySource( data ) self.CONSTANT = 13 self.ds2 = ConstantSource( self.CONSTANT ) self.layer1 = GrayscaleLayer( self.ds1 ) self.layer1.visible = True self.layer1.opacity = 1.0 self.layer2 = GrayscaleLayer( self.ds2 ) self.lsm = LayerStackModel() self.pump = ImagePump( self.lsm, SliceProjection() )
def setUp( self ): self.layerstack = LayerStackModel() self.sims = StackedImageSources( self.layerstack ) self.GRAY = 201 self.ds = ConstantSource(self.GRAY) self.layer = GrayscaleLayer( self.ds ) self.layerstack.append(self.layer) self.ims = imsfac.createImageSource( self.layer, [self.ds] ) self.sims.register(self.layer, self.ims) self.scene = ImageScene2D(PositionModel(), (0,3,4), preemptive_fetch_number=0) self.scene.stackedImageSources = self.sims self.scene.dataShape = (310,290)
def setUp( self ): self.app = QApplication([], False) self.layerstack = LayerStackModel() self.sims = StackedImageSources( self.layerstack ) self.GRAY = 201 self.ds = ConstantSource(self.GRAY) self.layer = GrayscaleLayer( self.ds ) self.layerstack.append(self.layer) self.ims = imsfac.createImageSource( self.layer, [self.ds] ) self.sims.register(self.layer, self.ims) self.scene = ImageScene2D(self.app) self.scene.stackedImageSources = self.sims self.scene.sceneShape = (310,290)
def __init__(self, posModel, along, preemptive_fetch_number=5, parent=None, name="Unnamed Scene", swapped_default=False): """ * preemptive_fetch_number -- number of prefetched slices; 0 turns the feature off * swapped_default -- whether axes should be swapped by default. """ QGraphicsScene.__init__(self, parent=parent) self._along = along self._posModel = posModel self._dataShape = (0, 0) self._dataRect = None #A QGraphicsRectItem (or None) self._offsetX = 0 self._offsetY = 0 self.name = name self._stackedImageSources = StackedImageSources(LayerStackModel()) self._showTileOutlines = False self._showTileProgress = True self._tileProvider = None self._dirtyIndicator = None self._prefetching_enabled = False self._swappedDefault = swapped_default self.reset() # BowWave preemptive caching self.setPreemptiveFetchNumber(preemptive_fetch_number) self._course = (1, 1) # (along, pos or neg direction) self._time = self._posModel.time self._channel = self._posModel.channel self._posModel.timeChanged.connect(self._onTimeChanged) self._posModel.channelChanged.connect(self._onChannelChanged) self._posModel.slicingPositionChanged.connect( self._onSlicingPositionChanged) self._allTilesCompleteEvent = threading.Event()
def setUp( self ): self.layerstack = LayerStackModel() self.sims = StackedImageSources( self.layerstack ) self.g = Graph() self.op = OpLazy(self.g) self.ds = LazyflowSource( self.op.Output ) self.ss = SliceSource( self.ds, projectionAlongTZC ) self.layer = GrayscaleLayer(self.ds, normalize = False) self.layerstack.append(self.layer) self.ims = imsfac.createImageSource( self.layer, [self.ss] ) self.sims.register(self.layer, self.ims) self.scene = ImageScene2D(PositionModel(), (0,0,0), preemptive_fetch_number=0) self.scene.setCacheSize(1) self.scene.stackedImageSources = self.sims self.scene.dataShape = (30,30)
def testAddingAndRemoving( self ): lsm = LayerStackModel() self.assertEqual(len(lsm), 0) lsm.append(self.l1) self.assertEqual(len(lsm), 1) self.assertEqual(lsm[0].name, self.l1.name ) lsm.append(self.l2) self.assertEqual(len(lsm), 2) self.assertEqual(lsm[0].name, self.l2.name ) self.assertEqual(lsm[1].name, self.l1.name ) lsm.insert(1, self.l3) self.assertEqual(len(lsm), 3) self.assertEqual(lsm[0].name, self.l2.name ) self.assertEqual(lsm[1].name, self.l3.name ) self.assertEqual(lsm[2].name, self.l1.name ) lsm.selectRow( 0 ) lsm.deleteSelected() self.assertEqual(len(lsm), 2) self.assertEqual(lsm[0].name, self.l3.name ) self.assertEqual(lsm[1].name, self.l1.name ) lsm.clear() self.assertEqual(len(lsm), 0)
def testMovingLayers( self ): lsm = LayerStackModel() lsm.append(self.l1) lsm.append(self.l2) lsm.append(self.l3) lsm.selectRow(1) self.assertEqual(len(lsm),3) self.assertEqual(lsm[0].name, self.l3.name ) self.assertEqual(lsm[1].name, self.l2.name ) self.assertEqual(lsm[2].name, self.l1.name ) self.assertEqual(lsm.selectedRow(), 1) lsm.moveSelectedDown() self.assertEqual(lsm.selectedRow(), 2) self.assertEqual(len(lsm),3) self.assertEqual(lsm[0].name, self.l3.name ) self.assertEqual(lsm[1].name, self.l1.name ) self.assertEqual(lsm[2].name, self.l2.name ) lsm.selectRow(1) lsm.moveSelectedUp() self.assertEqual(lsm.selectedRow(), 0) self.assertEqual(len(lsm),3) self.assertEqual(lsm[0].name, self.l1.name ) self.assertEqual(lsm[1].name, self.l3.name ) self.assertEqual(lsm[2].name, self.l2.name ) # moving topmost layer up => nothing should happen lsm.selectRow(0) self.assertEqual(lsm.selectedRow(), 0) self.assertEqual(lsm[0].name, self.l1.name ) self.assertEqual(lsm[1].name, self.l3.name ) self.assertEqual(lsm[2].name, self.l2.name ) lsm.moveSelectedUp() self.assertEqual(lsm.selectedRow(), 0) self.assertEqual(lsm[0].name, self.l1.name ) self.assertEqual(lsm[1].name, self.l3.name ) self.assertEqual(lsm[2].name, self.l2.name ) # moving bottommost layer down => nothing should happen lsm.selectRow(2) self.assertEqual(lsm.selectedRow(), 2) self.assertEqual(lsm[0].name, self.l1.name ) self.assertEqual(lsm[1].name, self.l3.name ) self.assertEqual(lsm[2].name, self.l2.name ) lsm.moveSelectedDown() self.assertEqual(lsm.selectedRow(), 2) self.assertEqual(lsm[0].name, self.l1.name ) self.assertEqual(lsm[1].name, self.l3.name ) self.assertEqual(lsm[2].name, self.l2.name )
# ******************************************************************************* if __name__ == "__main__": # make the program quit on Ctrl+C import signal signal.signal(signal.SIGINT, signal.SIG_DFL) import sys, numpy from PyQt5.QtWidgets import QPushButton, QHBoxLayout, QVBoxLayout from volumina.pixelpipeline.datasources import ArraySource, ConstantSource app = QApplication(sys.argv) model = LayerStackModel() o1 = Layer([ConstantSource()]) o1.name = "Fancy Layer" o1.opacity = 0.5 model.append(o1) o2 = Layer([ConstantSource()]) o2.name = "Some other Layer" o2.opacity = 0.25 o2.numberOfChannels = 3 model.append(o2) o3 = Layer([ConstantSource()]) o3.name = "Invisible Layer" o3.opacity = 0.15
def createWidget(self, parent): model = LayerStackModel() o1 = Layer() o1.name = "Fancy Layer" o1.opacity = 0.5 model.append(o1) o2 = Layer() o2.name = "Some other Layer" o2.opacity = 0.25 model.append(o2) o3 = Layer() o3.name = "Invisible Layer" o3.opacity = 0.15 o3.visible = False model.append(o3) o4 = Layer() o4.name = "Fancy Layer II" o4.opacity = 0.95 model.append(o4) o5 = Layer() o5.name = "Fancy Layer III" o5.opacity = 0.65 model.append(o5) view = LayerWidget(parent, model) view.updateGeometry() return view
class TestLayerWidget(ut.TestCase): """ Create two layers and add them to a LayerWidget. Then change one of the layer visibilities and verify that the layer widget appearance updates. At the time of this writing, the widget doesn't properly repaint the selected layer (all others repaint correctly). """ @classmethod def setUpClass(cls): if 'TRAVIS' in os.environ: # This test fails on Travis-CI for unknown reasons, # probably due to the variability of time.sleep(). # Skip it on Travis-CI. import nose raise nose.SkipTest cls.app = QApplication([]) cls.errors = False @classmethod def tearDownClass(cls): del cls.app def impl(self): try: # Change the visibility of the *selected* layer self.o2.visible = False # Make sure the GUI is caught up on paint events QApplication.processEvents() # We must sleep for the screenshot to be right. time.sleep(0.1) self.w.repaint() # Capture the window before we change anything beforeImg = QPixmap.grabWindow(self.w.winId()).toImage() # Change the visibility of the *selected* layer self.o2.visible = True self.w.repaint() # Make sure the GUI is caught up on paint events QApplication.processEvents() # We must sleep for the screenshot to be right. time.sleep(0.1) # Capture the window now that we've changed a layer. afterImg = QPixmap.grabWindow(self.w.winId()).toImage() # Optional: Save the files so we can inspect them ourselves... #beforeImg.save('before.png') #afterImg.save('after.png') # Before and after should NOT match. assert beforeImg != afterImg except: # Catch all exceptions and print them # We must finish so we can quit the app. import traceback traceback.print_exc() TestLayerWidget.errors = True qApp.quit() def test_repaint_after_visible_change(self): self.model = LayerStackModel() self.o1 = Layer([]) self.o1.name = "Fancy Layer" self.o1.opacity = 0.5 self.model.append(self.o1) self.o2 = Layer([]) self.o2.name = "Some other Layer" self.o2.opacity = 0.25 self.model.append(self.o2) self.view = LayerWidget(None, self.model) self.view.show() self.view.updateGeometry() self.w = QWidget() self.lh = QHBoxLayout(self.w) self.lh.addWidget(self.view) self.w.setGeometry(100, 100, 300, 300) self.w.show() self.w.raise_() # Run the test within the GUI event loop QTimer.singleShot(500, self.impl) self.app.exec_() # Were there errors? assert not TestLayerWidget.errors, "There were GUI errors/failures. See above."
def __init__(self, parent=None, model=None): QListView.__init__(self, parent) if model is None: model = LayerStackModel() self.init(model)
def testAddingAndRemovingLayers( self ): lsm = LayerStackModel() sims = StackedImageSources( lsm ) ims_view = sims.viewImageSources() self.assertEqual(len(lsm), 0) self.assertEqual(len(sims), 0) self.assertEqual(len(ims_view), 0) lsm.append(self.layer1) lsm.append(self.layer2) sims.register(self.layer1, self.ims1) sims.register(self.layer2, self.ims2) self.assertEqual(sims.isRegistered(self.layer1), True) self.assertEqual(sims.isRegistered(self.layer2), True) self.assertEqual(len(lsm), 2) self.assertEqual(len(sims), 2) self.assertEqual(len(ims_view), 2) self.assertEqual(ims_view[0], self.ims2) self.assertEqual(ims_view[1], self.ims1) lsm.append(self.layer3) self.assertEqual(len(lsm), 3) self.assertEqual(len(sims), 2) self.assertEqual(len(ims_view), 2) self.assertEqual(ims_view[0], self.ims2) self.assertEqual(ims_view[1], self.ims1) self.assertEqual(sims.isRegistered(self.layer1), True) self.assertEqual(sims.isRegistered(self.layer2), True) lsm.selectRow(1) # layer2 lsm.deleteSelected() self.assertEqual(len(lsm), 2) self.assertEqual(len(sims), 1) self.assertEqual(len(ims_view), 1) self.assertEqual(ims_view[0], self.ims1) self.assertEqual(sims.isRegistered(self.layer1), True) self.assertEqual(sims.isRegistered(self.layer2), False) lsm.selectRow(0) # layer3 lsm.deleteSelected() self.assertEqual(len(lsm), 1) self.assertEqual(len(sims), 1) self.assertEqual(len(ims_view), 1) self.assertEqual(ims_view[0], self.ims1) self.assertEqual(sims.isRegistered(self.layer1), True) self.assertEqual(sims.isRegistered(self.layer2), False) sims.deregister(self.layer1) self.assertEqual(len(lsm), 1) self.assertEqual(len(sims), 0) self.assertEqual(len(ims_view), 0) self.assertEqual(sims.isRegistered(self.layer1), False) self.assertEqual(sims.isRegistered(self.layer2), False)
def testMovingLayers(self): lsm = LayerStackModel() lsm.append(self.l1) lsm.append(self.l2) lsm.append(self.l3) lsm.selectRow(1) self.assertEqual(len(lsm), 3) self.assertEqual(lsm[0].name, self.l3.name) self.assertEqual(lsm[1].name, self.l2.name) self.assertEqual(lsm[2].name, self.l1.name) self.assertEqual(lsm.selectedRow(), 1) lsm.moveSelectedDown() self.assertEqual(lsm.selectedRow(), 2) self.assertEqual(len(lsm), 3) self.assertEqual(lsm[0].name, self.l3.name) self.assertEqual(lsm[1].name, self.l1.name) self.assertEqual(lsm[2].name, self.l2.name) lsm.selectRow(1) lsm.moveSelectedUp() self.assertEqual(lsm.selectedRow(), 0) self.assertEqual(len(lsm), 3) self.assertEqual(lsm[0].name, self.l1.name) self.assertEqual(lsm[1].name, self.l3.name) self.assertEqual(lsm[2].name, self.l2.name) # moving topmost layer up => nothing should happen lsm.selectRow(0) self.assertEqual(lsm.selectedRow(), 0) self.assertEqual(lsm[0].name, self.l1.name) self.assertEqual(lsm[1].name, self.l3.name) self.assertEqual(lsm[2].name, self.l2.name) lsm.moveSelectedUp() self.assertEqual(lsm.selectedRow(), 0) self.assertEqual(lsm[0].name, self.l1.name) self.assertEqual(lsm[1].name, self.l3.name) self.assertEqual(lsm[2].name, self.l2.name) # moving bottommost layer down => nothing should happen lsm.selectRow(2) self.assertEqual(lsm.selectedRow(), 2) self.assertEqual(lsm[0].name, self.l1.name) self.assertEqual(lsm[1].name, self.l3.name) self.assertEqual(lsm[2].name, self.l2.name) lsm.moveSelectedDown() self.assertEqual(lsm.selectedRow(), 2) self.assertEqual(lsm[0].name, self.l1.name) self.assertEqual(lsm[1].name, self.l3.name) self.assertEqual(lsm[2].name, self.l2.name)
# i f _ _ n a m e _ _ = = " _ _ m a i n _ _ " * #******************************************************************************* if __name__ == "__main__": #make the program quit on Ctrl+C import signal signal.signal(signal.SIGINT, signal.SIG_DFL) import sys, numpy from PyQt4.QtGui import QApplication, QPushButton, QHBoxLayout, QVBoxLayout from volumina.pixelpipeline.datasources import ArraySource app = QApplication(sys.argv) model = LayerStackModel() o1 = Layer() o1.name = "Fancy Layer" o1.opacity = 0.5 model.append(o1) o2 = Layer() o2.name = "Some other Layer" o2.opacity = 0.25 model.append(o2) o3 = Layer() o3.name = "Invisible Layer" o3.opacity = 0.15 o3.visible = False
class Viewer(QMainWindow): """High-level API to view multi-dimensional arrays. Properties: title -- window title """ def __init__(self, parent=None): QMainWindow.__init__(self, parent) uiDirectory = os.path.split(volumina.__file__)[0] if uiDirectory == '': uiDirectory = '.' loadUi(uiDirectory + '/viewer.ui', self) self._dataShape = None self._viewerInitialized = False self.editor = None self.viewingWidget = None self.actionQuit.triggered.connect(qApp.quit) #when connecting in renderScreenshot to a partial(...) function, #we need to remember the created function to be able to disconnect #to it later self._renderScreenshotDisconnect = None self.initLayerstackModel() self.actionCurrentView = QAction(QIcon(), "Only for selected view", self.menuView) f = self.actionCurrentView.font() f.setBold(True) self.actionCurrentView.setFont(f) self.editor = VolumeEditor(self.layerstack) #make sure the layer stack widget, which is the right widget #managed by the splitter self.splitter shows up correctly #TODO: find a proper way of doing this within the designer def adjustSplitter(): s = self.splitter.sizes() s = [int(0.66*s[0]), s[0]-int(0.66*s[0])] self.splitter.setSizes(s) QTimer.singleShot(0, adjustSplitter) def initLayerstackModel(self): self.layerstack = LayerStackModel() self.layerWidget.init(self.layerstack) model = self.layerstack self.UpButton.clicked.connect(model.moveSelectedUp) model.canMoveSelectedUp.connect(self.UpButton.setEnabled) self.DownButton.clicked.connect(model.moveSelectedDown) model.canMoveSelectedDown.connect(self.DownButton.setEnabled) self.DeleteButton.clicked.connect(model.deleteSelected) model.canDeleteSelected.connect(self.DeleteButton.setEnabled) @property def dataShape(self): return self._dataShape @dataShape.setter def dataShape(self, s): if s is None: return assert len(s) == 5 self._dataShape = s self.editor.dataShape = s if not self._viewerInitialized: self._viewerInitialized = True self.viewer.init(self.editor) #make sure the data shape is correctly set #(some signal/slot connections may be set up in the above init) self.editor.dataShape = s #if its 2D, maximize the corresponding window if len([i for i in list(self.dataShape)[1:4] if i == 1]) == 1: viewAxis = [i for i in range(1,4) if self.dataShape[i] != 1][0] - 1 self.viewer.quadview.switchMinMax(viewAxis) def addGrayscaleLayer(self, a, name=None, direct=False): source,self.dataShape = createDataSource(a,True) layer = GrayscaleLayer(source, direct=direct) if name: layer.name = name self.layerstack.append(layer) return layer def addAlphaModulatedLayer(self, a, name=None): source,self.dataShape = createDataSource(a,True) layer = AlphaModulatedLayer(source) if name: layer.name = name self.layerstack.append(layer) return layer def addRGBALayer(self, a, name=None): source,self.dataShape = createDataSource(a,True) layer = RGBALayer(source[0],source[1],source[2]) if name: layer.name = name self.layerstack.append(layer) return layer def addRandomColorsLayer(self, a, name=None, direct=False): layer = self.addColorTableLayer(a, name, colortable=None, direct=direct) layer.colortableIsRandom = True layer.zeroIsTransparent = True return layer def addColorTableLayer(self, a, name=None, colortable=None, direct=False, clickFunctor=None): if colortable is None: colortable = self._randomColors() source,self.dataShape = createDataSource(a,True) if clickFunctor is None: layer = ColortableLayer(source, colortable, direct=direct) else: layer = ClickableColortableLayer(self.editor, clickFunctor, source, colortable, direct=direct) if name: layer.name = name self.layerstack.append(layer) return layer def addRelabelingColorTableLayer(self, a, name=None, relabeling=None, colortable=None, direct=False, clickFunctor=None): if colortable is None: colortable = self._randomColors() source = RelabelingArraySource(a) if relabeling is None: source.setRelabeling(numpy.zeros(numpy.max(a)+1, dtype=a.dtype)) else: source.setRelabeling(relabeling) if colortable is None: colortable = [QColor(0,0,0,0).rgba(), QColor(255,0,0).rgba()] if clickFunctor is None: layer = ColortableLayer(source, colortable, direct=direct) else: layer = ClickableColortableLayer(self.editor, clickFunctor, source, colortable, direct=direct) if name: layer.name = name self.layerstack.append(layer) return (layer, source) def addClickableSegmentationLayer(self, a, name=None, direct=False): M = a.max() clickedObjects = dict() #maps from object to the label that is used for it usedLabels = set() def onClick(layer, pos5D, pos): obj = layer.data.originalData[pos5D] if obj in clickedObjects: layer._datasources[0].setRelabelingEntry(obj, 0) usedLabels.remove( clickedObjects[obj] ) del clickedObjects[obj] else: labels = sorted(list(usedLabels)) #find first free entry if labels: for l in range(1, labels[-1]+2): if l not in labels: break assert l not in usedLabels else: l = 1 usedLabels.add(l) clickedObjects[obj] = l layer._datasources[0].setRelabelingEntry(obj, l) colortable = volumina.layer.generateRandomColors(1000, "hsv", {"v": 1.0}, zeroIsTransparent=True) layer, source = self.addRelabelingColorTableLayer(a, clickFunctor=onClick, name=None, relabeling=numpy.zeros(M+1, dtype=a.dtype), colortable=colortable, direct=direct) if name is not None: layer.name = name layer.zeroIsTransparent = True layer.colortableIsRandom = True return layer def _randomColors(self, M=256): """Generates a pleasing color table with M entries.""" colors = [] for i in range(M): if i == 0: colors.append(QColor(0, 0, 0, 0).rgba()) else: h, s, v = random.random(), random.random(), 1.0 color = numpy.asarray(colorsys.hsv_to_rgb(h, s, v)) * 255 qColor = QColor(*color) colors.append(qColor.rgba()) return colors
def testFirstFullyOpaque(self): lsm = LayerStackModel() sims = StackedImageSources(lsm) self.assertEqual(sims.firstFullyOpaque(), None) lsm.append(self.layer1) lsm.append(self.layer2) lsm.append(self.layer3) self.assertEqual(lsm.layerIndex(self.layer1), 2) self.assertEqual(lsm.layerIndex(self.layer2), 1) self.assertEqual(lsm.layerIndex(self.layer3), 0) sims.register(self.layer1, self.ims1) sims.register(self.layer2, self.ims2) sims.register(self.layer3, self.ims3) self.assertEqual(sims.firstFullyOpaque(), 0) lsm.clear() sims = StackedImageSources(lsm) lsm.append(self.layer2) lsm.append(self.layer3) lsm.append(self.layer1) self.assertEqual(lsm.layerIndex(self.layer1), 0) self.assertEqual(lsm.layerIndex(self.layer2), 2) self.assertEqual(lsm.layerIndex(self.layer3), 1) sims.register(self.layer1, self.ims1) sims.register(self.layer2, self.ims2) sims.register(self.layer3, self.ims3) self.assertEqual(sims.firstFullyOpaque(), 1) lsm.clear() sims = StackedImageSources(lsm) lsm.append(self.layer2) lsm.append(self.layer1) self.assertEqual(lsm.layerIndex(self.layer1), 0) self.assertEqual(lsm.layerIndex(self.layer2), 1) sims.register(self.layer1, self.ims1) sims.register(self.layer2, self.ims2) self.assertEqual(sims.firstFullyOpaque(), None) lsm.clear()
def testAddingAndRemovingLayers(self): lsm = LayerStackModel() sims = StackedImageSources(lsm) ims_view = sims.viewImageSources() self.assertEqual(len(lsm), 0) self.assertEqual(len(sims), 0) self.assertEqual(len(ims_view), 0) lsm.append(self.layer1) lsm.append(self.layer2) sims.register(self.layer1, self.ims1) sims.register(self.layer2, self.ims2) self.assertEqual(sims.isRegistered(self.layer1), True) self.assertEqual(sims.isRegistered(self.layer2), True) self.assertEqual(len(lsm), 2) self.assertEqual(len(sims), 2) self.assertEqual(len(ims_view), 2) self.assertEqual(ims_view[0], self.ims2) self.assertEqual(ims_view[1], self.ims1) lsm.append(self.layer3) self.assertEqual(len(lsm), 3) self.assertEqual(len(sims), 2) self.assertEqual(len(ims_view), 2) self.assertEqual(ims_view[0], self.ims2) self.assertEqual(ims_view[1], self.ims1) self.assertEqual(sims.isRegistered(self.layer1), True) self.assertEqual(sims.isRegistered(self.layer2), True) lsm.selectRow(1) # layer2 lsm.deleteSelected() self.assertEqual(len(lsm), 2) self.assertEqual(len(sims), 1) self.assertEqual(len(ims_view), 1) self.assertEqual(ims_view[0], self.ims1) self.assertEqual(sims.isRegistered(self.layer1), True) self.assertEqual(sims.isRegistered(self.layer2), False) lsm.selectRow(0) # layer3 lsm.deleteSelected() self.assertEqual(len(lsm), 1) self.assertEqual(len(sims), 1) self.assertEqual(len(ims_view), 1) self.assertEqual(ims_view[0], self.ims1) self.assertEqual(sims.isRegistered(self.layer1), True) self.assertEqual(sims.isRegistered(self.layer2), False) sims.deregister(self.layer1) self.assertEqual(len(lsm), 1) self.assertEqual(len(sims), 0) self.assertEqual(len(ims_view), 0) self.assertEqual(sims.isRegistered(self.layer1), False) self.assertEqual(sims.isRegistered(self.layer2), False)
class Viewer(QMainWindow): """High-level API to view multi-dimensional arrays. Properties: title -- window title """ def __init__(self, parent=None): QMainWindow.__init__(self, parent) uiDirectory = os.path.split(volumina.__file__)[0] if uiDirectory == '': uiDirectory = '.' loadUi(uiDirectory + '/viewer.ui', self) self._dataShape = None self._viewerInitialized = False self.editor = None self.viewingWidget = None self.actionQuit.triggered.connect(qApp.quit) #when connecting in renderScreenshot to a partial(...) function, #we need to remember the created function to be able to disconnect #to it later self._renderScreenshotDisconnect = None self.initLayerstackModel() self.actionCurrentView = QAction(QIcon(), "Only for selected view", self.menuView) f = self.actionCurrentView.font() f.setBold(True) self.actionCurrentView.setFont(f) # Lazy import here to prevent this module from ignoring volumine.NO3D flag. from volumina.volumeEditor import VolumeEditor self.editor = VolumeEditor(self.layerstack, parent=self) #make sure the layer stack widget, which is the right widget #managed by the splitter self.splitter shows up correctly #TODO: find a proper way of doing this within the designer def adjustSplitter(): s = self.splitter.sizes() s = [int(0.66*s[0]), s[0]-int(0.66*s[0])] self.splitter.setSizes(s) QTimer.singleShot(0, adjustSplitter) @property def title(self): return self.windowTitle() @title.setter def title(self, t): self.setWindowTitle(t) def initLayerstackModel(self): self.layerstack = LayerStackModel() self.layerWidget.init(self.layerstack) model = self.layerstack self.UpButton.clicked.connect(model.moveSelectedUp) model.canMoveSelectedUp.connect(self.UpButton.setEnabled) self.DownButton.clicked.connect(model.moveSelectedDown) model.canMoveSelectedDown.connect(self.DownButton.setEnabled) self.DeleteButton.clicked.connect(model.deleteSelected) model.canDeleteSelected.connect(self.DeleteButton.setEnabled) @property def dataShape(self): return self._dataShape @dataShape.setter def dataShape(self, s): if s is None: return assert len(s) == 5 self._dataShape = s self.editor.dataShape = s if not self._viewerInitialized: self._viewerInitialized = True self.viewer.init(self.editor) #make sure the data shape is correctly set #(some signal/slot connections may be set up in the above init) self.editor.dataShape = s #FIXME: this code is broken #if its 2D, maximize the corresponding window #if len([i for i in list(self.dataShape)[1:4] if i == 1]) == 1: # viewAxis = [i for i in range(1,4) if self.dataShape[i] == 1][0] - 1 # self.viewer.quadview.switchMinMax(viewAxis) def addGrayscaleLayer(self, a, name=None, direct=False): source,self.dataShape = createDataSource(a,True) layer = GrayscaleLayer(source, direct=direct) layer.numberOfChannels = self.dataShape[-1] if name: layer.name = name self.layerstack.append(layer) return layer def addAlphaModulatedLayer(self, a, name=None, **kwargs): source,self.dataShape = createDataSource(a,True) layer = AlphaModulatedLayer(source, **kwargs) if name: layer.name = name self.layerstack.append(layer) return layer def addRGBALayer(self, a, name=None): assert a.shape[2] >= 3 sources = [None, None, None,None] for i in range(3): sources[i], self.dataShape = createDataSource(a[...,i], True) if(a.shape[-1] >= 4): sources[3], self.dataShape = createDataSource(a[...,3], True) layer = RGBALayer(sources[0],sources[1],sources[2], sources[3]) if name: layer.name = name self.layerstack.append(layer) return layer def addRandomColorsLayer(self, a, name=None, direct=False): layer = self.addColorTableLayer(a, name, colortable=None, direct=direct) layer.colortableIsRandom = True layer.zeroIsTransparent = True return layer def addColorTableLayer(self, a, name=None, colortable=None, direct=False, clickFunctor=None): if colortable is None: colortable = self._randomColors() source,self.dataShape = createDataSource(a,True) if clickFunctor is None: layer = ColortableLayer(source, colortable, direct=direct) else: layer = ClickableColortableLayer(self.editor, clickFunctor, source, colortable, direct=direct) if name: layer.name = name self.layerstack.append(layer) return layer def addRelabelingColorTableLayer(self, a, name=None, relabeling=None, colortable=None, direct=False, clickFunctor=None, right=True): if colortable is None: colortable = self._randomColors() source = RelabelingArraySource(a) if relabeling is None: source.setRelabeling(numpy.zeros(numpy.max(a)+1, dtype=a.dtype)) else: source.setRelabeling(relabeling) if colortable is None: colortable = [QColor(0,0,0,0).rgba(), QColor(255,0,0).rgba()] if clickFunctor is None: layer = ColortableLayer(source, colortable, direct=direct) else: layer = ClickableColortableLayer(self.editor, clickFunctor, source, colortable, direct=direct, right=right) if name: layer.name = name self.layerstack.append(layer) return (layer, source) def addClickableSegmentationLayer(self, a, name=None, direct=False, colortable=None, reuseColors=True): return ClickableSegmentationLayer(a, self, name=name, direct=direct, colortable=colortable, reuseColors=reuseColors) def _randomColors(self, M=256): """Generates a pleasing color table with M entries.""" colors = [] for i in range(M): if i == 0: colors.append(QColor(0, 0, 0, 0).rgba()) else: h, s, v = random.random(), random.random(), 1.0 color = numpy.asarray(colorsys.hsv_to_rgb(h, s, v)) * 255 qColor = QColor(*color) colors.append(qColor.rgba()) #for the first 16 objects, use some colors that are easily distinguishable colors[1:17] = colortables.default16 return colors
def testAddingAndRemoving(self): lsm = LayerStackModel() self.assertEqual(len(lsm), 0) lsm.append(self.l1) self.assertEqual(len(lsm), 1) self.assertEqual(lsm[0].name, self.l1.name) lsm.append(self.l2) self.assertEqual(len(lsm), 2) self.assertEqual(lsm[0].name, self.l2.name) self.assertEqual(lsm[1].name, self.l1.name) lsm.insert(1, self.l3) self.assertEqual(len(lsm), 3) self.assertEqual(lsm[0].name, self.l2.name) self.assertEqual(lsm[1].name, self.l3.name) self.assertEqual(lsm[2].name, self.l1.name) lsm.selectRow(0) lsm.deleteSelected() self.assertEqual(len(lsm), 2) self.assertEqual(lsm[0].name, self.l3.name) self.assertEqual(lsm[1].name, self.l1.name) lsm.clear() self.assertEqual(len(lsm), 0)
def testFirstFullyOpaque( self ): lsm = LayerStackModel() sims = StackedImageSources( lsm ) self.assertEqual(sims.firstFullyOpaque(), None) lsm.append(self.layer1) lsm.append(self.layer2) lsm.append(self.layer3) self.assertEqual( lsm.layerIndex(self.layer1), 2 ) self.assertEqual( lsm.layerIndex(self.layer2), 1 ) self.assertEqual( lsm.layerIndex(self.layer3), 0 ) sims.register(self.layer1, self.ims1) sims.register(self.layer2, self.ims2) sims.register(self.layer3, self.ims3) self.assertEqual(sims.firstFullyOpaque(), 0) lsm.clear() sims = StackedImageSources( lsm ) lsm.append(self.layer2) lsm.append(self.layer3) lsm.append(self.layer1) self.assertEqual( lsm.layerIndex(self.layer1), 0 ) self.assertEqual( lsm.layerIndex(self.layer2), 2 ) self.assertEqual( lsm.layerIndex(self.layer3), 1 ) sims.register(self.layer1, self.ims1) sims.register(self.layer2, self.ims2) sims.register(self.layer3, self.ims3) self.assertEqual(sims.firstFullyOpaque(), 1) lsm.clear() sims = StackedImageSources( lsm ) lsm.append(self.layer2) lsm.append(self.layer1) self.assertEqual( lsm.layerIndex(self.layer1), 0 ) self.assertEqual( lsm.layerIndex(self.layer2), 1 ) sims.register(self.layer1, self.ims1) sims.register(self.layer2, self.ims2) self.assertEqual(sims.firstFullyOpaque(), None) lsm.clear()
class ImageScene2D_LazyTest(ut.TestCase): @classmethod def setUpClass(cls): cls.app = None if QApplication.instance(): cls.app = QApplication.instance() else: cls.app = QApplication([], False) @classmethod def tearDownClass(cls): del cls.app def setUp(self): self.layerstack = LayerStackModel() self.sims = StackedImageSources(self.layerstack) self.g = Graph() self.op = OpLazy(self.g) self.ds = LazyflowSource(self.op.Output) self.ss = SliceSource(self.ds, projectionAlongTZC) self.layer = GrayscaleLayer(self.ds) self.layerstack.append(self.layer) self.ims = imsfac.createImageSource(self.layer, [self.ss]) self.sims.register(self.layer, self.ims) self.scene = ImageScene2D(PositionModel(), (0, 0, 0), preemptive_fetch_number=0) self.scene.setCacheSize(1) self.scene.stackedImageSources = self.sims self.scene.dataShape = (30, 30) def tearDown(self): if self.scene._tileProvider: self.scene._tileProvider.notifyThreadsToStop() self.scene._tileProvider.joinThreads() def renderScene(self, s, exportFilename=None, joinRendering=True): img = QImage(30, 30, QImage.Format_ARGB32_Premultiplied) img.fill(Qt.white) p = QPainter(img) s.render(p) #trigger a rendering of the whole scene if joinRendering: #wait for all the data to arrive s.joinRendering() #finally, render everything s.render(p) p.end() if exportFilename is not None: img.save(exportFilename) return byte_view(img) def testLazy(self): for i in range(3): self.op.setConstant(i) aimg = self.renderScene(self.scene, "/tmp/a_%03d.png" % i) assert numpy.all( aimg[:, :, 0] == i), "!= %d, [0,0,0]=%d" % (i, aimg[0, 0, 0]) self.op.setConstant(42) self.op.setDelay(1) aimg = self.renderScene(self.scene, joinRendering=False, exportFilename="/tmp/x_%03d.png" % i) #this should be "i", not 255 (the default background for the imagescene) assert numpy.all( aimg[:, :, 0] == i), "!= %d, [0,0,0]=%d" % (i, aimg[0, 0, 0])
class Viewer(QMainWindow): """High-level API to view multi-dimensional arrays. Properties: title -- window title """ def __init__(self, parent=None): QMainWindow.__init__(self, parent) uiDirectory = os.path.split(volumina.__file__)[0] if uiDirectory == '': uiDirectory = '.' loadUi(uiDirectory + '/viewer.ui', self) self._dataShape = None self.editor = None self.actionQuit.triggered.connect(qApp.quit) #when connecting in renderScreenshot to a partial(...) function, #we need to remember the created function to be able to disconnect #to it later self._renderScreenshotDisconnect = None self.initLayerstackModel() self.actionCurrentView = QAction(QIcon(), \ "Only for selected view", self.menuView) f = self.actionCurrentView.font() f.setBold(True) self.actionCurrentView.setFont(f) #make sure the layer stack widget, which is the right widget #managed by the splitter self.splitter shows up correctly #TODO: find a proper way of doing this within the designer def adjustSplitter(): s = self.splitter.sizes() s = [int(0.66*s[0]), s[0]-int(0.66*s[0])] self.splitter.setSizes(s) QTimer.singleShot(0, adjustSplitter) def initLayerstackModel(self): self.layerstack = LayerStackModel() self.layerWidget.init(self.layerstack) model = self.layerstack self.UpButton.clicked.connect(model.moveSelectedUp) model.canMoveSelectedUp.connect(self.UpButton.setEnabled) self.DownButton.clicked.connect(model.moveSelectedDown) model.canMoveSelectedDown.connect(self.DownButton.setEnabled) self.DeleteButton.clicked.connect(model.deleteSelected) model.canDeleteSelected.connect(self.DeleteButton.setEnabled) def renderScreenshot(self, axis, blowup=1, filename="/tmp/volumina_screenshot.png"): """Save the complete slice as shown by the slice view 'axis' in the GUI as an image axis -- 0, 1, 2 (x, y, or z slice view) blowup -- enlarge written image by this factor filename -- output file """ print "Rendering screenshot for axis=%d to '%s'" % (axis, filename) s = self.editor.imageScenes[axis] self.editor.navCtrl.enableNavigation = False func = partial(self._renderScreenshot, s, blowup, filename) self._renderScreenshotDisconnect = func s._renderThread.patchAvailable.connect(func) nRequested = 0 for patchNumber in range(len(s._tiling)): p = s.tileProgress(patchNumber) if p < 1.0: s.requestPatch(patchNumber) nRequested += 1 print " need to compute %d of %d patches" % (nRequested, len(s._tiling)) if nRequested == 0: #If no tile needed to be requested, the 'patchAvailable' signal #of the render thread will never come. #In this case, we need to call the implementation ourselves: self._renderScreenshot(s, blowup, filename, patchNumber=0) def addLayer(self, a, display='grayscale', opacity=1.0, \ name='Unnamed Layer', visible=True, interpretChannelsAs=None): print "adding layer '%s', shape=%r, %r" % (name, a.shape, type(a)) """Adds a new layer on top of the layer stack (such that it will be above all currently defined layers). The array 'a' may be a simple numpy.ndarray or implicitly defined via a LazyflowArraySource. Returns the created Layer object. The layer can either be removed by passing this object to self.removeLayer, or by giving a unique name. """ aSlices = None #in case a needs to be split by a lazyflow operator if len(a.shape) not in [2,3,5]: raise RuntimeError("Cannot interpret array with: shape=%r" \ % a.shape) volumeImage = True if len(a.shape) == 2: volumeImage = False if len(a.shape) == 3 and a.shape[2] == 3 and interpretChannelsAs == 'RGB': volumeImage = False viewerType = "volume 5D" if not volumeImage: viewerType = "image 2D" print " treating as %s" % viewerType aType = None if 'lazyflow' in a.__class__.__module__: aType = 'lazyflow' elif hasattr(a, 'axistags'): aType = 'vigra' elif isinstance(a, numpy.ndarray): aType = 'numpy' else: aType = 'generic' # # construct a canonical form for arrays: # # 2D: x,y,c (to be viewed with image viewer) # 5D: t,x,y,z,c (to be viewed with volume viewer) # #convert from LAZYFLOW if aType == 'lazyflow': Source = LazyflowSource if volumeImage: if len(a.shape) < 5: o = Op5ifyer(a.operator.graph) o.inputs['Input'].connect(a) a = o.outputs['Output'] elif not volumeImage: o = OpMultiArraySlicer(a.operator.graph) o.inputs['Input'].connect(a) o.inputs['AxisFlag'].setValue('c') aSlices = o.outputs['Slices'] class A: pass a = A(); a.shape = aSlices[0].shape #convert from VIGRANUMPY ARRAY elif aType == 'vigra': if volumeImage: #vigra array with axistags a = a.withAxes('t', 'x', 'y', 'z', 'c').view(numpy.ndarray) else: a = a.withAxes('x', 'y', 'c').view(numpy.ndarray) Source = ArraySource #convert from NUMPY ARRAY elif aType == 'numpy': if volumeImage: if len(a.shape) == 3: a = a[numpy.newaxis, ..., numpy.newaxis] elif len(a.shape) != 5: raise RuntimeError("Cannot interpret numpy array with shape %r as 5D volume" % (a.shape,)) Source = ArraySource #convert from GENERIC ARRAY elif aType == 'generic': # not a numpy array. Maybe h5py or something else. Embed it. if(hasattr(a, 'dtype')): a = Array5d(a, dtype=a.dtype) else: a = Array5d(a, dtype=np.uint8) Source = ArraySource # # initialize the proper viewer (2D or 5D) # if not volumeImage: init = self._initImageViewing shape = a.shape[0:2] #2D instance = ImageEditor else: init = self._initVolumeViewing shape = a.shape #5D instance = VolumeEditor if self.editor is None or self.editor.dataShape != shape: if self.editor: print " new %s layer '%s', shape %r is not compatible with existing shape %r" % (viewerType, name, shape, self.editor.dataShape) if not isinstance(self.editor, instance) or self.editor is None: init() self.editor.dataShape = shape print " --> resetting viewer to shape=%r and zero layers" % (self.editor.dataShape,) self.layerstack.clear() # # create layer # if display == 'grayscale': if interpretChannelsAs == None: source = Source(a) type_info = numpy.iinfo(a.dtype) if type_info.min < 0: print "WARNING: datatype is not bound to semi-positive values" layer = GrayscaleLayer(source, range=(0, type_info.max)) elif interpretChannelsAs == "RGB": if aSlices is not None: layer = RGBALayer(LazyflowSource(aSlices[0]), LazyflowSource(aSlices[1]), LazyflowSource(aSlices[2])) else: assert len(a.shape) == 3 layer = RGBALayer(Source(a[:,:,0]), Source(a[:,:,1]), Source(a[:,:,2])) elif display == 'randomcolors': if a.dtype != numpy.uint8: print "layer '%s': implicit conversion from %s to uint8" \ % (name, a.dtype) if a.dtype == numpy.uint32: a = a.astype(numpy.uint8) else: raise RuntimeError("unhandled dtype=%r" % a.dtype) source = Source(a) layer = ColortableLayer(source, self._randomColors()) else: raise RuntimeError("unhandled type of overlay") layer.name = name layer.opacity = opacity layer.visible = visible self.layerstack.append(layer) return layer def removeLayer(self, layer): """Remove layer either by given 'Layer' object (as returned by self.addLayer), or by it's name string (as given to the name parameter in self.addLayer)""" if isinstance(layer, Layer): idx = self.layerstack.layerIndex(layer) self.layerstack.removeRows(idx, 1) else: idx = [i for i in range(len(self.layerstack)) if \ self.layerstack.data(self.layerstack.index(i)).name == layer] if len(idx) > 1: raise RuntimeError("Trying to remove layer '%s', whose name is" "ambigous as it refers to %d layers" % len(idx)) return False self.layerstack.removeRows(idx[0], 1) return True @property def title(self): """Get the window title""" return self.windowTitle() @title.setter def title(self, t): """Set the window title""" self.setWindowTitle(t) ### private implementations def _initVolumeViewing(self): self.initLayerstackModel() self.editor = VolumeEditor(self.layerstack, labelsink=None) if not isinstance(self.viewer, VolumeEditorWidget) or self.viewer.editor is None: splitterSizes = self.splitter.sizes() self.viewer.setParent(None) del self.viewer self.viewer = VolumeEditorWidget() self.splitter.insertWidget(0, self.viewer) self.splitter.setSizes(splitterSizes) self.viewer.init(self.editor) w = self.viewer self.menuView.addAction(w.allZoomToFit) self.menuView.addAction(w.allToggleHUD) self.menuView.addAction(w.allCenter) self.menuView.addSeparator() self.menuView.addAction(self.actionCurrentView) self.menuView.addAction(w.selectedZoomToFit) self.menuView.addAction(w.toggleSelectedHUD) self.menuView.addAction(w.selectedCenter) self.menuView.addAction(w.selectedZoomToOriginal) self.menuView.addAction(w.rubberBandZoom) self.editor.newImageView2DFocus.connect(self._setIconToViewMenu) def _initImageViewing(self): if not isinstance(self.viewer, ImageEditorWidget): self.initLayerstackModel() w = self.viewer if isinstance(w, VolumeEditor) and w.editor is not None: self.menuView.removeAction(w.allZoomToFit) self.menuView.removeAction(w.allToggleHUD) self.menuView.removeAction(w.allCenter) self.menuView.removeAction(self.actionCurrentView) self.menuView.removeAction(w.selectedZoomToFit) self.menuView.removeAction(w.toggleSelectedHUD) self.menuView.removeAction(w.selectedCenter) self.menuView.removeAction(w.selectedZoomToOriginal) self.menuView.removeAction(w.rubberBandZoom) #remove 3D viewer splitterSizes = self.splitter.sizes() self.viewer.setParent(None) del self.viewer self.viewer = ImageEditorWidget() self.editor = ImageEditor(layerStackModel=self.layerstack) self.viewer.init(self.editor) self.splitter.insertWidget(0, self.viewer) self.splitter.setSizes(splitterSizes) if not _has_lazyflow: self.viewer.setEnabled(False) def _renderScreenshot(self, s, blowup, filename, patchNumber): progress = 0 for patchNumber in range(len(s._tiling)): p = s.tileProgress(patchNumber) progress += p progress = progress/float(len(s._tiling)) if progress == 1.0: s._renderThread.patchAvailable.disconnect(self._renderScreenshotDisconnect) img = QImage(int(round((blowup*s.sceneRect().size().width()))), int(round((blowup*s.sceneRect().size().height()))), QImage.Format_ARGB32) screenshotPainter = QPainter(img) screenshotPainter.setRenderHint(QPainter.Antialiasing, True) s.render(screenshotPainter, QRectF(0, 0, img.width()-1, img.height()-1), s.sceneRect()) print " saving to '%s'" % filename img.save(filename) del screenshotPainter self.editor.navCtrl.enableNavigation = True def _setIconToViewMenu(self): focused = self.editor.imageViews[self.editor._lastImageViewFocus] self.actionCurrentView.setIcon(\ QIcon(focused._hud.axisLabel.pixmap())) def _randomColors(self, M=256): """Generates a pleasing color table with M entries.""" colors = [] for i in range(M): if i == 0: colors.append(QColor(0, 0, 0, 0).rgba()) else: h, s, v = random.random(), random.random(), 1.0 color = numpy.asarray(colorsys.hsv_to_rgb(h, s, v)) * 255 qColor = QColor(*color) colors.append(qColor.rgba()) return colors def show(self): if not _has_lazyflow: popUp = QMessageBox(parent=self) popUp.setTextFormat(1) popUp.setText("<font size=\"4\"> Lazyflow could not be imported:</font> <br><br><b><font size=\"4\" color=\"#8A0808\">%s</font></b>"%(exceptStr)) popUp.show() popUp.exec_() QMainWindow.show(self)
class ImageScene2D_RenderTest( ut.TestCase ): @classmethod def setUpClass(cls): cls.app = None if QApplication.instance(): cls.app = QApplication.instance() else: cls.app = QApplication([], False) @classmethod def tearDownClass(cls): del cls.app def setUp( self ): self.layerstack = LayerStackModel() self.sims = StackedImageSources( self.layerstack ) self.GRAY = 201 self.ds = ConstantSource(self.GRAY) self.layer = GrayscaleLayer( self.ds ) self.layerstack.append(self.layer) self.ims = imsfac.createImageSource( self.layer, [self.ds] ) self.sims.register(self.layer, self.ims) self.scene = ImageScene2D(PositionModel(), (0,3,4), preemptive_fetch_number=0) self.scene.stackedImageSources = self.sims self.scene.dataShape = (310,290) def tearDown( self ): if self.scene._tileProvider: self.scene._tileProvider.notifyThreadsToStop() self.scene._tileProvider.joinThreads() def renderScene( self, s, exportFilename=None): img = QImage(310,290,QImage.Format_ARGB32_Premultiplied) p = QPainter(img) s.render(p) s.joinRendering() s.render(p) p.end() if exportFilename is not None: img.save(exportFilename) return byte_view(img) def testBasicImageRenderingCapability( self ): aimg = self.renderScene(self.scene) self.assertTrue(np.all(aimg[:,:,0:3] == self.GRAY)) self.assertTrue(np.all(aimg[:,:,3] == 255)) def testToggleVisibilityOfOneLayer( self ): aimg = self.renderScene(self.scene) self.assertTrue(np.all(aimg[:,:,0:3] == self.GRAY)) self.assertTrue(np.all(aimg[:,:,3] == 255)) self.layer.visible = False aimg = self.renderScene(self.scene) self.assertTrue(np.all(aimg[:,:,0:3] == 255)) # all white self.assertTrue(np.all(aimg[:,:,3] == 255)) self.layer.visible = True aimg = self.renderScene(self.scene) self.assertTrue(np.all(aimg[:,:,0:3] == self.GRAY)) self.assertTrue(np.all(aimg[:,:,3] == 255))
class DirtyPropagationTest( ut.TestCase ): def setUp( self ): dataShape = (1, 900, 400, 10, 1) # t,x,y,z,c data = np.indices(dataShape)[3] # Data is labeled according to z-index self.ds1 = ArraySource( data ) self.CONSTANT = 13 self.ds2 = ConstantSource( self.CONSTANT ) self.layer1 = GrayscaleLayer( self.ds1 ) self.layer1.visible = True self.layer1.opacity = 1.0 self.layer2 = GrayscaleLayer( self.ds2 ) self.lsm = LayerStackModel() self.pump = ImagePump( self.lsm, SliceProjection() ) def testEverythingDirtyPropagation( self ): self.lsm.append(self.layer2) tiling = Tiling((900,400), blockSize=100) tp = TileProvider(tiling, self.pump.stackedImageSources) try: tp.requestRefresh(QRectF(100,100,200,200)) tp.join() tiles = tp.getTiles(QRectF(100,100,200,200)) for tile in tiles: aimg = byte_view(tile.qimg) self.assertTrue(np.all(aimg[:,:,0:3] == self.CONSTANT)) self.assertTrue(np.all(aimg[:,:,3] == 255)) NEW_CONSTANT = self.CONSTANT+1 self.ds2.constant = NEW_CONSTANT tp.requestRefresh(QRectF(100,100,200,200)) tp.join() tiles = tp.getTiles(QRectF(100,100,200,200)) for tile in tiles: aimg = byte_view(tile.qimg) self.assertTrue(np.all(aimg[:,:,0:3] == NEW_CONSTANT)) self.assertTrue(np.all(aimg[:,:,3] == 255)) finally: tp.notifyThreadsToStop() tp.joinThreads() def testOutOfViewDirtyPropagation( self ): self.lsm.append(self.layer1) tiling = Tiling((900,400), blockSize=100) tp = TileProvider(tiling, self.pump.stackedImageSources) try: # Navigate down to the second z-slice self.pump.syncedSliceSources.through = [0,1,0] tp.requestRefresh(QRectF(100,100,200,200)) tp.join() # Sanity check: Do we see the right data on the second slice? (should be all 1s) tiles = tp.getTiles(QRectF(100,100,200,200)) for tile in tiles: aimg = byte_view(tile.qimg) self.assertTrue(np.all(aimg[:,:,0:3] == 1)) self.assertTrue(np.all(aimg[:,:,3] == 255)) # Navigate down to the third z-slice self.pump.syncedSliceSources.through = [0,2,0] tp.requestRefresh(QRectF(100,100,200,200)) tp.join() # Sanity check: Do we see the right data on the third slice?(should be all 2s) tiles = tp.getTiles(QRectF(100,100,200,200)) for tile in tiles: aimg = byte_view(tile.qimg) self.assertTrue(np.all(aimg[:,:,0:3] == 2)) self.assertTrue(np.all(aimg[:,:,3] == 255)) # Navigate back up to the second z-slice self.pump.syncedSliceSources.through = [0,1,0] tp.requestRefresh(QRectF(100,100,200,200)) tp.join() for tile in tiles: aimg = byte_view(tile.qimg) self.assertTrue(np.all(aimg[:,:,0:3] == 1)) self.assertTrue(np.all(aimg[:,:,3] == 255)) # Change some of the data in the (out-of-view) third z-slice slicing = (slice(None), slice(100,300), slice(100,300), slice(2,3), slice(None)) slicing = tuple(slicing) self.ds1._array[slicing] = 99 self.ds1.setDirty( slicing ) # Navigate back down to the third z-slice self.pump.syncedSliceSources.through = [0,2,0] tp.requestRefresh(QRectF(100,100,200,200)) tp.join() # Even though the data was out-of-view when it was changed, it should still have new values. # If dirtiness wasn't propagated correctly, the cache's old values will be used. # (For example, this fails if you comment out the call to setDirty, above.) tiles = tp.getTiles(QRectF(100,100,200,200)) for tile in tiles: aimg = byte_view(tile.qimg) # Use any() because the tile borders may not be perfectly aligned with the data we changed. self.assertTrue(np.any(aimg[:,:,0:3] == 99)) finally: tp.notifyThreadsToStop() tp.joinThreads()
class ImageScene2D_LazyTest( ut.TestCase ): @classmethod def setUpClass(cls): cls.app = None if QApplication.instance(): cls.app = QApplication.instance() else: cls.app = QApplication([], False) @classmethod def tearDownClass(cls): del cls.app def setUp( self ): self.layerstack = LayerStackModel() self.sims = StackedImageSources( self.layerstack ) self.g = Graph() self.op = OpLazy(self.g) self.ds = LazyflowSource( self.op.Output ) self.ss = SliceSource( self.ds, projectionAlongTZC ) self.layer = GrayscaleLayer(self.ds) self.layerstack.append(self.layer) self.ims = imsfac.createImageSource( self.layer, [self.ss] ) self.sims.register(self.layer, self.ims) self.scene = ImageScene2D(PositionModel(), (0,0,0), preemptive_fetch_number=0) self.scene.setCacheSize(1) self.scene.stackedImageSources = self.sims self.scene.dataShape = (30,30) def tearDown( self ): if self.scene._tileProvider: self.scene._tileProvider.notifyThreadsToStop() self.scene._tileProvider.joinThreads() def renderScene( self, s, exportFilename=None, joinRendering=True): img = QImage(30,30,QImage.Format_ARGB32_Premultiplied) img.fill(Qt.white) p = QPainter(img) s.render(p) #trigger a rendering of the whole scene if joinRendering: #wait for all the data to arrive s.joinRendering() #finally, render everything s.render(p) p.end() if exportFilename is not None: img.save(exportFilename) return byte_view(img) def testLazy( self ): for i in range(3): self.op.setConstant(i) aimg = self.renderScene(self.scene, "/tmp/a_%03d.png" % i) assert numpy.all(aimg[:,:,0] == i), "!= %d, [0,0,0]=%d" % (i, aimg[0,0,0]) self.op.setConstant(42) self.op.setDelay(1) aimg = self.renderScene(self.scene, joinRendering=False, exportFilename="/tmp/x_%03d.png" % i) #this should be "i", not 255 (the default background for the imagescene) assert numpy.all(aimg[:,:,0] == i), "!= %d, [0,0,0]=%d" % (i, aimg[0,0,0])
class DirtyPropagationTest( ut.TestCase ): def setUp( self ): dataShape = (1, 900, 400, 10, 1) # t,x,y,z,c data = np.indices(dataShape)[3].astype(np.uint8) # Data is labeled according to z-index self.ds1 = ArraySource( data ) self.CONSTANT = 13 self.ds2 = ConstantSource( self.CONSTANT ) self.layer1 = GrayscaleLayer( self.ds1, normalize=False ) self.layer1.visible = True self.layer1.opacity = 1.0 self.layer2 = GrayscaleLayer( self.ds2, normalize=False ) self.lsm = LayerStackModel() self.pump = ImagePump( self.lsm, SliceProjection(), sync_along=(0,1,2) ) def testEverythingDirtyPropagation( self ): self.lsm.append(self.layer2) tiling = Tiling((900,400), blockSize=100) tp = TileProvider(tiling, self.pump.stackedImageSources) tp.requestRefresh(QRectF(100,100,200,200)) tp.waitForTiles() tiles = tp.getTiles(QRectF(100,100,200,200)) for tile in tiles: aimg = byte_view(tile.qimg) self.assertTrue(np.all(aimg[:,:,0:3] == self.CONSTANT)) self.assertTrue(np.all(aimg[:,:,3] == 255)) NEW_CONSTANT = self.CONSTANT+1 self.ds2.constant = NEW_CONSTANT tp.requestRefresh(QRectF(100,100,200,200)) tp.waitForTiles() tiles = tp.getTiles(QRectF(100,100,200,200)) for tile in tiles: aimg = byte_view(tile.qimg) self.assertTrue(np.all(aimg[:,:,0:3] == NEW_CONSTANT)) self.assertTrue(np.all(aimg[:,:,3] == 255)) def testOutOfViewDirtyPropagation( self ): self.lsm.append(self.layer1) tiling = Tiling((900,400), blockSize=100) tp = TileProvider(tiling, self.pump.stackedImageSources) # Navigate down to the second z-slice self.pump.syncedSliceSources.through = [0,1,0] tp.requestRefresh(QRectF(100,100,200,200)) tp.waitForTiles() # Sanity check: Do we see the right data on the second # slice? (should be all 1s) tiles = tp.getTiles(QRectF(100,100,200,200)) for tile in tiles: aimg = byte_view(tile.qimg) self.assertTrue(np.all(aimg[:,:,0:3] == 1)) self.assertTrue(np.all(aimg[:,:,3] == 255)) # Navigate down to the third z-slice self.pump.syncedSliceSources.through = [0,2,0] tp.requestRefresh(QRectF(100,100,200,200)) tp.waitForTiles() # Sanity check: Do we see the right data on the third # slice?(should be all 2s) tiles = tp.getTiles(QRectF(100,100,200,200)) for tile in tiles: aimg = byte_view(tile.qimg) self.assertTrue(np.all(aimg[:,:,0:3] == 2)) self.assertTrue(np.all(aimg[:,:,3] == 255)) # Navigate back up to the second z-slice self.pump.syncedSliceSources.through = [0,1,0] tp.requestRefresh(QRectF(100,100,200,200)) tp.waitForTiles() for tile in tiles: aimg = byte_view(tile.qimg) self.assertTrue(np.all(aimg[:,:,0:3] == 1)) self.assertTrue(np.all(aimg[:,:,3] == 255)) # Change some of the data in the (out-of-view) third z-slice slicing = (slice(None), slice(100,300), slice(100,300), slice(2,3), slice(None)) slicing = tuple(slicing) self.ds1._array[slicing] = 99 self.ds1.setDirty( slicing ) # Navigate back down to the third z-slice self.pump.syncedSliceSources.through = [0,2,0] tp.requestRefresh(QRectF(100,100,200,200)) tp.waitForTiles() # Even though the data was out-of-view when it was # changed, it should still have new values. If dirtiness # wasn't propagated correctly, the cache's old values will # be used. (For example, this fails if you comment out the # call to setDirty, above.) # Shrink accessed rect by 1 pixel on each side (Otherwise, # tiling overlap_draw causes getTiles() to return # surrounding tiles that we haven't actually touched in # this test) tiles = tp.getTiles(QRectF(101,101,198,198)) for tile in tiles: aimg = byte_view(tile.qimg) # Use any() because the tile borders may not be # perfectly aligned with the data we changed. self.assertTrue(np.any(aimg[:,:,0:3] == 99))
class ImageScene2D_RenderTest(ut.TestCase): @classmethod def setUpClass(cls): cls.app = None if QApplication.instance(): cls.app = QApplication.instance() else: cls.app = QApplication([], False) @classmethod def tearDownClass(cls): del cls.app def setUp(self): self.layerstack = LayerStackModel() self.sims = StackedImageSources(self.layerstack) self.GRAY = 201 self.ds = ConstantSource(self.GRAY) self.layer = GrayscaleLayer(self.ds) self.layerstack.append(self.layer) self.ims = imsfac.createImageSource(self.layer, [self.ds]) self.sims.register(self.layer, self.ims) self.scene = ImageScene2D(PositionModel(), (0, 3, 4), preemptive_fetch_number=0) self.scene.stackedImageSources = self.sims self.scene.dataShape = (310, 290) def tearDown(self): if self.scene._tileProvider: self.scene._tileProvider.notifyThreadsToStop() self.scene._tileProvider.joinThreads() def renderScene(self, s, exportFilename=None): img = QImage(310, 290, QImage.Format_ARGB32_Premultiplied) img.fill(0) p = QPainter(img) s.render(p) s.joinRendering() s.render(p) p.end() if exportFilename is not None: img.save(exportFilename) return byte_view(img) def testBasicImageRenderingCapability(self): aimg = self.renderScene(self.scene) self.assertTrue(np.all(aimg[:, :, 0:3] == self.GRAY)) self.assertTrue(np.all(aimg[:, :, 3] == 255)) def testToggleVisibilityOfOneLayer(self): aimg = self.renderScene(self.scene) self.assertTrue(np.all(aimg[:, :, 0:3] == self.GRAY)) self.assertTrue(np.all(aimg[:, :, 3] == 255)) self.layer.visible = False aimg = self.renderScene(self.scene) self.assertTrue(np.all(aimg[:, :, 0:3] == 0)) # all white self.assertTrue(np.all(aimg[:, :, 3] == 0)) self.layer.visible = True aimg = self.renderScene(self.scene) self.assertTrue(np.all(aimg[:, :, 0:3] == self.GRAY)) self.assertTrue(np.all(aimg[:, :, 3] == 255))
class TestLayerWidget( ut.TestCase ): """ Create two layers and add them to a LayerWidget. Then change one of the layer visibilities and verify that the layer widget appearance updates. At the time of this writing, the widget doesn't properly repaint the selected layer (all others repaint correctly). """ @classmethod def setUpClass(cls): if 'TRAVIS' in os.environ: # This test fails on Travis-CI for unknown reasons, # probably due to the variability of time.sleep(). # Skip it on Travis-CI. import nose raise nose.SkipTest cls.app = QApplication([]) cls.errors = False @classmethod def tearDownClass(cls): del cls.app def impl(self): try: # Capture the window before we change anything beforeImg = QPixmap.grabWindow( self.w.winId() ).toImage() # Change the visibility of the *selected* layer self.o2.visible = False self.w.repaint() # Make sure the GUI is caught up on paint events QApplication.processEvents() # We must sleep for the screenshot to be right. time.sleep(0.1) # Capture the window now that we've changed a layer. afterImg = QPixmap.grabWindow( self.w.winId() ).toImage() # Optional: Save the files so we can inspect them ourselves... #beforeImg.save('before.png') #afterImg.save('after.png') # Before and after should NOT match. assert beforeImg != afterImg except: # Catch all exceptions and print them # We must finish so we can quit the app. import traceback traceback.print_exc() TestLayerWidget.errors = True qApp.quit() def test_repaint_after_visible_change(self): self.model = LayerStackModel() self.o1 = Layer([]) self.o1.name = "Fancy Layer" self.o1.opacity = 0.5 self.model.append(self.o1) self.o2 = Layer([]) self.o2.name = "Some other Layer" self.o2.opacity = 0.25 self.model.append(self.o2) self.view = LayerWidget(None, self.model) self.view.show() self.view.updateGeometry() self.w = QWidget() self.lh = QHBoxLayout(self.w) self.lh.addWidget(self.view) self.w.setGeometry(100, 100, 300, 300) self.w.show() # Run the test within the GUI event loop QTimer.singleShot(500, self.impl ) self.app.exec_() # Were there errors? assert not TestLayerWidget.errors, "There were GUI errors/failures. See above."
class Viewer(QMainWindow): """High-level API to view multi-dimensional arrays. Properties: title -- window title """ def __init__(self, parent=None): QMainWindow.__init__(self, parent) uiDirectory = os.path.split(volumina.__file__)[0] if uiDirectory == '': uiDirectory = '.' loadUi(uiDirectory + '/viewer.ui', self) self._dataShape = None self._viewerInitialized = False self.editor = None self.viewingWidget = None self.actionQuit.triggered.connect(qApp.quit) #when connecting in renderScreenshot to a partial(...) function, #we need to remember the created function to be able to disconnect #to it later self._renderScreenshotDisconnect = None self.initLayerstackModel() self.actionCurrentView = QAction(QIcon(), "Only for selected view", self.menuView) f = self.actionCurrentView.font() f.setBold(True) self.actionCurrentView.setFont(f) # Lazy import here to prevent this module from ignoring volumine.NO3D flag. from volumina.volumeEditor import VolumeEditor self.editor = VolumeEditor(self.layerstack, parent=self) #make sure the layer stack widget, which is the right widget #managed by the splitter self.splitter shows up correctly #TODO: find a proper way of doing this within the designer def adjustSplitter(): s = self.splitter.sizes() s = [int(0.66 * s[0]), s[0] - int(0.66 * s[0])] self.splitter.setSizes(s) QTimer.singleShot(0, adjustSplitter) @property def title(self): return self.windowTitle() @title.setter def title(self, t): self.setWindowTitle(t) def initLayerstackModel(self): self.layerstack = LayerStackModel() self.layerWidget.init(self.layerstack) model = self.layerstack self.UpButton.clicked.connect(model.moveSelectedUp) model.canMoveSelectedUp.connect(self.UpButton.setEnabled) self.DownButton.clicked.connect(model.moveSelectedDown) model.canMoveSelectedDown.connect(self.DownButton.setEnabled) self.DeleteButton.clicked.connect(model.deleteSelected) model.canDeleteSelected.connect(self.DeleteButton.setEnabled) @property def dataShape(self): return self._dataShape @dataShape.setter def dataShape(self, s): if s is None: return assert len(s) == 5 self._dataShape = s self.editor.dataShape = s if not self._viewerInitialized: self._viewerInitialized = True self.viewer.init(self.editor) #make sure the data shape is correctly set #(some signal/slot connections may be set up in the above init) self.editor.dataShape = s #FIXME: this code is broken #if its 2D, maximize the corresponding window #if len([i for i in list(self.dataShape)[1:4] if i == 1]) == 1: # viewAxis = [i for i in range(1,4) if self.dataShape[i] == 1][0] - 1 # self.viewer.quadview.switchMinMax(viewAxis) def addGrayscaleLayer(self, a, name=None, direct=False): source, self.dataShape = createDataSource(a, True) layer = GrayscaleLayer(source, direct=direct) layer.numberOfChannels = self.dataShape[-1] if name: layer.name = name self.layerstack.append(layer) return layer def addAlphaModulatedLayer(self, a, name=None): source, self.dataShape = createDataSource(a, True) layer = AlphaModulatedLayer(source) if name: layer.name = name self.layerstack.append(layer) return layer def addRGBALayer(self, a, name=None): assert a.shape[2] >= 3 sources = [None, None, None, None] for i in range(3): sources[i], self.dataShape = createDataSource(a[..., i], True) if (a.shape[-1] >= 4): sources[3], self.dataShape = createDataSource(a[..., 3], True) layer = RGBALayer(sources[0], sources[1], sources[2], sources[3]) if name: layer.name = name self.layerstack.append(layer) return layer def addRandomColorsLayer(self, a, name=None, direct=False): layer = self.addColorTableLayer(a, name, colortable=None, direct=direct) layer.colortableIsRandom = True layer.zeroIsTransparent = True return layer def addColorTableLayer(self, a, name=None, colortable=None, direct=False, clickFunctor=None): if colortable is None: colortable = self._randomColors() source, self.dataShape = createDataSource(a, True) if clickFunctor is None: layer = ColortableLayer(source, colortable, direct=direct) else: layer = ClickableColortableLayer(self.editor, clickFunctor, source, colortable, direct=direct) if name: layer.name = name self.layerstack.append(layer) return layer def addRelabelingColorTableLayer(self, a, name=None, relabeling=None, colortable=None, direct=False, clickFunctor=None, right=True): if colortable is None: colortable = self._randomColors() source = RelabelingArraySource(a) if relabeling is None: source.setRelabeling(numpy.zeros(numpy.max(a) + 1, dtype=a.dtype)) else: source.setRelabeling(relabeling) if colortable is None: colortable = [QColor(0, 0, 0, 0).rgba(), QColor(255, 0, 0).rgba()] if clickFunctor is None: layer = ColortableLayer(source, colortable, direct=direct) else: layer = ClickableColortableLayer(self.editor, clickFunctor, source, colortable, direct=direct, right=right) if name: layer.name = name self.layerstack.append(layer) return (layer, source) def addClickableSegmentationLayer(self, a, name=None, direct=False, colortable=None, reuseColors=True): return ClickableSegmentationLayer(a, self, name=name, direct=direct, colortable=colortable, reuseColors=reuseColors) def _randomColors(self, M=256): """Generates a pleasing color table with M entries.""" colors = [] for i in range(M): if i == 0: colors.append(QColor(0, 0, 0, 0).rgba()) else: h, s, v = random.random(), random.random(), 1.0 color = numpy.asarray(colorsys.hsv_to_rgb(h, s, v)) * 255 qColor = QColor(*color) colors.append(qColor.rgba()) #for the first 16 objects, use some colors that are easily distinguishable colors[1:17] = colortables.default16 return colors