From a58ceca380063d3356fd85d766ae710892c30b3f Mon Sep 17 00:00:00 2001 From: Paul McCarthy <pauld.mccarthy@gmail.com> Date: Mon, 16 Feb 2015 16:59:22 +0000 Subject: [PATCH] AtlasPanel refactoring. Is broken right now. --- TODO | 7 +- fsl/data/atlases.py | 65 +++++- fsl/data/strings.py | 6 +- fsl/fslview/controls/atlasinfopanel.py | 165 +++++++++++++ fsl/fslview/controls/atlasoverlaypanel.py | 246 +++++++++++++++++++ fsl/fslview/controls/atlaspanel.py | 272 +++------------------- 6 files changed, 504 insertions(+), 257 deletions(-) create mode 100644 fsl/fslview/controls/atlasinfopanel.py create mode 100644 fsl/fslview/controls/atlasoverlaypanel.py diff --git a/TODO b/TODO index c6ac63343..52fe8a59a 100644 --- a/TODO +++ b/TODO @@ -7,13 +7,17 @@ https://internal.fmrib.ox.ac.uk/i-wiki/Analysis/Todos/FSLView regularly, too. * February 2015 internal release -** Git release pipeline ** UI design ** Atlas tools +** Histogram? ** SSH / VNC performance ** Fix all the bugs *** Command line *** Offscreen rendering +** DONE Git release pipeline +We now have an 'oxford' branch, which is linked to the jalapeno installation. +When you want to 'release' something, merge from the master branch to the +oxford branch, and push to jalapeno * Things to do in props package @@ -102,6 +106,7 @@ perhaps into a standalone module... ** DONE Graceful handling of bad input filenames ** DONE Aspect ratio on slicecanvas zoom, and panning is broken. * Little things +** Ability to display vector data as directional (i.e. starting from voxel centre) ** Ortho canvases not taking up all the available space ** 'Annotation' image type that allows the user to highlight regions in different colours. ** Support ANALYZE75 diff --git a/fsl/data/atlases.py b/fsl/data/atlases.py index 9a4287f80..9b536c9c4 100644 --- a/fsl/data/atlases.py +++ b/fsl/data/atlases.py @@ -49,6 +49,7 @@ import os import xml.etree.ElementTree as et import os.path as op import glob +import collections import logging import numpy as np @@ -67,28 +68,74 @@ if os.environ.get('FSLDIR', None) is None: else: ATLAS_DIR = op.join(os.environ['FSLDIR'], 'data', 'atlases') + +ATLAS_DESCRIPTIONS = collections.OrderedDict() + -def listAtlases(): +def listAtlases(refresh=False): """Returns a dictionary containing :class:`AtlasDescription` objects for all available atlases. + + :arg refresh: If ``True``, or if the atlas desriptions have not + previously been loaded, atlas descriptions are + loaded from the atlas files. Otherwise, prefviously + loaded descriptions are returned (see + :attr:`ATLAS_DESCRIPTIONS`). """ + if len(ATLAS_DESCRIPTIONS) == 0: + refresh = True + + if not refresh: + return ATLAS_DESCRIPTIONS.values() + atlasFiles = glob.glob(op.join(ATLAS_DIR, '*.xml')) atlasDescs = map(AtlasDescription, atlasFiles) + atlasDescs = sorted(atlasDescs, key=lambda d: d.name) - return {d.atlasID: d for d in atlasDescs} + ATLAS_DESCRIPTIONS.clear() + for desc in atlasDescs: + ATLAS_DESCRIPTIONS[desc.atlasID] = desc -def loadAtlas(atlasDesc, loadSummary=False): + return atlasDescs - if loadSummary or atlasDesc.atlasType == 'label': - return LabelAtlas(atlasDesc) + +def getAtlasDescription(atlasID): + """Returns an :class:`AtlasDescription` instance describing the + atlas with the given ``atlasID``. + """ - if atlasDesc.atlasType == 'probabilistic': - return ProbabilisticAtlas(atlasDesc) - else: - raise ValueError('Unknown atlas type: {}'.format(atlasDesc.atlasType)) + if len(ATLAS_DESCRIPTIONS) == 0: + listAtlases() + + return ATLAS_DESCRIPTIONS[atlasID] + + +def loadAtlas(atlasID, loadSummary=False): + """Loads and returns an :class:`Atlas` instance for the atlas + with the given ``atlasID``. + + :arg loadSummary: If ``True``, a 3D :class:`LabelAtlas` image is + loaded. Otherwise, if the atlas is probabilistic, + a 4D :class:`ProbabilisticAtlas` image is loaded. + """ + + if len(ATLAS_DESCRIPTIONS) == 0: + listAtlases() + + atlasDesc = ATLAS_DESCRIPTIONS[atlasID] + + # label atlases are only + # available in 'summary' form + if atlasDesc.atlasType == 'label': + loadSummary = True + + if loadSummary: atlas = LabelAtlas( atlasDesc) + else: atlas = ProbabilisticAtlas(atlasDesc) + + return atlas class AtlasDescription(object): diff --git a/fsl/data/strings.py b/fsl/data/strings.py index f82b42375..42cbe247d 100644 --- a/fsl/data/strings.py +++ b/fsl/data/strings.py @@ -48,6 +48,8 @@ titles = TypeDict({ 'TimeSeriesPanel' : 'Time series', 'SpacePanel' : 'Space inspector', + 'AtlasInfoPanel' : 'Atlas information', + 'AtlasOverlayPanel' : 'Atlas overlays' }) @@ -88,10 +90,6 @@ labels = TypeDict({ 'LocationPanel.volumeLabel' : 'Volume', 'LocationPanel.spaceLabel' : 'Space', 'LocationPanel.outOfBounds' : 'Out of bounds', - - 'AtlasPanel.infoPanel' : 'Information', - 'AtlasPanel.atlasListPanel' : 'Choose atlases', - 'AtlasPanel.overlayPanel' : 'Choose overlays' }) diff --git a/fsl/fslview/controls/atlasinfopanel.py b/fsl/fslview/controls/atlasinfopanel.py new file mode 100644 index 000000000..43156bb61 --- /dev/null +++ b/fsl/fslview/controls/atlasinfopanel.py @@ -0,0 +1,165 @@ +#!/usr/bin/env python +# +# atlasinfopanel.py - +# +# Author: Paul McCarthy <pauldmccarthy@gmail.com> +# + +import logging + +import wx +import wx.html as wxhtml + +import pwidgets.elistbox as elistbox + +import fsl.fslview.panel as fslpanel +import fsl.data.atlases as atlases + + +log = logging.getLogger(__name__) + + + +class AtlasListWidget(wx.Panel): + + def __init__(self, parent, atlasPanel, atlasID): + + wx.Panel.__init__(self, parent) + + self.atlasID = atlasID + self.atlasPanel = atlasPanel + self.enableBox = wx.CheckBox(self) + + self.sizer = wx.BoxSizer(wx.HORIZONTAL) + self.sizer.Add(self.enableBox, flag=wx.EXPAND) + + self.enableBox.Bind(wx.EVT_CHECKBOX, self.onEnable) + + + def onEnable(self, ev): + + if self.enableBox.GetValue(): + self.atlasPanel.enableAtlasInfo(self.atlasID) + else: + self.atlasPanel.disableAtlasInfo(self.atlasID) + + +# TODO +# +# Actions to show/hide atlas overlays are managed by the AtlasPanel, +# which provides methods to do so, and maintains the state required +# to track whether an overlay is enabled/disabled. Thec +# AtlasInfoPanel and AtlasOverlayPanel implement the GUI +# functionality, and the event handling needed to allow the user +# to toggle atlas information/overlays on/off, but management +# of the in-memory atlas images is handled by the AtlasPanel. + + +# Info panel, containing atlas-based regional +# proportions/labels for the current location + +# Atlas list, containing a list of atlases +# that the user can choose from +class AtlasInfoPanel(fslpanel.FSLViewPanel): + + def __init__(self, parent, imageList, displayCtx, atlasPanel): + fslpanel.FSLViewPanel.__init__(self, parent, imageList, displayCtx) + + + self.atlasPanel = atlasPanel + self.contentPanel = wx.SplitterWindow(self) + self.infoPanel = wxhtml.HtmlWindow(self.notebook) + + self.atlasList = elistbox.EditableListBox( + self, + style=(elistbox.ELB_NO_ADD | + elistbox.ELB_NO_REMOVE | + elistbox.ELB_NO_MOVE)) + + for i, atlasDesc in enumerate(atlases.listAtlases()): + + self.atlasList.Append(atlasDesc.name, atlasDesc.atlasID) + widget = AtlasListWidget(self.atlasList, atlasPanel, atlasDesc) + self.atlasList.SetItemWidget(i, widget) + + # The info panel contains clickable links + # for the currently displayed regions - + # when a link is clicked, the location + # is centred at the corresponding region + self.infoPanel.Bind(wxhtml.EVT_HTML_LINK_CLICKED, + self._infoPanelLinkClicked) + + displayCtx.addListener('location', self._name, self._locationChanged) + + + def _infoPanelLinkClicked(self, ev): + + showType, atlasID, labelIndex = ev.GetLinkInfo().GetHref().split() + labelIndex = int(labelIndex) + atlas = self.enabledAtlases[atlasID] + label = atlas.desc.labels[labelIndex] + + log.debug('{}/{} ({}) clicked'.format(atlasID, label.name, showType)) + + if showType == 'summary': + self.overlayPanel.toggleSummaryOverlay(atlas.desc) + + elif showType == 'prob': + self.overlayPanel.toggleOverlay(atlas.desc, labelIndex, False) + + elif showType == 'label': + self.overlayPanel.toggleOverlay(atlas.desc, labelIndex, True) + + + def _locationChanged(self, *a): + + image = self._displayCtx.getSelectedImage() + display = self._displayCtx.getDisplayProperties(image) + loc = self._displayCtx.location + text = self.infoPanel + loc = transform.transform([loc], display.displayToWorldMat)[0] + + if len(self.enabledAtlases) == 0: + text.SetPage(strings.messages['atlaspanel.chooseAnAtlas']) + return + + if image.getXFormCode() != constants.NIFTI_XFORM_MNI_152: + text.SetPage(strings.messages['atlaspanel.notMNISpace']) + return + + lines = [] + + + titleTemplate = '<b>{}</b> (<a href="summary {} {}">Show/Hide</a>)' + labelTemplate = '{} (<a href="label {} {}">Show/Hide</a>)' + probTemplate = '{:0.2f}% {} (<a href="prob {} {}">Show/Hide</a>)' + + for atlasID, atlas in self.enabledAtlases.items(): + + lines.append(titleTemplate.format(atlas.desc.name, atlasID, 0)) + + if isinstance(atlas, atlases.ProbabilisticAtlas): + proportions = atlas.proportions(loc) + + for label, prop in zip(atlas.desc.labels, proportions): + if prop == 0.0: + continue + lines.append(probTemplate.format(prop, + label.name, + atlasID, + label.index, + atlasID, + label.index)) + + elif isinstance(atlas, atlases.LabelAtlas): + + labelVal = atlas.label(loc) + label = atlas.desc.labels[int(labelVal)] + lines.append(labelTemplate.format(label.name, + atlasID, + label.index, + atlasID, + label.index)) + + text.SetPage('<br>'.join(lines)) + diff --git a/fsl/fslview/controls/atlasoverlaypanel.py b/fsl/fslview/controls/atlasoverlaypanel.py new file mode 100644 index 000000000..6d149201a --- /dev/null +++ b/fsl/fslview/controls/atlasoverlaypanel.py @@ -0,0 +1,246 @@ +#!/usr/bin/env python +# +# atlasoverlaypanel.py - +# +# Author: Paul McCarthy <pauldmccarthy@gmail.com> +# +""" +""" + +import logging + +import wx +import numpy as np + +import pwidgets.elistbox as elistbox + +import fsl.data.atlases as atlases +import fsl.data.image as fslimage +import fsl.data.constants as constants +import fsl.fslview.panel as fslpanel + + +log = logging.getLogger(__name__) + + + + +class OverlayListWidget(wx.CheckBox): + + def __init__(self, parent, atlasDesc, label=None): + + wx.CheckBox.__init__(self, parent) + + self.parent = parent + self.atlasDesc = atlasDesc + self.label = label + + + def _onCheck(self, ev): + + pass + + +class AtlasOverlayPanel(fslpanel.FSLViewPanel): + + def __init__(self, parent, imageList, displayCtx, atlasPanel): + + fslpanel.FSLViewPanel.__init__(self, parent, imageList, displayCtx) + + self.atlasPanel = atlasPanel + + self.enabledOverlays = {} + self.atlasList = elistbox.EditableListBox( + self, + style=(elistbox.ELB_NO_ADD | + elistbox.ELB_NO_REMOVE | + + elistbox.ELB_NO_MOVE)) + + self.regionPanel = wx.Panel( self) + self.regionFilter = wx.TextCtrl(self.regionPanel) + + self.regionLists = [None] * len(atlasDescs) + + self.sizer = wx.BoxSizer(wx.HORIZONTAL) + self.regionSizer = wx.BoxSizer(wx.VERTICAL) + + self.sizer .Add(self.atlasList, flag=wx.EXPAND, proportion=1) + self.regionSizer.Add(self.regionFilter, flag=wx.EXPAND) + self.regionSizer.AddStretchSpacer() + self.sizer .Add(self.regionPanel, flag=wx.EXPAND, proportion=1) + + self.regionPanel.SetSizer(self.regionSizer) + self .SetSizer(self.sizer) + + for i, atlasDesc in enumerate(atlasDescs): + self.atlasList.Append(atlasDesc.name, atlasDesc) + self._updateAtlasState(i) + + self.regionFilter.Bind(wx.EVT_TEXT, self._onRegionFilter) + self.atlasList.Bind(elistbox.EVT_ELB_SELECT_EVENT, self._onAtlasSelect) + + self.regionSizer.Layout() + self.sizer .Layout() + + + def _onRegionFilter(self, ev): + + filterStr = self.regionFilter.GetValue().lower() + + for i, listBox in enumerate(self.regionLists): + + if listBox is None: + continue + + listBox.ApplyFilter(filterStr, ignoreCase=True) + self._updateAtlasState(i) + + + def _updateAtlasState(self, atlasIdx): + + listBox = self.regionLists[atlasIdx] + + if listBox is None: + weight = wx.FONTWEIGHT_LIGHT + colour = '#a0a0a0' + + elif listBox.VisibleItemCount() == 0: + weight = wx.FONTWEIGHT_LIGHT + colour = '#303030' + else: + weight = wx.FONTWEIGHT_BOLD + colour = '#000000' + + font = self.atlasList.GetItemFont(atlasIdx) + font.SetWeight(weight) + + self.atlasList.SetItemFont(atlasIdx, font) + self.atlasList.SetItemForegroundColour(atlasIdx, colour, colour) + + + def _onAtlasSelect(self, ev): + + atlasDesc = ev.data + atlasIdx = ev.idx + regionList = self.regionLists[atlasIdx] + + if regionList is None: + + regionList = elistbox.EditableListBox( + self.regionPanel, + style=(elistbox.ELB_NO_ADD | + elistbox.ELB_NO_REMOVE | + elistbox.ELB_NO_MOVE)) + + print 'Creating region list for {} ({})'.format( + atlasDesc.atlasID, id(regionList)) + + self.regionLists[atlasIdx] = regionList + + for label in atlasDesc.labels: + regionList.Append(label.name) + + filterStr = self.regionFilter.GetValue().lower() + regionList.ApplyFilter(filterStr, ignoreCase=True) + + self._updateAtlasState(atlasIdx) + + print 'Showing region list for {} ({})'.format( + atlasDesc.atlasID, id(regionList)) + + old = self.regionSizer.GetItem(1).GetWindow() + + if old is not None: + old.Show(False) + + regionList.Show(True) + self.regionSizer.Remove(1) + + self.regionSizer.Insert(1, regionList, flag=wx.EXPAND, proportion=1) + self.regionSizer.Layout() + + + def overlayIsEnabled(self, atlasDesc, label=None, summary=False): + pass + + + def toggleSummaryOverlay(self, atlasDesc): + + atlasID = atlasDesc.atlasID + overlayName = '{}/all'.format(atlasID) + overlay = self._imageList.find(overlayName) + + if overlay is not None: + self._imageList.remove(overlay) + log.debug('Removed summary overlay {}'.format(overlayName)) + + else: + overlay = self.enabledAtlases.get(atlasID, None) + if overlay is None or \ + isinstance(overlay, atlases.ProbabilisticAtlas): + overlay = atlases.loadAtlas(atlasDesc, True) + + overlay.name = overlayName + + # Even though all the FSL atlases + # are in MNI152 space, not all of + # their sform_codes are correctly set + overlay.nibImage.get_header().set_sform( + None, code=constants.NIFTI_XFORM_MNI_152) + + self._imageList.append(overlay) + log.debug('Added summary overlay {}'.format(overlayName)) + + + def toggleOverlay(self, atlasDesc, labelIndex, label): + """ + """ + + atlasID = atlasDesc.atlasID + overlayName = '{}/{}'.format(atlasID, + atlasDesc.labels[labelIndex].name) + overlay = self._imageList.find(overlayName) + + if overlay is not None: + self._imageList.remove(overlay) + log.debug('Removed overlay {}'.format(overlayName)) + + else: + atlas = self.enabledAtlases.get(atlasID, None) + if atlas is None or \ + (label and isinstance(overlay, atlases.LabelAtlas)): + atlas = atlases.loadAtlas(atlasDesc, True) + + if label: + if atlasDesc.atlasType == 'probabilistic': + labelVal = labelIndex + 1 + elif atlasDesc.atlasType == 'label': + labelVal = labelIndex + + mask = np.zeros(atlas.shape, dtype=np.uint8) + mask[atlas.data == labelIndex] = labelVal + else: + mask = atlas.data[..., labelIndex] + + overlay = fslimage.Image( + mask, + header=atlas.nibImage.get_header(), + name=overlayName) + + # See comment in toggleSummaryOverlay + overlay.nibImage.get_header().set_sform( + None, code=constants.NIFTI_XFORM_MNI_152) + + if label: + overlay.imageType = 'mask' + + self._imageList.append(overlay) + log.debug('Added overlay {}'.format(overlayName)) + + display = self._displayCtx.getDisplayProperties(overlay) + + if label: + display.getDisplayOpts().colour = np.random.random(3) + else: + display.getDisplayOpts().cmap = 'hot' diff --git a/fsl/fslview/controls/atlaspanel.py b/fsl/fslview/controls/atlaspanel.py index 036cb4915..874a32adc 100644 --- a/fsl/fslview/controls/atlaspanel.py +++ b/fsl/fslview/controls/atlaspanel.py @@ -7,47 +7,27 @@ import logging -import wx -import wx.html as wxhtml -import numpy as np +import wx +import wx.html as wxhtml +import wx.lib.newevent as wxevent +import numpy as np import pwidgets.elistbox as elistbox import pwidgets.notebook as notebook -import fsl.utils.transform as transform -import fsl.data.image as fslimage -import fsl.data.atlases as atlases -import fsl.data.strings as strings -import fsl.data.constants as constants -import fsl.fslview.panel as fslpanel +import fsl.utils.transform as transform +import fsl.data.image as fslimage +import fsl.data.atlases as atlases +import fsl.data.strings as strings +import fsl.data.constants as constants +import fsl.fslview.panel as fslpanel +import fsl.fslview.controls.atlasoverlaypanel as atlasoverlaypanel +import fsl.fslview.controls.atlasinfopanel as atlasinfopanel log = logging.getLogger(__name__) -class AtlasListWidget(wx.Panel): - - def __init__(self, parent, atlasPanel, atlasDesc): - - wx.Panel.__init__(self, parent) - - self.atlasDesc = atlasDesc - self.atlasPanel = atlasPanel - self.enableBox = wx.CheckBox(self) - - self.sizer = wx.BoxSizer(wx.HORIZONTAL) - self.sizer.Add(self.enableBox, flag=wx.EXPAND) - - self.enableBox.Bind(wx.EVT_CHECKBOX, self.onEnable) - - def onEnable(self, ev): - - if self.enableBox.GetValue(): - self.atlasPanel.enableAtlasInfo(self.atlasDesc.atlasID) - else: - self.atlasPanel.disableAtlasInfo(self.atlasDesc.atlasID) - - class AtlasPanel(fslpanel.FSLViewPanel): @@ -55,6 +35,10 @@ class AtlasPanel(fslpanel.FSLViewPanel): fslpanel.FSLViewPanel.__init__(self, parent, imageList, displayCtx) + self.atlasDescs = sorted(atlases.listAtlases(), + key=lambda d: d.name) + self.enabledAtlases = {} + self.notebook = notebook.Notebook(self) self.sizer = wx.BoxSizer(wx.HORIZONTAL) @@ -62,237 +46,39 @@ class AtlasPanel(fslpanel.FSLViewPanel): self.SetSizer(self.sizer) - # Info panel, containing atlas-based regional - # proportions/labels for the current location - self.infoPanel = wxhtml.HtmlWindow(self.notebook) - self.infoPanel.SetMinSize((-1, 100)) + # Temporary + self.SetMaxSize((-1, 200)) - # Atlas list, containing a list of atlases - # that the user can choose from - self.atlasPanel = wx.Panel(self.notebook) - self.atlasFilter = wx.TextCtrl(self.atlasPanel) - self.atlasList = elistbox.EditableListBox( - self.atlasPanel, - style=(elistbox.ELB_NO_ADD | - elistbox.ELB_NO_REMOVE | - elistbox.ELB_NO_MOVE)) + self.atlasInfoPanel = atlasinfopanel.AtlasInfoPanel( + self.notebook, imageList, displayCtx, self) # Overlay panel, containing a list of regions, # allowing the user to add/remove overlays - self.overlayPanel = wx.Panel(self.notebook) + self.overlayPanel = atlasoverlaypanel.AtlasOverlayPanel( + self.notebook, imageList, displayCtx, self) self.notebook.AddPage(self.infoPanel, - strings.labels['AtlasPanel.infoPanel']) - self.notebook.AddPage(self.atlasPanel, - strings.labels['AtlasPanel.atlasListPanel']) + strings.labels[self.infoPanel]) self.notebook.AddPage(self.overlayPanel, - strings.labels['AtlasPanel.overlayPanel']) + strings.labels[self.overlayPanel]) self.notebook.ShowPage(0) - # The info panel contains clickable links - # for the currently displayed regions - - # when a link is clicked, the location - # is centred at the corresponding region - self.infoPanel.Bind(wxhtml.EVT_HTML_LINK_CLICKED, - self._infoPanelLinkClicked) - # Set up the atlas list, allowing the - # user to choose which atlases information - # (in the info panel) is shown for - self.atlasSizer = wx.BoxSizer(wx.VERTICAL) - self.atlasSizer.Add(self.atlasFilter, flag=wx.EXPAND) - self.atlasSizer.Add(self.atlasList, flag=wx.EXPAND, proportion=1) - self.atlasPanel.SetSizer(self.atlasSizer) - - self.atlasFilter.Bind(wx.EVT_TEXT, self._onAtlasFilter) - - self.atlasDescs = atlases.listAtlases() - self.enabledAtlases = {} - - listItems = sorted(self.atlasDescs.items(), key=lambda (a, d): d.name) - - for i, (atlasID, desc) in enumerate(listItems): - - self.atlasList.Append(desc.name, atlasID) - widget = AtlasListWidget(self.atlasList, self, desc) - self.atlasList.SetItemWidget(i, widget) - - displayCtx.addListener('location', self._name, self._locationChanged) self.Layout() - - def _infoPanelLinkClicked(self, ev): - - showType, atlasID, labelIndex = ev.GetLinkInfo().GetHref().split() - labelIndex = int(labelIndex) - atlas = self.enabledAtlases[atlasID] - label = atlas.desc.labels[labelIndex] - - log.debug('{}/{} ({}) clicked'.format(atlasID, label.name, showType)) - - if showType == 'summary': - self.toggleSummaryOverlay(atlasID) - - elif showType == 'prob': - self.toggleOverlay(atlasID, labelIndex, False) - - elif showType == 'label': - self.toggleOverlay(atlasID, labelIndex, True) - - - def _onAtlasFilter(self, ev): - filterStr = self.atlasFilter.GetValue().lower() - self.atlasList.ApplyFilter(filterStr, ignoreCase=True) - - def enableAtlasInfo(self, atlasID): - - desc = self.atlasDescs[atlasID] - atlasImage = atlases.loadAtlas(desc) + def enableAtlasInfo(self, atlasDesc): - self.enabledAtlases[atlasID] = atlasImage + atlasImage = atlases.loadAtlas(atlasDesc) + self.enabledAtlases[atlasDesc.atlasID] = atlasImage self._locationChanged() - def disableAtlasInfo(self, atlasID): + def disableAtlasInfo(self, atlasDesc): - self.enabledAtlases.pop(atlasID, None) + self.enabledAtlases.pop(atlasDesc.atlasID, None) self._locationChanged() - - def toggleSummaryOverlay(self, atlasID): - - overlayName = '{}/all'.format(atlasID) - overlay = self._imageList.find(overlayName) - - if overlay is not None: - self._imageList.remove(overlay) - log.debug('Removed summary overlay {}'.format(overlayName)) - - else: - overlay = self.enabledAtlases.get(atlasID, None) - if overlay is None or \ - isinstance(overlay, atlases.ProbabilisticAtlas): - overlay = atlases.loadAtlas(self.atlasDescs[atlasID], True) - - overlay.name = overlayName - - # Even though all the FSL atlases - # are in MNI152 space, not all of - # their sform_codes are correctly set - overlay.nibImage.get_header().set_sform( - None, code=constants.NIFTI_XFORM_MNI_152) - - self._imageList.append(overlay) - log.debug('Added summary overlay {}'.format(overlayName)) - - - - - def toggleOverlay(self, atlasID, labelIndex, label): - """ - """ - - desc = self.atlasDescs[atlasID] - overlayName = '{}/{}'.format(atlasID, desc.labels[labelIndex].name) - overlay = self._imageList.find(overlayName) - - if overlay is not None: - self._imageList.remove(overlay) - log.debug('Removed overlay {}'.format(overlayName)) - - else: - atlas = self.enabledAtlases.get(atlasID, None) - if atlas is None or \ - (label and isinstance(overlay, atlases.LabelAtlas)): - atlas = atlases.loadAtlas(self.atlasDescs[atlasID], True) - - if label: - if desc.atlasType == 'probabilistic': - labelVal = labelIndex + 1 - elif desc.atlasType == 'label': - labelVal = labelIndex - - mask = np.zeros(atlas.shape, dtype=np.uint8) - mask[atlas.data == labelIndex] = labelVal - else: - mask = atlas.data[..., labelIndex] - - overlay = fslimage.Image( - mask, - header=atlas.nibImage.get_header(), - name=overlayName) - - # See comment in toggleSummaryOverlay - overlay.nibImage.get_header().set_sform( - None, code=constants.NIFTI_XFORM_MNI_152) - - if label: - overlay.imageType = 'mask' - - self._imageList.append(overlay) - log.debug('Added overlay {}'.format(overlayName)) - - display = self._displayCtx.getDisplayProperties(overlay) - - if label: - display.getDisplayOpts().colour = np.random.random(3) - else: - display.getDisplayOpts().cmap = 'hot' - - - - def _locationChanged(self, *a): - - image = self._displayCtx.getSelectedImage() - display = self._displayCtx.getDisplayProperties(image) - loc = self._displayCtx.location - text = self.infoPanel - loc = transform.transform([loc], display.displayToWorldMat)[0] - - if len(self.enabledAtlases) == 0: - text.SetPage(strings.messages['atlaspanel.chooseAnAtlas']) - return - - if image.getXFormCode() != constants.NIFTI_XFORM_MNI_152: - text.SetPage(strings.messages['atlaspanel.notMNISpace']) - return - - lines = [] - - - titleTemplate = '<b>{}</b> (<a href="summary {} {}">Show/Hide</a>)' - labelTemplate = '{} (<a href="label {} {}">Show/Hide</a>)' - probTemplate = '{:0.2f}% {} (<a href="prob {} {}">Show/Hide</a>)' - - for atlasID, atlas in self.enabledAtlases.items(): - - lines.append(titleTemplate.format(atlas.desc.name, atlasID, 0)) - - if isinstance(atlas, atlases.ProbabilisticAtlas): - proportions = atlas.proportions(loc) - - for label, prop in zip(atlas.desc.labels, proportions): - if prop == 0.0: - continue - lines.append(probTemplate.format(prop, - label.name, - atlasID, - label.index, - atlasID, - label.index)) - - elif isinstance(atlas, atlases.LabelAtlas): - - labelVal = atlas.label(loc) - label = atlas.desc.labels[int(labelVal)] - lines.append(labelTemplate.format(label.name, - atlasID, - label.index, - atlasID, - label.index)) - - text.SetPage('<br>'.join(lines)) -- GitLab