Esempio n. 1
0
    def __onApply(self, ev):
        """Called when the *Apply* button is pushed. Sets the
        ``voxToWorldMat`` attribute of the :class:`.Image` instance being
        transformed.
        """

        overlay = self.__overlay

        if overlay is None:
            return

        if self.__extraXform is None: v2wXform = overlay.voxToWorldMat
        else: v2wXform = self.__extraXform

        newXform = self.__getCurrentXform()
        opts = self.displayCtx.getOpts(overlay)

        xform = transform.concat(newXform, v2wXform)

        with props.suppress(opts, 'displayXform'):
            opts.displayXform = np.eye(4)
            overlay.voxToWorldMat = xform

        # Reset the interface, and clear any
        # cached transform for this overlay
        self.__deregisterOverlay()
        self.__cachedXforms.pop(overlay, None)
        self.__selectedOverlayChanged()
Esempio n. 2
0
    def __dataRangeChanged(self, *args, **kwargs):
        """Called when the :attr:`dataRange` property changes, and also by the
        :meth:`__initProperties` and :meth:`__volumeChanged` methods.
        """

        finData = self.__finiteData
        nzData = self.__nonZeroData

        self.__clippedFiniteData = finData[(finData >= self.dataRange.xlo)
                                           & (finData < self.dataRange.xhi)]
        self.__clippedNonZeroData = nzData[(nzData >= self.dataRange.xlo)
                                           & (nzData < self.dataRange.xhi)]

        with props.suppress(self, 'showOverlayRange', notify=True):

            dlo, dhi = self.dataRange.x
            dist = (dhi - dlo) / 10000.0

            needsInit = np.all(np.isclose(self.showOverlayRange.x, [0, 0]))

            self.showOverlayRange.xmin = dlo - dist
            self.showOverlayRange.xmax = dhi + dist

            if needsInit or not self.showOverlay:
                self.showOverlayRange.xlo = dlo
                self.showOverlayRange.xhi = dhi
            else:
                self.showOverlayRange.xlo = max(dlo, self.showOverlayRange.xlo)
                self.showOverlayRange.xhi = min(dhi, self.showOverlayRange.xhi)

        self.__histPropsChanged()
Esempio n. 3
0
    def _cropModeLeftMouseDrag(self, ev, canvas, mousePos, canvasPos):
        """Called on left mouse drags. Updates the :attr:`cropBox` boudary
        which was clicked on (see :meth:`_cropModeLeftMouseDown`), so it
        follows the mouse location.
        """

        if self.__overlay is None or self.__dragAxis is None:
            return

        box = self.cropBox
        axis = self.__dragAxis
        limit = self.__dragLimit
        oppLimit = 1 - limit
        vox = self.__getVoxel(self.__overlay, canvasPos)

        newval = vox[axis]
        oppval = box.getLimit(axis, oppLimit)

        if limit == 0 and newval >= oppval: newval = oppval - 1
        elif limit == 1 and newval <= oppval: newval = oppval + 1

        with props.suppress(self, 'cropBox', notify=True):
            self.cropBox.setLimit(axis, limit, newval)

        self.displayCtx.location = canvasPos
Esempio n. 4
0
    def append(self, item, overlayType=None):

        with props.suppress(self, 'overlays', notify=True):

            self.overlays.append(item)

            if overlayType is not None:
                self.__initOverlayType[item] = overlayType
Esempio n. 5
0
    def insert(self, index, item, overlayType=None):

        with props.suppress(self, 'overlays', notify=True):

            self.overlays.insert(index, item)

            if overlayType is not None:
                self.__initOverlayType[item] = overlayType
Esempio n. 6
0
    def insert(self, index, item, **initProps):
        """Insert a new overlay into the overlay list.

        Any initial :class:`.Display`/:class:`.DisplayOpts` property values
        may be passed in as keyword arguments.
        """

        with props.suppress(self, 'overlays', notify=True):
            self.overlays.insert(index, item)
            self.__initProps[item] = initProps
Esempio n. 7
0
    def extend(self, iterable, overlayTypes=None):

        with props.suppress(self, 'overlays', notify=True):

            result = self.overlays.extend(iterable)

            if overlayTypes is not None:
                for overlay, overlayType in overlayTypes.items():
                    self.__initOverlayType[overlay] = overlayType

        return result
