From b9715c92d065a7bdaa4bb5909e10558902c5546a Mon Sep 17 00:00:00 2001 From: Paul McCarthy <pauld.mccarthy@gmail.com> Date: Wed, 8 Jul 2015 14:49:57 +0100 Subject: [PATCH] 'Current' plot settings can be changed; Contrast vectors are scaled correctly --- fsl/data/featimage.py | 10 ++- fsl/data/strings.py | 9 ++- .../controls/timeseriescontrolpanel.py | 21 ++++++ fsl/fslview/views/timeseriespanel.py | 69 +++++++++++++++---- 4 files changed, 93 insertions(+), 16 deletions(-) diff --git a/fsl/data/featimage.py b/fsl/data/featimage.py index 19d267a57..77eeb1ee6 100644 --- a/fsl/data/featimage.py +++ b/fsl/data/featimage.py @@ -206,14 +206,18 @@ class FEATImage(fslimage.Image): return self.__copes[num] - def fit(self, contrast, xyz): + def fit(self, contrast, xyz, fullmodel=False): + + if not fullmodel: + contrast = np.array(contrast) + contrast /= np.sqrt(contrast.sum() ** 2) x, y, z = xyz numEVs = self.numEVs() if len(contrast) != numEVs: raise ValueError('Contrast is wrong length') - + X = self.__design data = self.data[x, y, z, :] modelfit = np.zeros(len(data)) @@ -221,7 +225,7 @@ class FEATImage(fslimage.Image): for i in range(numEVs): pe = self.getPE(i)[x, y, z] - modelfit += np.dot(X[:, i], pe) * contrast[i] + modelfit += X[:, i] * pe * contrast[i] return modelfit + data.mean() diff --git a/fsl/data/strings.py b/fsl/data/strings.py index f9daa0065..01a649224 100644 --- a/fsl/data/strings.py +++ b/fsl/data/strings.py @@ -213,11 +213,13 @@ labels = TypeDict({ 'PlotPanel.xlabel' : 'X', 'PlotPanel.ylabel' : 'Y', + 'TimeSeriesControlPanel.currentSettings' : 'Settings for current ' + 'voxel time course', 'TimeSeriesControlPanel.currentFEATSettings' : 'FEAT settings for ' 'selected overlay ({})', 'FEATModelFitTimeSeries.full' : 'Full model fit', - 'FEATModelFitTimeSeries.cope' : 'COPE{} fit ({})', + 'FEATModelFitTimeSeries.cope' : 'COPE{} fit: {}', 'FEATModelFitTimeSeries.pe' : 'PE{} fit', }) @@ -266,6 +268,11 @@ properties = TypeDict({ 'TimeSeriesPanel.demean' : 'Demean', 'TimeSeriesPanel.usePixdim' : 'Use pixdims', 'TimeSeriesPanel.showCurrent' : 'Plot time series for current voxel', + 'TimeSeriesPanel.currentColour' : 'Colour for current time course', + 'TimeSeriesPanel.currentAlpha' : 'Transparency for current ' + 'time course', + 'TimeSeriesPanel.currentLineWidth' : 'Line width for current time course', + 'TimeSeriesPanel.currentLineStyle' : 'Line style for current time course', 'TimeSeriesPanel.plotFullModelFit' : 'Plot full model fit', 'TimeSeriesPanel.plotResiduals' : 'Plot residuals', diff --git a/fsl/fslview/controls/timeseriescontrolpanel.py b/fsl/fslview/controls/timeseriescontrolpanel.py index 1e6159e04..461eefc96 100644 --- a/fsl/fslview/controls/timeseriescontrolpanel.py +++ b/fsl/fslview/controls/timeseriescontrolpanel.py @@ -30,6 +30,11 @@ class TimeSeriesControlPanel(fslpanel.FSLViewPanel): tsProps = ['demean', 'usePixdim', 'showCurrent'] + curProps = ['currentColour', + 'currentAlpha', + 'currentLineWidth', + 'currentLineStyle'] + plotProps = ['xLogScale', 'yLogScale', 'smooth', @@ -43,6 +48,22 @@ class TimeSeriesControlPanel(fslpanel.FSLViewPanel): props.makeWidget(self.__widgets, tsPanel, prop), displayName=strings.properties[tsPanel, prop]) + self.__widgets.AddGroup('currentSettings', + strings.labels[self, 'currentSettings']) + + # TODO Hide these when tsPanel.showCurrent is false + for prop in curProps: + if prop == 'currentAlpha': + kwargs = {'showLimits' : False, 'spin' : False} + else: + kwargs = {} + + self.__widgets.AddWidget( + props.makeWidget(self.__widgets, tsPanel, prop, **kwargs), + displayName=strings.properties[tsPanel, prop], + groupName='currentSettings') + + self.__widgets.AddGroup( 'plotSettings', strings.labels[tsPanel, 'plotSettings']) diff --git a/fsl/fslview/views/timeseriespanel.py b/fsl/fslview/views/timeseriespanel.py index 99dcc7267..0668476e1 100644 --- a/fsl/fslview/views/timeseriespanel.py +++ b/fsl/fslview/views/timeseriespanel.py @@ -12,6 +12,7 @@ of overlay objects stored in an :class:`.OverlayList`. """ +import copy import logging import numpy as np @@ -78,6 +79,9 @@ class FEATTimeSeries(TimeSeries): plotPEFits = props.List(props.Boolean(default=False)) plotCOPEFits = props.List(props.Boolean(default=False)) + # TODO 'None', or any PE/COPE + reduceDataAgainst = props.Choice() + def __init__(self, *args, **kwargs): TimeSeries.__init__(self, *args, **kwargs) @@ -226,8 +230,8 @@ class FEATTimeSeries(TimeSeries): if not TimeSeries.update(self, coords): return False - if self.__fullModelTs is not None: - self.__fullModelTs.update(coords) + for modelTs in self.getModelTimeSeries(): + modelTs.update(coords) return True @@ -254,10 +258,11 @@ class FEATModelFitTimeSeries(TimeSeries): def updateModelFit(self): - x, y, z = self.coords - self.data = self.overlay.fit(self.contrast, (x, y, z)) - + fitType = self.fitType + contrast = self.contrast + xyz = self.coords + self.data = self.overlay.fit(contrast, xyz, fitType == 'full') class TimeSeriesPanel(plotpanel.PlotPanel): @@ -269,13 +274,26 @@ class TimeSeriesPanel(plotpanel.PlotPanel): """ - demean = props.Boolean(default=True) - usePixdim = props.Boolean(default=False) - showCurrent = props.Boolean(default=True) + demean = props.Boolean(default=True) + usePixdim = props.Boolean(default=False) + showCurrent = props.Boolean(default=True) + + # TODO + percentChange = props.Boolean(default=False) + + currentColour = copy.copy(TimeSeries.colour) + currentAlpha = copy.copy(TimeSeries.alpha) + currentLineWidth = copy.copy(TimeSeries.lineWidth) + currentLineStyle = copy.copy(TimeSeries.lineStyle) def __init__(self, parent, overlayList, displayCtx): + self.currentColour = (0, 0, 0) + self.currentAlpha = 1 + self.currentLineWidth = 1 + self.currentLineStyle = ':' + actionz = { 'toggleTimeSeriesList' : lambda *a: self.togglePanel( fslcontrols.TimeSeriesListPanel, False, self), @@ -304,11 +322,33 @@ class TimeSeriesPanel(plotpanel.PlotPanel): self.addListener('usePixdim', self._name, self.draw) self.addListener('showCurrent', self._name, self.draw) + + csc = self.__currentSettingsChanged + self.addListener('currentColour', self._name, csc) + self.addListener('currentAlpha', self._name, csc) + self.addListener('currentLineWidth', self._name, csc) + self.addListener('currentLineStyle', self._name, csc) + self.__currentOverlay = None self.__currentTs = None self.Layout() self.draw() + + def __currentSettingsChanged(self, *a): + if self.__currentTs is None: + return + + tss = [self.__currentTs] + if isinstance(self.__currentTs, FEATTimeSeries): + tss.extend(self.__currentTs.getModelTimeSeries()) + + for ts in tss: + ts.colour = self.currentColour + ts.alpha = self.currentAlpha + ts.lineWidth = self.currentLineWidth + ts.lineStyle = self.currentLineStyle + def destroy(self): plotpanel.PlotPanel.destroy(self) @@ -378,12 +418,17 @@ class TimeSeriesPanel(plotpanel.PlotPanel): else: ts = TimeSeries(self, overlay, vox) - ts.colour = [0, 0, 0] - ts.alpha = 1 - ts.lineWidth = 2 - ts.lineStyle = ':' + ts.colour = self.currentColour + ts.alpha = self.currentAlpha + ts.lineWidth = self.currentLineWidth + ts.lineStyle = self.currentLineStyle ts.label = None + ts.bindProps('colour' , self, 'currentColour') + ts.bindProps('alpha' , self, 'currentAlpha') + ts.bindProps('lineWidth', self, 'currentLineWidth') + ts.bindProps('lineStyle', self, 'currentLineStyle') + self.__currentTs = ts self.__currentOverlay = overlay -- GitLab