Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit f5916f8

Browse files
NF: Adds overlay option in OrthoSlicer3D
Beginning of functionality to allow overlays in OrthoSlicer3D
1 parent fcc5448 commit f5916f8

File tree

1 file changed

+109
-1
lines changed

1 file changed

+109
-1
lines changed

‎nibabel/viewers.py‎

Lines changed: 109 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,9 @@ def __init__(self, data, affine=None, axes=None, title=None):
6969
self._title = title
7070
self._closed = False
7171
self._cross = True
72+
self._overlay = None
73+
self._threshold = None
74+
self._alpha = 1
7275

7376
data = np.asanyarray(data)
7477
if data.ndim < 3:
@@ -286,6 +289,111 @@ def clim(self, clim):
286289
self._clim = tuple(clim)
287290
self.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+
289397
def 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):
413521
idx = [slice(None)] * len(self._axes)
414522
for ii in range(3):
415523
idx[self._order[ii]] = self._data_idx[ii]
416-
vdata = self._data[tuple(idx)].ravel()
524+
vdata = np.asarray(self._data[tuple(idx)].ravel())
417525
vdata = np.concatenate((vdata, [vdata[-1]]))
418526
self._volume_ax_objs['patch'].set_x(self._data_idx[3] - 0.5)
419527
self._volume_ax_objs['step'].set_ydata(vdata)

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /