8000 Merge pull request #1350 from gjpcbecq/correct_viewers · nipy/nibabel@f51dced · GitHub
[go: up one dir, main page]

Skip to content

Commit f51dced

Browse files
authored
Merge pull request #1350 from gjpcbecq/correct_viewers
BF: `_on_mouse` and `_on_scroll` in viewers for non RAS matrices
2 parents 8c962e2 + 5d89d2f commit f51dced

File tree

2 files changed

+213
-10
lines changed
  • nibabel
    • tests

2 files changed

+213
-10
lines changed

nibabel/tests/test_viewers.py

Lines changed: 195 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,3 +134,198 @@ def test_viewer_nonRAS():
134134
assert_array_equal(sag, data1[6, :, :])
135135
assert_array_equal(cor, data1[:, :, 32].T)
136136
assert_array_equal(axi, data1[:, 13, :].T)
137+
138+
139+
@needs_mpl
140+
def test_viewer_nonRAS_on_mouse():
141+
"""
142+
test on_mouse selection on non RAS matrices
143+
144+
"""
145+
# This affine simulates an acquisition on a quadruped subject that is in a prone position.
146+
# This corresponds to an acquisition with:
147+
# - LR inverted on scanner x (i)
148+
# - IS on scanner y (j)
149+
# - PA on scanner z (k)
150+
# This example enables to test also OrthoSlicer3D properties `_flips` and `_order`.
151+
152+
(I, J, K) = (10, 20, 40)
153+
data1 = np.random.rand(I, J, K)
154+
(i_target, j_target, k_target) = (2, 14, 12)
155+
i1 = i_target - 2
156+
i2 = i_target + 2
157+
j1 = j_target - 3
158+
j2 = j_target + 3
159+
k1 = k_target - 4
160+
k2 = k_target + 4
161+
data1[i1 : i2 + 1, j1 : j2 + 1, k1 : k2 + 1] = 0
162+
data1[i_target, j_target, k_target] = 1
163+
valp1 = 1.5
164+
valm1 = 0.5
165+
data1[i_target - 1, j_target, k_target] = valp1 # x flipped
166+
data1[i_target + 1, j_target, k_target] = valm1 # x flipped
167+
data1[i_target, j_target - 1, k_target] = valm1
168+
data1[i_target, j_target + 1, k_target] = valp1
169+
data1[i_target, j_target, k_target - 1] = valm1
170+
data1[i_target, j_target, k_target + 1] = valp1
171+
172+
aff1 = np.array([[-1, 0, 0, 5], [0, 0, 1, -10], [0, 1, 0, -30], [0, 0, 0, 1]])
173+
174+
o1 = OrthoSlicer3D(data1, aff1)
175+
176+
class Event:
177+
def __init__(self):
178+
self.name = 'simulated mouse event'
179+
self.button = 1
180+
181+
event = Event()
182+
event.xdata = k_target
183+
event.ydata = j_target
184+
event.inaxes = o1._ims[0].axes
185+
o1._on_mouse(event)
186+
187+
event.inaxes = o1._ims[1].axes
188+
event.xdata = (I - 1) - i_target # x flipped
189+
event.ydata = j_target
190+
o1._on_mouse(event)
191+
192+
event.inaxes = o1._ims[2].axes
193+
event.xdata = (I - 1) - i_target # x flipped
194+
event.ydata = k_target
195+
o1._on_mouse(event)
196+
197+
sag = o1._ims[0].get_array()
198+
cor = o1._ims[1].get_array()
199+
axi = o1._ims[2].get_array()
200+
201+
assert_array_equal(sag, data1[i_target, :, :]) #
202+
assert_array_equal(cor, data1[::-1, :, k_target].T) # x flipped
203+
assert_array_equal(axi, data1[::-1, j_target, :].T) # x flipped
204+
return None
205+
206+
207+
@needs_mpl
208+
def test_viewer_nonRAS_on_scroll():
209+
"""
210+
test scrolling on non RAS matrices
211+
212+
"""
213+
# This affine simulates an acquisition on a quadruped subject that is in a prone position.
214+
# This corresponds to an acquisition with:
215+
# - LR inverted on scanner x (i)
216+
# - IS on scanner y (j)
217+
# - PA on scanner z (k)
218+
# This example enables to test also OrthoSlicer3D properties `_flips` and `_order`.
219+
220+
(I, J, K) = (10, 20, 40)
221+
data1 = np.random.rand(I, J, K)
222+
(i_target, j_target, k_target) = (2, 14, 12)
223+
i1 = i_target - 2
224+
i2 = i_target + 2
225+
j1 = j_target - 3
226+
j2 = j_target + 3
227+
k1 = k_target - 4
228+
k2 = k_target + 4
229+
data1[i1 : i2 + 1, j1 : j2 + 1, k1 : k2 + 1] = 0
230+
data1[i_target, j_target, k_target] = 1
231+
valp1 = 1.5
232+
valm1 = 0.5
233+
data1[i_target - 1, j_target, k_target] = valp1 # x flipped
234+
data1[i_target + 1, j_target, k_target] = valm1 # x flipped
235+
data1[i_target, j_target - 1, k_target] = valm1
236+
data1[i_target, j_target + 1, k_target] = valp1
237+
data1[i_target, j_target, k_target - 1] = valm1
238+
data1[i_target, j_target, k_target + 1] = valp1
239+
240+
aff1 = np.array([[-1, 0, 0, 5], [0, 0, 1, -10], [0, 1, 0, -30], [0, 0, 0, 1]])
241+
242+
o1 = OrthoSlicer3D(data1, aff1)
243+
244+
class Event:
245+
def __init__(self):
246+
self.name = 'simulated mouse event'
247+
self.button = None
248+
self.key = None
249+
250+
[x_t, y_t, z_t] = list(aff1.dot(np.array([i_target, j_target, k_target, 1]))[:3])
251+
# print(x_t, y_t, z_t)
252+
# scanner positions are x_t=3, y_t=2, z_t=16
253+
254+
event = Event()
255+
256+
# Sagittal plane - one scroll up
257+
# x coordinate is flipped so index decrease by 1
258+
o1.set_position(x_t, y_t, z_t)
259+
event.inaxes = o1._ims[0].axes
260+
event.button = 'up'
261+
o1._on_scroll(event)
262+
sag = o1._ims[0].get_array()
263+
cor = o1._ims[1].get_array()
264+
axi = o1._ims[2].get_array()
265+
assert_array_equal(sag, data1[i_target - 1, :, :])
266+
assert_array_equal(cor, data1[::-1, :, k_target].T) # ::-1 because the array is flipped in x
267+
assert_array_equal(axi, data1[::-1, j_target, :].T) # ::-1 because the array is flipped in x
268+
269+
# Sagittal plane - one scrolled down
270+
o1.set_position(x_t, y_t, z_t)
271+
event.button = 'down'
272+
o1._on_scroll(event)
273+
sag = o1._ims[0].get_array()
274+
cor = o1._ims[1].get_array()
275+
axi = o1._ims[2].get_array()
276+
assert_array_equal(sag, data1[i_target + 1, :, :])
277+
assert_array_equal(cor, data1[::-1, :, k_target].T)
278+
assert_array_equal(axi, data1[::-1, j_target, :].T)
279+
280+
# Coronal plane - one scroll up
281+
# y coordinate is increase by 1
282+
o1.set_position(x_t, y_t, z_t)
283+
event.inaxes = o1._ims[1].axes
284+
event.button = 'up'
285+
o1._on_scroll(event)
286+
sag = o1._ims[0].get_array()
287+
cor = o1._ims[1].get_array()
288+
axi = o1._ims[2].get_array()
289+
assert_array_equal(sag, data1[i_target, :, :])
290+
assert_array_equal(
291+
cor, data1[::-1, :, k_target + 1].T
292+
) # ::-1 because the array is flipped in x
293+
assert_array_equal(axi, data1[::-1, j_target, :].T) # ::-1 because the array is flipped in x
294+
295+
# Coronal plane - one scrolled down
296+
o1.set_position(x_t, y_t, z_t)
297+
event.button = 'down'
298+
o1._on_scroll(event)
299+
sag = o1._ims[0].get_array()
300+
cor = o1._ims[1].get_array()
301+
axi = o1._ims[2].get_array()
302+
assert_array_equal(sag, data1[i_target, :, :])
303+
assert_array_equal(cor, data1[::-1, :, k_target - 1].T)
304+
assert_array_equal(axi, data1[::-1, j_target, :].T)
305+
306+
# Axial plane - one scroll up
307+
# y is increase by 1
308+
o1.set_position(x_t, y_t, z_t)
309+
event.inaxes = o1._ims[2].axes
310+
event.button = 'up'
311+
o1._on_scroll(event)
312+
sag = o1._ims[0].get_array()
313+
cor = o1._ims[1].get_array()
314+
axi = o1._ims[2].get_array()
315+
assert_array_equal(sag, data1[i_target, :, :])
316+
assert_array_equal(cor, data1[::-1, :, k_target].T) # ::-1 because the array is flipped in x
317+
assert_array_equal(
318+
axi, data1[::-1, j_target + 1, :].T
319+
) # ::-1 because the array is flipped in x
320+
321+
# Axial plane - one scrolled down
322+
o1.set_position(x_t, y_t, z_t)
323+
event.button = 'down'
324+
o1._on_scroll(event)
325+
sag = o1._ims[0].get_array()
326+
cor = o1._ims[1].get_array()
327+
axi = o1._ims[2].get_array()
328+
assert_array_equal(sag, data1[i_target, :, :])
329+
assert_array_equal(cor, data1[::-1, :, k_target].T)
330+
assert_array_equal(axi, data1[::-1, j_target - 1, :].T)
331+
return None

nibabel/viewers.py

Lines changed: 18 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -103,15 +103,15 @@ def __init__(self, data, affine=None, axes=None, title=None):
103103
# | | | |
104104
# | | | |
105105
# +---------+ +---------+
106-
# A --> <-- R
106+
# A --> R -->
107107
# ^ +---------+ +---------+
108108
# | | | | |
109109
# | Axial | | Vol |
110110
# A | 2 | | 3 |
111111
# | | | |
112112
# | | | |
113113
# +---------+ +---------+
114-
# <-- R <-- t -->
114+
# R --> <-- t -->
115115

116116
fig, axes = plt.subplots(2, 2)
117117
fig.set_size_inches((8, 8), forward=True)
@@ -419,7 +419,7 @@ def _set_position(self, x, y, z, notify=True):
419419
# deal with crosshairs
420420
loc = self._data_idx[ii]
421421
if self._flips[ii]:
422-
loc = self._sizes[ii] - loc
422+
loc = self._sizes[ii] - 1 - loc
423423
loc = [loc] * 2
424424
if ii == 0:
425425
self._crosshairs[2]['vert'].set_xdata(loc)
@@ -468,12 +468,17 @@ def _on_scroll(self, event):
468468
dv *= 1.0 if event.button == 'up' else -1.0
469469
dv *= -1 if self._flips[ii] else 1
470470
val = self._data_idx[ii] + dv
471+
471472
if ii == 3:
472473
self._set_volume_index(val)
473474
else:
474-
coords = [self._data_idx[k] for k in range(3)] + [1.0]
475+
coords = [self._data_idx[k] for k in range(3)]
475476
coords[ii] = val
476-
self._set_position(*np.dot(self._affine, coords)[:3])
477+
coords_ordered = [0, 0, 0, 1]
478+
for k in range(3):
479+
coords_ordered[self._order[k]] = coords[k]
480+
position = np.dot(self._affine, coords_ordered)[:3]
481+
self._set_position(*position)
477482
self._draw()
478483

479484
def _on_mouse(self, event):
@@ -488,15 +493,18 @@ def _on_mouse(self, event):
488493
self._set_volume_index(event.xdata)
489494
else:
490495
# translate click xdata/ydata to physical position
491-
xax, yax = [[1, 2], [0, 2], [0, 1]][ii]
496+
xax, yax = [
497+
[self._order[1], self._order[2]],
498+
[self._order[0], self._order[2]],
499+
[self._order[0], self._order[1]],
500+
][ii]
492501
x, y = event.xdata, event.ydata
493-
x = self._sizes[xax] - x if self._flips[xax] else x
494-
y = self._sizes[yax] - y if self._flips[yax] else y
502+
x = self._sizes[xax] - x - 1 if self._flips[xax] else x
503+
y = self._sizes[yax] - y - 1 if self._flips[yax] else y
495504
idxs = np.ones(4)
496505
idxs[xax] = x
497506
idxs[yax] = y
498-
idxs[ii] = self._data_idx[ii]
499-
idxs[:3] = idxs[self._order]
507+
idxs[self._order[ii]] = self._data_idx[ii]
500508
self._set_position(*np.dot(self._affine, idxs)[:3])
501509
self._draw()
502510

0 commit comments

Comments
 (0)
0