Esempio n. 8
0
    def __updateAxisLimits(self):
        """Called by the ``panzoom`` ``MouseDrag`` event handlers. Makes sure
        that the :attr:`.PlotPanel.limits` property is up to date.
        """

        xlims = list(self.__axis.get_xlim())
        ylims = list(self.__axis.get_ylim())

        with props.suppress(self.viewPanel, 'limits'):
            self.viewPanel.limits.x = xlims
            self.viewPanel.limits.y = ylims
    def draw(self, *a):
        """Overrides :meth:`.PlotPanel.draw`. Draws some
        :class:`.PowerSpectrumSeries` using the
        :meth:`.PlotPanel.drawDataSeries` method.
        """

        if not self or self.destroyed:
            return

        pss = self.getDataSeriesToPlot()

        for ps in pss:
            with props.suppress(ps, 'label'):
                ps.label = ps.makeLabel()

        self.drawDataSeries(extraSeries=pss)
        self.drawArtists()
Esempio n. 10
0
        def realOnLoad(atlas):
            # label image
            if labelIdx is None:
                overlayType = 'label'
                data = atlas[:]

            else:

                # regional label image
                if summary:

                    labelVal = atlasDesc.find(index=labelIdx).value
                    overlayType = 'mask'
                    data = np.zeros(atlas.shape, dtype=np.uint16)
                    data[atlas[:] == labelVal] = labelVal

                # regional probability image
                else:
                    overlayType = 'volume'
                    data = atlas[:, :, :, labelIdx]

            overlay = fslimage.Image(data,
                                     header=atlas.header,
                                     name=overlayName)

            with props.suppress(self.overlayList, 'overlays', self.name):
                self.overlayList.append(overlay, overlayType=overlayType)

            self.__overlayPanel.setOverlayState(atlasDesc, labelIdx, summary,
                                                True)

            self.__enabledOverlays[overlayName] = (overlay, atlasID, labelIdx,
                                                   summary)

            log.debug('Added overlay {}'.format(overlayName))

            display = self.displayCtx.getDisplay(overlay)
            display.overlayType = overlayType
            opts = display.opts

            if overlayType == 'mask': opts.colour = np.random.random(3)
            elif overlayType == 'volume': opts.cmap = 'hot'

            if onLoad is not None:
                onLoad()
Esempio n. 11
0
    def extend(self, iterable, **initProps):
        """Add new overlays to the overlay list.

        Any initial :class:`.Display`/:class:`.DisplayOpts` property values
        may be passed in as keyword arguments, where the argument name is the
        property name, and the argument value is a dict of
        ``{overlay : value}`` mappings.
        """

        with props.suppress(self, 'overlays', notify=True):

            result = self.overlays.extend(iterable)

            for propName, overlayProps in initProps.items():
                for overlay, val in overlayProps.items():
                    self.__initProps[overlay][propName] = val

        return result
Esempio n. 12
0
    def __onVisButton(self, ev):
        """Called when the *visibility* button is pushed. Toggles the overlay
        visibility.
        """

        if self.__propagateSelect:
            self.__displayCtx.selectOverlay(self.__overlay)

        idx     = self.__listBox.IndexOf(self.__overlay)
        enabled = self.__visibility.GetValue()

        with props.suppress(self.__display, 'enabled', self.__name):
            self.__display.enabled = enabled

        if enabled: fgColour = ListItemWidget.enabledFG
        else:       fgColour = ListItemWidget.disabledFG

        self.__listBox.SetItemForegroundColour(idx, fgColour)
