Skip to content
Snippets Groups Projects
Commit 0f834c20 authored by Paul McCarthy's avatar Paul McCarthy
Browse files

Initial work on melodic classification panel. This is bound to change.

parent 1eabacde
No related branches found
No related tags found
No related merge requests found
...@@ -79,9 +79,11 @@ class MelodicImage(fslimage.Image): ...@@ -79,9 +79,11 @@ class MelodicImage(fslimage.Image):
*args, *args,
**kwargs) **kwargs)
self.__meldir = dirname self.__meldir = dirname
self.__melmix = melresults.getComponentTimeSeries( dirname) self.__melmix = melresults.getComponentTimeSeries( dirname)
self.__melFTmix = melresults.getComponentPowerSpectra(dirname) self.__melFTmix = melresults.getComponentPowerSpectra(dirname)
self.__melICClass = melresults.MelodicClassification(
self.numComponents())
# Automatically set the # Automatically set the
# TR value if possible # TR value if possible
...@@ -92,6 +94,8 @@ class MelodicImage(fslimage.Image): ...@@ -92,6 +94,8 @@ class MelodicImage(fslimage.Image):
if dataImage.is4DImage(): if dataImage.is4DImage():
self.tr = dataImage.pixdim[3] self.tr = dataImage.pixdim[3]
# TODO load classifications if present
def getComponentTimeSeries(self, component): def getComponentTimeSeries(self, component):
"""Returns the time course for the specified (0-indexed) component. """ """Returns the time course for the specified (0-indexed) component. """
...@@ -125,3 +129,7 @@ class MelodicImage(fslimage.Image): ...@@ -125,3 +129,7 @@ class MelodicImage(fslimage.Image):
:func:`.melodicresults.getDataFile` function. :func:`.melodicresults.getDataFile` function.
""" """
return melresults.getDataFile(self.__meldir) return melresults.getDataFile(self.__meldir)
def getICClassification(self):
return self.__melICClass
...@@ -179,3 +179,82 @@ def getComponentPowerSpectra(meldir): ...@@ -179,3 +179,82 @@ def getComponentPowerSpectra(meldir):
""" """
ftmixfile = getFTMixFile(meldir) ftmixfile = getFTMixFile(meldir)
return np.loadtxt(ftmixfile) return np.loadtxt(ftmixfile)
class MelodicClassification(object):
"""
"""
def __init__(self, ncomps):
"""Create a ``MelodicClassification`` instance.
"""
self.__ncomps = ncomps
self.__componentLabels = [[] for i in range(ncomps)]
self.__labelComponents = {}
def load(self, filename):
pass
def save(self, filename):
pass
def getLabels(self, component):
return list(self.__componentLabels[component])
def addLabel(self, component, label):
cmpLabels = self.__componentLabels[component]
labelCmps = self.__labelComponents.get(label, [])
if label in cmpLabels:
return
cmpLabels[component].append(label)
labelCmps[label] .append(component)
self.__componentLabels[component] = cmpLabels
self.__labelComponents[label] = labelCmps
def removeLabel(self, component, label):
if label not in self.__componentLabels[component]:
return
self.__componentLabels[component].remove(label)
self.__labelComponents[label] .remove(component)
def clearLabels(self, component):
labels = self.getLabels(component)
for l in labels:
self.removeLabel(component, l)
def getComponents(self, label):
return list(self.__labelComponents.get(label, []))
def addComponent(self, label, component):
self.addLabel(component, label)
def removeComponent(self, label, component):
self.removeLabel(component, label)
def clearComponents(self, label):
components = self.getComponents(label)
for c in components:
self.removeComponent(label, c)
...@@ -122,6 +122,9 @@ messages = TypeDict({ ...@@ -122,6 +122,9 @@ messages = TypeDict({
'OrthoEditProfile.displaySpaceChange' : 'Setting {} as the display ' 'OrthoEditProfile.displaySpaceChange' : 'Setting {} as the display '
'space reference image - this ' 'space reference image - this '
'is necessary for editing.', 'is necessary for editing.',
'MelodicClassificationPanel.disabled' : 'Choose a melodic image.',
}) })
...@@ -173,6 +176,8 @@ titles = TypeDict({ ...@@ -173,6 +176,8 @@ titles = TypeDict({
'OverlayInfoPanel' : 'Overlay information', 'OverlayInfoPanel' : 'Overlay information',
'ShellPanel' : 'Python shell', 'ShellPanel' : 'Python shell',
'MelodicClassificationPanel' : 'Melodic IC classification',
'LookupTablePanel.loadLut' : 'Select a lookup table file', 'LookupTablePanel.loadLut' : 'Select a lookup table file',
'LookupTablePanel.labelExists' : 'Label already exists', 'LookupTablePanel.labelExists' : 'Label already exists',
}) })
...@@ -188,17 +193,18 @@ actions = TypeDict({ ...@@ -188,17 +193,18 @@ actions = TypeDict({
'FSLEyesFrame.closeViewPanel' : 'Close', 'FSLEyesFrame.closeViewPanel' : 'Close',
'CanvasPanel.screenshot' : 'Take screenshot', 'CanvasPanel.screenshot' : 'Take screenshot',
'CanvasPanel.showCommandLineArgs' : 'Show command line for scene', 'CanvasPanel.showCommandLineArgs' : 'Show command line for scene',
'CanvasPanel.toggleColourBar' : 'Colour bar', 'CanvasPanel.toggleColourBar' : 'Colour bar',
'CanvasPanel.toggleOverlayList' : 'Overlay list', 'CanvasPanel.toggleOverlayList' : 'Overlay list',
'CanvasPanel.toggleDisplayProperties' : 'Overlay display properties', 'CanvasPanel.toggleDisplayProperties' : 'Overlay display properties',
'CanvasPanel.toggleLocationPanel' : 'Location panel', 'CanvasPanel.toggleLocationPanel' : 'Location panel',
'CanvasPanel.toggleAtlasPanel' : 'Atlas panel', 'CanvasPanel.toggleAtlasPanel' : 'Atlas panel',
'CanvasPanel.toggleLookupTablePanel' : 'Lookup tables', 'CanvasPanel.toggleLookupTablePanel' : 'Lookup tables',
'CanvasPanel.toggleClusterPanel' : 'Cluster browser', 'CanvasPanel.toggleClusterPanel' : 'Cluster browser',
'CanvasPanel.toggleOverlayInfo' : 'Overlay information', 'CanvasPanel.toggleOverlayInfo' : 'Overlay information',
'CanvasPanel.toggleShell' : 'Python shell', 'CanvasPanel.toggleClassificationPanel' : 'Melodic IC classification',
'CanvasPanel.toggleShell' : 'Python shell',
'OrthoPanel.toggleOrthoToolBar' : 'View properties', 'OrthoPanel.toggleOrthoToolBar' : 'View properties',
'OrthoPanel.toggleEditToolBar' : 'Edit toolbar', 'OrthoPanel.toggleEditToolBar' : 'Edit toolbar',
......
#!/usr/bin/env python
#
# melodicclassificationpanel.py - The MelodicClassificationPanel class.
#
# Author: Paul McCarthy <pauldmccarthy@gmail.com>
#
"""This module provides the :class:`MelodicClassificationPanel` class, a
*FSLeyes control* panel which allows the user to classify the components
of a :class:`.MelodicImage`.
"""
import wx
import pwidgets.widgetgrid as widgetgrid
import pwidgets.texttag as texttag
import pwidgets.notebook as notebook
import fsl.data.strings as strings
import fsl.data.melodicimage as fslmelimage
import fsl.fsleyes.panel as fslpanel
class MelodicClassificationPanel(fslpanel.FSLEyesPanel):
"""The ``MelodicClassificationPanel``
"""
#
# Choose label colours
# Load/save from/to file
#
def __init__(self, parent, overlayList, displayCtx):
"""Create a ``MelodicClassificationPanel``.
:arg parent: The :mod:`wx` parent object.
:arg overlayList: The :class:`.OverlayList`.
:arg displayCtx: The :class:`.DisplayContext` instance.
"""
fslpanel.FSLEyesPanel.__init__(self, parent, overlayList, displayCtx)
self.__disabledText = wx.StaticText(
self,
style=(wx.ALIGN_CENTRE_HORIZONTAL |
wx.ALIGN_CENTRE_VERTICAL))
self.__notebook = notebook.Notebook(self)
self.__componentGrid = ComponentGrid( self.__notebook)
self.__labelGrid = LabelGrid( self.__notebook)
self.__notebook.AddPage(self.__componentGrid, 'Components')
self.__notebook.AddPage(self.__labelGrid, 'Labels')
self.__mainSizer = wx.BoxSizer(wx.HORIZONTAL)
self.__mainSizer.Add(self.__notebook, flag=wx.EXPAND, proportion=1)
# TODO Things which you don't want shown when
# a melodic image is not selected should
# be added to __mainSizer. Things which
# you always want displayed should be
# added to __sizer (but need to be laid
# out w.r.t. __disabledText/__mainSizer)
self.__sizer = wx.BoxSizer(wx.HORIZONTAL)
self.__sizer.Add(self.__disabledText, flag=wx.EXPAND, proportion=1)
self.__sizer.Add(self.__mainSizer, flag=wx.EXPAND, proportion=1)
self.SetSizer(self.__sizer)
overlayList.addListener('overlays',
self._name,
self.__selectedOverlayChanged)
displayCtx .addListener('selectedOverlay',
self._name,
self.__selectedOverlayChanged)
self.__overlay = None
self.__selectedOverlayChanged()
def destroy(self):
"""
"""
self._displayCtx .removeListener('selectedOverlay', self._name)
self._overlayList.removeListener('overlays', self._name)
fslpanel.FSLEyesPanel.destroy(self)
def __enable(self, enable=True, message=''):
"""
"""
self.__disabledText.SetLabel(message)
self.__sizer.Show(self.__disabledText, not enable)
self.__sizer.Show(self.__mainSizer, enable)
self.Layout()
def __selectedOverlayChanged(self, *a):
overlay = self._displayCtx.getSelectedOverlay()
if (overlay is None) or \
not isinstance(overlay, fslmelimage.MelodicImage):
self.__enable(False, strings.messages[self, 'disabled'])
return
self.__overlay = overlay
self.__componentGrid.setOverlay(overlay)
self.__labelGrid .setOverlay(overlay)
self.__enable(True)
class ComponentGrid(wx.Panel):
def __init__(self, parent):
wx.Panel.__init__(self, parent)
self.__grid = widgetgrid.WidgetGrid(self)
self.__sizer = wx.BoxSizer(wx.HORIZONTAL)
self.__grid.ShowRowLabels(False)
self.__grid.ShowColLabels(True)
self.__sizer.Add(self.__grid, flag=wx.EXPAND, proportion=1)
self.SetSizer(self.__sizer)
def setOverlay(self, overlay):
numComps = overlay.numComponents()
self.__grid.ClearGrid()
self.__grid.SetGridSize(numComps, 2, growCols=[1])
self.__grid.SetColLabel(0, 'Component #')
self.__grid.SetColLabel(1, 'Labels')
for i in range(numComps):
tags = texttag.TextTagPanel(self.__grid,
style=(texttag.TTP_ALLOW_NEW_TAGS |
texttag.TTP_ADD_NEW_TAGS |
texttag.TTP_NO_DUPLICATES))
self.__grid.SetText( i, 0, str(i))
self.__grid.SetWidget(i, 1, tags)
self.Layout()
class LabelGrid(wx.Panel):
def __init__(self, parent):
wx.Panel.__init__(self, parent)
self.__grid = widgetgrid.WidgetGrid(self)
self.__sizer = wx.BoxSizer(wx.HORIZONTAL)
self.__sizer.Add(self.__grid, flag=wx.EXPAND, proportion=1)
self.SetSizer(self.__sizer)
def setOverlay(self, overlay):
pass
...@@ -21,23 +21,23 @@ import matplotlib.image as mplimg ...@@ -21,23 +21,23 @@ import matplotlib.image as mplimg
import props import props
import fsl.fsleyes.fsleyes_parseargs as fsleyes_parseargs import fsl.fsleyes.fsleyes_parseargs as fsleyes_parseargs
import fsl.utils.dialog as fsldlg import fsl.utils.dialog as fsldlg
import fsl.utils.settings as fslsettings import fsl.utils.settings as fslsettings
import fsl.data.image as fslimage import fsl.data.image as fslimage
import fsl.data.strings as strings import fsl.data.strings as strings
import fsl.fsleyes.displaycontext as displayctx import fsl.fsleyes.displaycontext as displayctx
import fsl.fsleyes.controls.overlaylistpanel as overlaylistpanel import fsl.fsleyes.controls.overlaylistpanel as overlaylistpanel
import fsl.fsleyes.controls.overlayinfopanel as overlayinfopanel import fsl.fsleyes.controls.overlayinfopanel as overlayinfopanel
import fsl.fsleyes.controls.atlaspanel as atlaspanel import fsl.fsleyes.controls.atlaspanel as atlaspanel
import fsl.fsleyes.controls.overlaydisplaytoolbar as overlaydisplaytoolbar import fsl.fsleyes.controls.overlaydisplaytoolbar as overlaydisplaytoolbar
import fsl.fsleyes.controls.locationpanel as locationpanel import fsl.fsleyes.controls.locationpanel as locationpanel
import fsl.fsleyes.controls.clusterpanel as clusterpanel import fsl.fsleyes.controls.clusterpanel as clusterpanel
import fsl.fsleyes.controls.lookuptablepanel as lookuptablepanel import fsl.fsleyes.controls.lookuptablepanel as lookuptablepanel
import fsl.fsleyes.controls.shellpanel as shellpanel import fsl.fsleyes.controls.melodicclassificationpanel as melclasspanel
import fsl.fsleyes.controls.shellpanel as shellpanel
import colourbarpanel import colourbarpanel
import viewpanel import viewpanel
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
...@@ -84,16 +84,18 @@ class CanvasPanel(viewpanel.ViewPanel): ...@@ -84,16 +84,18 @@ class CanvasPanel(viewpanel.ViewPanel):
:mod:`control <.controls>` panels: :mod:`control <.controls>` panels:
=========================== =========================================== ============================== ===========================================
``toggleOverlayList`` Toggles an :class:`.OverlayListPanel`. ``toggleOverlayList`` Toggles an :class:`.OverlayListPanel`.
``toggleOverlayInfo`` Toggles an :class:`.OverlayInfoPanel`. ``toggleOverlayInfo`` Toggles an :class:`.OverlayInfoPanel`.
``toggleAtlasPanel`` Toggles an :class:`.AtlasPanel`. ``toggleAtlasPanel`` Toggles an :class:`.AtlasPanel`.
``toggleDisplayProperties`` Toggles an :class:`.OverlayDisplayToolBar`. ``toggleDisplayProperties`` Toggles an :class:`.OverlayDisplayToolBar`.
``toggleLocationPanel`` Toggles a :class:`.LocationPanel`. ``toggleLocationPanel`` Toggles a :class:`.LocationPanel`.
``toggleClusterPanel`` Toggles a :class:`.ClusterPanel`. ``toggleClusterPanel`` Toggles a :class:`.ClusterPanel`.
``toggleLookupTablePanel`` Toggles a :class:`.LookupTablePanel`. ``toggleLookupTablePanel`` Toggles a :class:`.LookupTablePanel`.
``toggleShell`` Toggles a :class:`.ShellPanel`. ``toggleClassificationPanel`` Toggles a
=========================== =========================================== :class:`.MelodicClassificationPanel`.
``toggleShell`` Toggles a :class:`.ShellPanel`.
============================== ===========================================
A couple of other actions are also provided, with the same names as their A couple of other actions are also provided, with the same names as their
...@@ -246,29 +248,32 @@ class CanvasPanel(viewpanel.ViewPanel): ...@@ -246,29 +248,32 @@ class CanvasPanel(viewpanel.ViewPanel):
extraActions = {} extraActions = {}
actionz = [ actionz = [
('screenshot', self.screenshot), ('screenshot', self.screenshot),
('showCommandLineArgs', self.showCommandLineArgs), ('showCommandLineArgs', self.showCommandLineArgs),
('toggleOverlayList', lambda *a: self.togglePanel( ('toggleOverlayList', lambda *a: self.togglePanel(
overlaylistpanel.OverlayListPanel, overlaylistpanel.OverlayListPanel,
location=wx.BOTTOM)), location=wx.BOTTOM)),
('toggleOverlayInfo', lambda *a: self.togglePanel( ('toggleOverlayInfo', lambda *a: self.togglePanel(
overlayinfopanel.OverlayInfoPanel, overlayinfopanel.OverlayInfoPanel,
location=wx.RIGHT)), location=wx.LEFT)),
('toggleAtlasPanel', lambda *a: self.togglePanel( ('toggleAtlasPanel', lambda *a: self.togglePanel(
atlaspanel.AtlasPanel, atlaspanel.AtlasPanel,
location=wx.BOTTOM)), location=wx.BOTTOM)),
('toggleDisplayProperties', lambda *a: self.togglePanel( ('toggleDisplayProperties', lambda *a: self.togglePanel(
overlaydisplaytoolbar.OverlayDisplayToolBar, overlaydisplaytoolbar.OverlayDisplayToolBar,
viewPanel=self)), viewPanel=self)),
('toggleLocationPanel', lambda *a: self.togglePanel( ('toggleLocationPanel', lambda *a: self.togglePanel(
locationpanel.LocationPanel, locationpanel.LocationPanel,
location=wx.BOTTOM)), location=wx.BOTTOM)),
('toggleClusterPanel', lambda *a: self.togglePanel( ('toggleClusterPanel', lambda *a: self.togglePanel(
clusterpanel.ClusterPanel, clusterpanel.ClusterPanel,
location=wx.TOP)), location=wx.TOP)),
('toggleLookupTablePanel', lambda *a: self.togglePanel( ('toggleLookupTablePanel', lambda *a: self.togglePanel(
lookuptablepanel.LookupTablePanel, lookuptablepanel.LookupTablePanel,
location=wx.TOP))] location=wx.TOP)),
('toggleClassificationPanel', lambda *a: self.togglePanel(
melclasspanel.MelodicClassificationPanel,
location=wx.RIGHT))]
actionz += extraActions.items() actionz += extraActions.items()
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment