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

Functional histogram list, allows add/remove histogram plots. Histograms

are not recalculated if they've already been calculated.
parent 012016c3
No related branches found
No related tags found
No related merge requests found
......@@ -112,12 +112,12 @@ titles = TypeDict({
'OrthoSettingsPanel' : 'Ortho view settings',
'LightBoxToolBar' : 'Lightbox view toolbar',
'LightBoxSettingsPanel' : 'Lightbox view settings',
'HistogramToolBar' : 'Histogram settings',
'LookupTablePanel' : 'Lookup tables',
'LutLabelDialog' : 'New LUT label',
'NewLutDialog' : 'New LUT',
'TimeSeriesListPanel' : 'Time series list',
'TimeSeriesControlPanel' : 'Time series control',
'HistogramListPanel' : 'Histogram list',
'LookupTablePanel.loadLut' : 'Select a lookup table file',
'LookupTablePanel.labelExists' : 'Label already exists',
......@@ -149,13 +149,10 @@ actions = TypeDict({
'LightBoxPanel.toggleLightBoxToolBar' : 'View properties',
'PlotPanel.screenshot' : 'Take screenshot',
'TimeSeriesPanel.toggleTimeSeriesList' : 'Time series list',
'TimeSeriesPanel.toggleTimeSeriesControl' : 'Time series control',
'HistogramPanel.toggleToolbar' : 'Histogram controls',
'HistogramPanel.toggleHistogramList' : 'Histogram list',
'OrthoViewProfile.centreCursor' : 'Centre cursor',
'OrthoViewProfile.resetZoom' : 'Reset zoom',
......
......@@ -15,6 +15,7 @@ from orthosettingspanel import OrthoSettingsPanel
from lookuptablepanel import LookupTablePanel
from timeserieslistpanel import TimeSeriesListPanel
from timeseriescontrolpanel import TimeSeriesControlPanel
from histogramlistpanel import HistogramListPanel
from orthotoolbar import OrthoToolBar
from orthoprofiletoolbar import OrthoProfileToolBar
......
#!/usr/bin/env python
#
# histogramlistpanel.py -
#
# Author: Paul McCarthy <pauldmccarthy@gmail.com>
#
import wx
import pwidgets.elistbox as elistbox
import fsl.fslview.panel as fslpanel
import fsl.fslview.colourmaps as fslcm
import timeserieslistpanel
class HistogramListPanel(fslpanel.FSLViewPanel):
def __init__(self, parent, overlayList, displayCtx, histPanel):
fslpanel.FSLViewPanel.__init__(self, parent, overlayList, displayCtx)
self.__hsPanel = histPanel
self.__hsList = elistbox.EditableListBox(
self, style=(elistbox.ELB_NO_MOVE |
elistbox.ELB_EDITABLE))
self.__sizer = wx.BoxSizer(wx.VERTICAL)
self.SetSizer(self.__sizer)
self.__sizer.Add(self.__hsList, flag=wx.EXPAND, proportion=1)
self.__hsList.Bind(elistbox.EVT_ELB_ADD_EVENT, self.__onListAdd)
self.__hsList.Bind(elistbox.EVT_ELB_REMOVE_EVENT, self.__onListRemove)
self.__hsList.Bind(elistbox.EVT_ELB_EDIT_EVENT, self.__onListEdit)
self.__hsList.Bind(elistbox.EVT_ELB_SELECT_EVENT, self.__onListSelect)
self.__hsPanel.addListener('dataSeries',
self._name,
self.__histSeriesChanged)
self.__histSeriesChanged()
self.Layout()
def destroy(self):
fslpanel.FSLViewPanel.destroy(self)
self.__hsPanel.removeListener('dataSeries', self._name)
def __histSeriesChanged(self, *a):
self.__hsList.Clear()
for hs in self.__hsPanel.dataSeries:
widg = timeserieslistpanel.TimeSeriesWidget(self, hs)
self.__hsList.Append(hs.overlay.name,
clientData=hs,
extraWidget=widg)
def __onListAdd(self, ev):
hs = self.__hsPanel.getCurrent()
if hs is None:
return
hs.alpha = 1
hs.lineWidth = 2
hs.lineStyle = '-'
hs.colour = fslcm.randomColour()
hs.label = hs.overlay.name
self.__hsPanel.dataSeries.append(hs)
def __onListEdit(self, ev):
ev.data.label = ev.label
def __onListSelect(self, ev):
overlay = ev.data.overlay
self._displayCtx.selectedOverlay = self._overlayList.index(overlay)
def __onListRemove(self, ev):
self.__hsPanel.dataSeries.remove(ev.data)
......@@ -15,7 +15,7 @@ import props
import fsl.data.image as fslimage
import fsl.data.strings as strings
import fsl.fslview.colourmaps as fslcm
import fsl.fslview.controls as fslcontrols
import plotpanel
......@@ -50,26 +50,6 @@ def autoBin(data, dataRange):
return nbins
#
# Ideas:
#
# - Plot histogram for multiple images (how to select them?)
#
# - Ability to apply a label mask image, and plot separate
# histograms for each label
#
# - Ability to put an overlay on the display, showing the
# voxels that are within the histogram range
#
# - For 4D images, add an option to plot the histogram for
# the current volume only, or for all volumes
#
# - For different image types (e.g. vector), add anoption
# to plot the histogram of calculated values, e.g.
# magnitude, or separate histogram lines for xyz
# components?
#
class HistogramSeries(plotpanel.DataSeries):
nbins = props.Int(minval=10, maxval=500, default=100, clamped=True)
......@@ -92,13 +72,13 @@ class HistogramSeries(plotpanel.DataSeries):
if overlay.is4DImage():
self.setConstraint('volume', 'maxval', overlay.shape[3] - 1)
self.__calcInitDataRange()
self.histPropsChanged()
hsPanel.addListener('autoBin', self.name, self.histPropsChanged)
self .addListener('nbins', self.name, self.histPropsChanged)
self .addListener('dataRange', self.name, self.histPropsChanged)
self.__calcInitDataRange()
self.histPropsChanged()
def __del__(self):
self.hsPanel.removeListener('autoBin', self.name)
......@@ -133,7 +113,13 @@ class HistogramSeries(plotpanel.DataSeries):
if self.ignoreZeros:
data = data[data != 0]
nvals = data.size
dataRange = self.dataRange.x
log.debug('Calculating histogram for overlay '
'{} (number of values {})'.format(
self.overlay.name,
nvals))
if self.hsPanel.autoBin: nbins = autoBin(data, dataRange)
else: nbins = self.nbins
......@@ -153,10 +139,18 @@ class HistogramSeries(plotpanel.DataSeries):
self.xdata = np.array(histX, dtype=np.float32)
self.ydata = np.array(histY, dtype=np.float32)
self.nvals = nvals
def getData(self):
return self.xdata, self.ydata
xdata = self.xdata
ydata = self.ydata
nvals = self.nvals
histType = self.hsPanel.histType
if histType == 'count': return xdata, ydata
elif histType == 'probability': return xdata, ydata / nvals
class HistogramPanel(plotpanel.PlotPanel):
......@@ -164,13 +158,15 @@ class HistogramPanel(plotpanel.PlotPanel):
autoBin = props.Boolean(default=True)
showCurrent = props.Boolean(default=True)
enableOverlay = props.Boolean(default=False)
histType = props.Choice(('probability', 'count'))
def __init__(self, parent, overlayList, displayCtx):
actionz = {}
actionz = {'toggleHistogramList' : lambda *a: self.togglePanel(
fslcontrols.HistogramListPanel, False, self)
}
plotpanel.PlotPanel.__init__(
self, parent, overlayList, displayCtx, actionz)
......@@ -185,13 +181,14 @@ class HistogramPanel(plotpanel.PlotPanel):
self._overlayList.addListener(
'overlays',
self._name,
self.__selectedOverlayChanged)
self.__updateCurrent)
self._displayCtx.addListener(
'selectedOverlay',
self._name,
self.__selectedOverlayChanged)
self.__updateCurrent)
self.addGlobalListener(self._name, self.__updateCurrent)
self.__selectedOverlayChanged()
self.__updateCurrent()
self.Layout()
......@@ -204,7 +201,8 @@ class HistogramPanel(plotpanel.PlotPanel):
self._displayCtx .removeListener('selectedOverlay', self._name)
def __calcCurrent(self):
def __updateCurrent(self, *a):
self.__current = None
if self._overlayList == 0:
......@@ -215,6 +213,9 @@ class HistogramPanel(plotpanel.PlotPanel):
if not isinstance(overlay, fslimage.Image):
return
if overlay in [hs.overlay for hs in self.dataSeries]:
return
hs = HistogramSeries(self, overlay)
hs.colour = [0.2, 0.2, 0.2]
hs.alpha = 1
......@@ -228,19 +229,9 @@ class HistogramPanel(plotpanel.PlotPanel):
def getCurrent(self):
return self.__current
def __selectedOverlayChanged(self, *a):
if len(self._overlayList) == 0:
return
self.draw()
def draw(self, *a):
self.__calcCurrent()
current = self.getCurrent()
if current is not None: self.drawDataSeries([current])
......
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