Esempio n. 13
0
    def draw(self, *a):
        """Overrides :meth:`.PlotPanel.draw`. Passes some :class:`.TimeSeries`
        instances to the :meth:`.PlotPanel.drawDataSeries` method.
        """

        if not self or self.destroyed():
            return

        tss = self.getDataSeriesToPlot()

        # Include all of the extra model series
        # for all FEATTimeSeries instances
        newTss = []
        for ts in tss:
            if isinstance(ts, plotting.FEATTimeSeries):

                mtss = ts.getModelTimeSeries()
                newTss += mtss

                # If the FEATTimeSeries is disabled,
                # disable the associated model time
                # series.
                for mts in mtss:
                    mts.enabled = ts.enabled
            else:
                newTss.append(ts)
        tss = newTss

        for ts in tss:

            # Changing the label might trigger
            # another call to this method, as
            # the PlotPanel might have a listener
            # registered on it. Hence the suppress
            with props.suppress(ts, 'label'):
                ts.label = ts.makeLabel()

        xlabel, ylabel = self.__generateDefaultLabels(tss)

        self.drawDataSeries(extraSeries=tss, xlabel=xlabel, ylabel=ylabel)
        self.drawArtists()
Esempio n. 14
0
    def onDataRangeChange(self):
        """Overrides :meth:`HistogramSeries.onDataRangeChange`. Makes sure
        that the :attr:`showOverlayRange` limits are synced to the
        :attr:`HistogramSeries.dataRange`.
        """
        with props.suppress(self, 'showOverlayRange', notify=True):

            dlo, dhi = self.dataRange.x
            dist     = (dhi - dlo) / 10000.0

            needsInit = np.all(np.isclose(self.showOverlayRange.x, [0, 0]))

            self.showOverlayRange.xmin = dlo - dist
            self.showOverlayRange.xmax = dhi + dist

            if needsInit or not self.showOverlay:
                self.showOverlayRange.xlo = dlo
                self.showOverlayRange.xhi = dhi
            else:
                self.showOverlayRange.xlo = max(dlo, self.showOverlayRange.xlo)
                self.showOverlayRange.xhi = min(dhi, self.showOverlayRange.xhi)
Esempio n. 15
0
        def realOnLoad(atlas):
            initprops = {}
            # label image
            if labelIdx is None:
                overlay = fslimage.Image(atlas)
                initprops['overlayType'] = 'label'

            else:

                # regional label image
                if summary:
                    overlay = atlas.get(index=labelIdx, binary=False)
                    initprops['overlayType'] = 'mask'
                    initprops['colour'] = np.random.random(3)

                # regional statistic/probability image
                else:
                    overlay = atlas.get(index=labelIdx)
                    initprops['overlayType'] = 'volume'
                    initprops['cmap'] = 'hot'
                    initprops['displayRange'] = (atlasDesc.lower,
                                                 atlasDesc.upper)
                    initprops['clippingRange'] = (atlasDesc.lower,
                                                  atlasDesc.upper)

            overlay.name = overlayName

            with props.suppress(self.overlayList, 'overlays', self.name):
                self.overlayList.append(overlay, **initprops)

            self.__overlayPanel.setOverlayState(atlasDesc, labelIdx, summary,
                                                True)

            self.__enabledOverlays[overlayName] = (overlay, atlasID, labelIdx,
                                                   summary)

            log.debug('Added overlay {}'.format(overlayName))

            if onLoad is not None:
                onLoad()
Esempio n. 16
0
    def draw(self, *a):
        """Overrides :meth:`.PlotPanel.draw`. Passes some :class:`.DataSeries`
        instances to the :meth:`.PlotPanel.drawDataSeries` method.
        """

        if not self or self.destroyed:
            return

        tss = self.getDataSeriesToPlot()
        for ts in tss:

            # Changing the label might trigger
            # another call to this method, as
            # the PlotPanel might have a listener
            # registered on it. Hence the suppress
            with props.suppress(ts, 'label'):
                ts.label = ts.makeLabel()

        xlabel, ylabel = self.__generateDefaultLabels(tss)

        self.drawDataSeries(extraSeries=tss, xlabel=xlabel, ylabel=ylabel)
        self.drawArtists()
