@@ -69,6 +69,9 @@ def __init__(self, data, affine=None, axes=None, title=None):
6969self ._title = title
7070self ._closed = False
7171self ._cross = True
72+ self ._overlay = None
73+ self ._threshold = None
74+ self ._alpha = 1
7275
7376data = np .asanyarray (data )
7477if data .ndim < 3 :
@@ -286,6 +289,111 @@ def clim(self, clim):
286289self ._clim = tuple (clim )
287290self .draw ()
288291
292+ @property
293+ def overlay (self ):
294+ """The current overlay """
295+ return self ._overlay
296+
297+ @property
298+ def threshold (self ):
299+ """The current data display threshold """
300+ return self ._threshold
301+
302+ @threshold .setter
303+ def threshold (self ,threshold ):
304+ # mask data array
305+ if threshold is not None :
306+ self ._data = np .ma .masked_array (np .asarray (self ._data ),
307+ np .asarray (self ._data )<= threshold )
308+ self ._threshold = float (threshold )
309+ else :
310+ self ._data = np .asarray (self ._data )
311+ self ._threshold = threshold
312+
313+ # update current volume data w/masked array and re-draw everything
314+ if self ._data .ndim > 3 :
315+ self ._current_vol_data = self ._data [...,self ._data_idx [3 ]]
316+ else :
317+ self ._current_vol_data = self ._data
318+ self ._set_position (None ,None ,None ,notify = False )
319+
320+ @property
321+ def alpha (self ):
322+ """ The current alpha (transparency) value """
323+ return self ._alpha
324+
325+ @alpha .setter
326+ def alpha (self ,alpha ):
327+ alpha = float (alpha )
328+ if alpha > 1 or alpha < 0 :
329+ raise ValueError ('alpha must be between 0 and 1' )
330+ for im in self ._ims :
331+ im .set_alpha (alpha )
332+ self ._alpha = alpha
333+ self .draw ()
334+
335+ def set_overlay (self ,data ,affine = None ,threshold = None ,cmap = 'viridis' ):
336+ if affine is None :
337+ try :# did we get an image?
338+ affine = data .affine
339+ data = data .dataobj
340+ except AttributeError :
341+ pass
342+
343+ # check that we have sufficient information to match the overlays
344+ if affine is None and data .shape [:3 ]!= self ._data .shape [:3 ]:
345+ raise ValueError ('Provided `data` do not match shape of '
346+ 'underlay and no `affine` matrix was '
347+ 'provided. Please provide an `affine` matrix '
348+ 'or resample first three dims of `data` to {}'
349+ .format (self ._data .shape [:3 ]))
350+
351+ # we need to resample the provided data to the already-plotted data
352+ if not np .allclose (affine ,self ._affine ):
353+ from .processing import resample_from_to
354+ from .nifti1 import Nifti1Image
355+ target_shape = self ._data .shape [:3 ]+ data .shape [3 :]
356+ # we can't just use SpatialImage because we need an image type
357+ # where the spatial axes are _always_ first
358+ data = resample_from_to (Nifti1Image (data ,affine ),
359+ (target_shape ,self ._affine )).dataobj
360+ affine = self ._affine
361+
362+ if self ._overlay is not None :
363+ # remove all images + cross hair lines
364+ for nn ,im in enumerate (self ._overlay ._ims ):
365+ im .remove ()
366+ for line in self ._overlay ._crosshairs [nn ].values ():
367+ line .remove ()
368+ # remove the fourth axis, if it was created for the overlay
369+ if (self ._overlay .n_volumes > 1 and len (self ._overlay ._axes )> 3
370+ and self .n_volumes == 1 ):
371+ a = self ._axes .pop (- 1 )
372+ a .remove ()
373+
374+ # create an axis if we have a 4D overlay (vs a 3D underlay)
375+ axes = self ._axes
376+ o_n_volumes = int (np .prod (data .shape [3 :]))
377+ if o_n_volumes > self .n_volumes :
378+ axes += [axes [0 ].figure .add_subplot (224 )]
379+ elif o_n_volumes < self .n_volumes :
380+ axes = axes [:- 1 ]
381+
382+ # mask array for provided threshold
383+ self ._overlay = self .__class__ (data ,affine = affine ,axes = axes )
384+ self ._overlay .threshold = threshold
385+
386+ # set transparency and new cmap
387+ self ._overlay .cmap = cmap
388+ for im in self ._overlay ._ims :
389+ im .set_alpha (0.7 )
390+
391+ # no double cross-hairs (they get confused when we have linked orthos)
392+ for cross in self ._overlay ._crosshairs :
393+ cross ['horiz' ].set_visible (False )
394+ cross ['vert' ].set_visible (False )
395+ self ._overlay ._draw ()
396+
289397def link_to (self ,other ):
290398"""Link positional changes between two canvases
291399
@@ -413,7 +521,7 @@ def _set_position(self, x, y, z, notify=True):
413521idx = [slice (None )]* len (self ._axes )
414522for ii in range (3 ):
415523idx [self ._order [ii ]]= self ._data_idx [ii ]
416- vdata = self ._data [tuple (idx )].ravel ()
524+ vdata = np . asarray ( self ._data [tuple (idx )].ravel () )
417525vdata = np .concatenate ((vdata , [vdata [- 1 ]]))
418526self ._volume_ax_objs ['patch' ].set_x (self ._data_idx [3 ]- 0.5 )
419527self ._volume_ax_objs ['step' ].set_ydata (vdata )