Esempio n. 17
0
    def __selectedOverlayChanged(self, *a):
        """Called when the :attr:`.DisplayContext.selectedOverlay` changes.
        If the overlay is a :class:`.Image` instance, it is set as the
        :attr:`.DisplayContext.displaySpace` reference, and the
        :attr:`cropBox` is configured to be relative to the newly selected
        overlay.
        """
        overlay = self.displayCtx.getSelectedOverlay()

        if overlay is self.__overlay:
            return

        self.__deregisterOverlay()

        enabled = isinstance(overlay, fslimage.Image)

        self.__xrect.enabled = enabled
        self.__yrect.enabled = enabled
        self.__zrect.enabled = enabled

        if not enabled:
            return

        self.__registerOverlay(overlay)

        shape = overlay.shape[:3]
        crop = self.__cachedCrops.get(overlay, None)

        if crop is None:
            crop = [0, shape[0], 0, shape[1], 0, shape[2]]

        with props.suppress(self, 'cropBox', notify=True):
            self.cropBox.xmin = 0
            self.cropBox.ymin = 0
            self.cropBox.zmin = 0
            self.cropBox.xmax = shape[0]
            self.cropBox.ymax = shape[1]
            self.cropBox.zmax = shape[2]
            self.cropBox = crop
Esempio n. 18
0
    def __updateBounds(self, *a):
        """Called when the overlay list changes, or when any overlay display
        transform is changed. Updates the :attr:`bounds` property so that it
        is big enough to contain all of the overlays (as defined by their
        :attr:`.DisplayOpts.bounds` properties).
        """

        if len(self.__overlayList) == 0:
            minBounds = [0.0, 0.0, 0.0]
            maxBounds = [0.0, 0.0, 0.0]

        else:
            minBounds = 3 * [sys.float_info.max]
            maxBounds = 3 * [-sys.float_info.max]

        for ovl in self.__overlayList:

            display = self.__displays[ovl]
            opts = display.opts
            lo = opts.bounds.getLo()
            hi = opts.bounds.getHi()

            for ax in range(3):

                if lo[ax] < minBounds[ax]: minBounds[ax] = lo[ax]
                if hi[ax] > maxBounds[ax]: maxBounds[ax] = hi[ax]

        self.bounds[:] = [
            minBounds[0], maxBounds[0], minBounds[1], maxBounds[1],
            minBounds[2], maxBounds[2]
        ]

        # Update the constraints on the location
        # property to be aligned with the new bounds
        with props.suppress(self, 'location'):
            self.location.setLimits(0, self.bounds.xlo, self.bounds.xhi)
            self.location.setLimits(1, self.bounds.ylo, self.bounds.yhi)
            self.location.setLimits(2, self.bounds.zlo, self.bounds.zhi)
Esempio n. 19
0
    def draw(self, *a):
        """Overrides :meth:`.PlotPanel.draw`. Passes some
        :class:`.HistogramSeries` instances to the
        :meth:`.PlotPanel.drawDataSeries` method.
        """

        if not self or self.destroyed():
            return

        hss = self.getDataSeriesToPlot()

        for hs in hss:
            with props.suppress(hs, 'label'):
                hs.label = self.displayCtx.getDisplay(hs.overlay).name

        if self.smooth or self.plotType == 'centre':
            self.drawDataSeries(hss)

        # use a step plot when plotting bin edges
        else:
            self.drawDataSeries(hss, drawstyle='steps-pre')

        self.drawArtists()
Esempio n. 20
0
    def __updateWidgets(self):
        """Called by the :meth:`__selectedOverlayChanged` and
        :meth:`__displayOptsChanged` methods.  Enables/disables the
        voxel/world location and volume controls depending on the currently
        selected overlay (or reference image).
        """

        overlay = self.__registeredOverlay
        opts    = self.__registeredOpts

        if overlay is not None: refImage = opts.referenceImage
        else:                   refImage = None

        haveRef = refImage is not None

        self.__voxelX     .Enable(haveRef)
        self.__voxelY     .Enable(haveRef)
        self.__voxelZ     .Enable(haveRef)
        self.__voxelLabel .Enable(haveRef)

        ######################
        # World location label
        ######################

        label = strings.labels[self, 'worldLocation']

        if haveRef: label += strings.anatomy[refImage,
                                             'space',
                                             refImage.getXFormCode()]
        else:       label += strings.labels[ self,
                                             'worldLocation',
                                             'unknown']

        self.__worldLabel.SetLabel(label)

        ####################################
        # Voxel/world location widget limits
        ####################################

        # Figure out the limits for the
        # voxel/world location widgets
        if haveRef:
            opts     = self.displayCtx.getOpts(refImage)
            v2w      = opts.getTransform('voxel', 'world')
            shape    = refImage.shape[:3]
            vlo      = [0, 0, 0]
            vhi      = np.array(shape) - 1
            wlo, whi = transform.axisBounds(shape, v2w)
            wstep    = refImage.pixdim[:3]
        else:
            vlo     = [0, 0, 0]
            vhi     = [0, 0, 0]
            wbounds = self.displayCtx.bounds[:]
            wlo     = wbounds[0::2]
            whi     = wbounds[1::2]
            wstep   = [1, 1, 1]

        log.debug('Setting voxelLocation limits: {} - {}'.format(vlo, vhi))
        log.debug('Setting worldLocation limits: {} - {}'.format(wlo, whi))

        # Update the voxel and world location limits,
        # but don't trigger a listener callback, as
        # this would change the display location.
        widgets = [self.__worldX, self.__worldY, self.__worldZ]
        with props.suppress(self, 'worldLocation'), \
             props.suppress(self, 'voxelLocation'):

            for i in range(3):
                self.voxelLocation.setLimits(i, vlo[i], vhi[i])
                self.worldLocation.setLimits(i, wlo[i], whi[i])
                widgets[i].SetIncrement(wstep[i])
Esempio n. 21
0
    def _cropModeLeftMouseDown(self, ev, canvas, mousePos, canvasPos):
        """Called on mouse down events. Calculates the nearest crop box
        boundary to the mouse click, adjusts the boundary accordingly,
        and saves the boundary/axis information for subsequent drag
        events (see :meth:`_cropModeLeftMouseDrag`).
        """

        copts = canvas.opts
        overlay = self.__overlay

        if overlay is None:
            return

        # What canvas was the click on?
        if copts.zax == 0: hax, vax = 1, 2
        elif copts.zax == 1: hax, vax = 0, 2
        elif copts.zax == 2: hax, vax = 0, 1

        # Figure out the distances from
        # the mouse click  to each crop
        # box boundary on the clicked
        # canvas
        vox = self.__getVoxel(overlay, canvasPos)
        hlo, hhi = self.cropBox.getLo(hax), self.cropBox.getHi(hax)
        vlo, vhi = self.cropBox.getLo(vax), self.cropBox.getHi(vax)

        # We compare the click voxel
        # coords with each of the x/y
        # lo/hi crop box boundaries
        boundaries = np.array([[hlo, vox[vax]], [hhi, vox[vax]],
                               [vox[hax], vlo], [vox[hax], vhi]])

        # In case the voxel is out of bounds,
        # make sure that the crop box boundary
        # coordinates are actually in the crop
        # box (or on an edge).
        boundaries[:, 0] = np.clip(boundaries[:, 0], hlo, hhi)
        boundaries[:, 1] = np.clip(boundaries[:, 1], vlo, vhi)

        # As the display space is set to
        # this overlay, the display coordinate
        # system is equivalent to the scaled
        # voxel coordinate system of the
        # overlay. So we can just multiply the
        # 2D voxel coordinates by the
        # corresponding pixdims to get the
        # distances in the display coordinate
        # system.
        pixdim = overlay.pixdim[:3]
        scVox = [vox[hax] * pixdim[hax], vox[vax] * pixdim[vax]]
        boundaries[:, 0] = boundaries[:, 0] * pixdim[hax]
        boundaries[:, 1] = boundaries[:, 1] * pixdim[vax]

        # Calculate distance from click to
        # crop boundaries, and figure out
        # the screen axis (x/y) and limit
        # (lo/hi) to be dragged.
        dists = (boundaries - scVox)**2
        dists = np.sqrt(np.sum(dists, axis=1))
        axis, limit = np.unravel_index(np.argmin(dists), (2, 2))
        voxAxis = [hax, vax][axis]

        axis = int(axis)
        limit = int(limit)

        # Save these for the mouse drag handler
        self.__dragAxis = voxAxis
        self.__dragLimit = limit

        # Update the crop box and location
        with props.suppress(self, 'cropBox', notify=True):
            self.cropBox.setLimit(voxAxis, limit, vox[voxAxis])

        self.displayCtx.location = canvasPos