Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

BF: Correct viewers _on_mouse, _on_scroll for non RAS matrices #1349

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
200 changes: 200 additions & 0 deletions nibabel/tests/test_viewers.py
Original file line number Diff line number Diff line change
Expand Up @@ -134,3 +134,203 @@ def test_viewer_nonRAS():
assert_array_equal(sag, data1[6, :, :])
assert_array_equal(cor, data1[:, :, 32].T)
assert_array_equal(axi, data1[:, 13, :].T)



@needs_mpl
def test_viewer_nonRAS_on_mouse():
"""
test on_mouse selection on non RAS matrices

"""
# This affine simulates an acquisition on a quadruped subject that is in a prone position.
# This corresponds to an acquisition with:
# - LR inverted on scanner x (i)
# - IS on scanner y (j)
# - PA on scanner z (k)
# This example enables to test also OrthoSlicer3D properties `_flips` and `_order`.

(I, J, K) = (10, 20, 40)
data1 = np.random.rand(I, J, K)
(i_target, j_target, k_target) = (2, 14, 12)
i1 = i_target - 2
i2 = i_target + 2
j1 = j_target - 3
j2 = j_target + 3
k1 = k_target - 4
k2 = k_target + 4
data1[i1: i2 + 1, j1: j2 + 1, k1: k2 + 1] = 0
data1[i_target, j_target, k_target] = 1
valp1 = 1.5
valm1 = 0.5
data1[i_target - 1, j_target, k_target] = valp1 # x flipped
data1[i_target + 1, j_target, k_target] = valm1 # x flipped
data1[i_target, j_target - 1, k_target] = valm1
data1[i_target, j_target + 1, k_target] = valp1
data1[i_target, j_target, k_target - 1] = valm1
data1[i_target, j_target, k_target + 1] = valp1

aff1 = np.array([[-1, 0, 0, 5],
[0, 0, 1, -10],
[0, 1, 0, -30],
[0, 0, 0, 1]])

o1 = OrthoSlicer3D(data1, aff1)

class Event:
def __init__(self):
self.name = "simulated mouse event"
self.button = 1

event = Event()
event.xdata = k_target
event.ydata = j_target
event.inaxes = o1._ims[0].axes
o1._on_mouse(event)

event.inaxes = o1._ims[1].axes
event.xdata = (I - 1) - i_target # x flipped
event.ydata = j_target
o1._on_mouse(event)

event.inaxes = o1._ims[2].axes
event.xdata = (I - 1) - i_target # x flipped
event.ydata = k_target
o1._on_mouse(event)

sag = o1._ims[0].get_array()
cor = o1._ims[1].get_array()
axi = o1._ims[2].get_array()

assert_array_equal(sag, data1[i_target, :, :]) #
assert_array_equal(cor, data1[::-1, :, k_target].T) # x flipped
assert_array_equal(axi, data1[::-1, j_target, :].T) # x flipped
return None


@needs_mpl
def test_viewer_nonRAS_on_scroll():
"""
test scrolling on non RAS matrices

"""
# This affine simulates an acquisition on a quadruped subject that is in a prone position.
# This corresponds to an acquisition with:
# - LR inverted on scanner x (i)
# - IS on scanner y (j)
# - PA on scanner z (k)
# This example enables to test also OrthoSlicer3D properties `_flips` and `_order`.

(I, J, K) = (10, 20, 40)
data1 = np.random.rand(I, J, K)
(i_target, j_target, k_target) = (2, 14, 12)
i1 = i_target - 2
i2 = i_target + 2
j1 = j_target - 3
j2 = j_target + 3
k1 = k_target - 4
k2 = k_target + 4
data1[i1: i2 + 1, j1: j2 + 1, k1: k2 + 1] = 0
data1[i_target, j_target, k_target] = 1
valp1 = 1.5
valm1 = 0.5
data1[i_target - 1, j_target, k_target] = valp1 # x flipped
data1[i_target + 1, j_target, k_target] = valm1 # x flipped
data1[i_target, j_target - 1, k_target] = valm1
data1[i_target, j_target + 1, k_target] = valp1
data1[i_target, j_target, k_target - 1] = valm1
data1[i_target, j_target, k_target + 1] = valp1

aff1 = np.array([[-1, 0, 0, 5],
[0, 0, 1, -10],
[0, 1, 0, -30],
[0, 0, 0, 1]])

o1 = OrthoSlicer3D(data1, aff1)

class Event:
def __init__(self):
self.name = "simulated mouse event"
self.button = None
self.key = None

i_last = data1.shape[0] - 1

[x_t, y_t, z_t] = list(aff1.dot(np.array([i_target, j_target, k_target, 1]))[:3])
# print(x_t, y_t, z_t)
# scanner positions are x_t=3, y_t=2, z_t=16

event = Event()

# Sagittal plane - one scroll up
# x coordinate is flipped so index decrease by 1
o1.set_position(x_t, y_t, z_t)
event.inaxes = o1._ims[0].axes
event.button = 'up'
o1._on_scroll(event)
sag = o1._ims[0].get_array()
cor = o1._ims[1].get_array()
axi = o1._ims[2].get_array()
assert_array_equal(sag, data1[i_target - 1, :, :])
assert_array_equal(cor, data1[::-1, :, k_target].T) # ::-1 because the array is flipped in x
assert_array_equal(axi, data1[::-1, j_target, :].T) # ::-1 because the array is flipped in x

# Sagittal plane - one scrolled down
o1.set_position(x_t, y_t, z_t)
event.button = 'down'
o1._on_scroll(event)
sag = o1._ims[0].get_array()
cor = o1._ims[1].get_array()
axi = o1._ims[2].get_array()
assert_array_equal(sag, data1[i_target + 1, :, :])
assert_array_equal(cor, data1[::-1, :, k_target].T)
assert_array_equal(axi, data1[::-1, j_target, :].T)

# Coronal plane - one scroll up
# y coordinate is increase by 1
o1.set_position(x_t, y_t, z_t)
event.inaxes = o1._ims[1].axes
event.button = 'up'
o1._on_scroll(event)
sag = o1._ims[0].get_array()
cor = o1._ims[1].get_array()
axi = o1._ims[2].get_array()
assert_array_equal(sag, data1[i_target, :, :])
assert_array_equal(cor, data1[::-1, :, k_target + 1].T) # ::-1 because the array is flipped in x
assert_array_equal(axi, data1[::-1, j_target, :].T) # ::-1 because the array is flipped in x

# Coronal plane - one scrolled down
o1.set_position(x_t, y_t, z_t)
event.button = 'down'
o1._on_scroll(event)
sag = o1._ims[0].get_array()
cor = o1._ims[1].get_array()
axi = o1._ims[2].get_array()
assert_array_equal(sag, data1[i_target, :, :])
assert_array_equal(cor, data1[::-1, :, k_target - 1].T)
assert_array_equal(axi, data1[::-1, j_target, :].T)

# Axial plane - one scroll up
# y is increase by 1
o1.set_position(x_t, y_t, z_t)
event.inaxes = o1._ims[2].axes
event.button = 'up'
o1._on_scroll(event)
sag = o1._ims[0].get_array()
cor = o1._ims[1].get_array()
axi = o1._ims[2].get_array()
assert_array_equal(sag, data1[i_target, :, :])
assert_array_equal(cor, data1[::-1, :, k_target].T) # ::-1 because the array is flipped in x
assert_array_equal(axi, data1[::-1, j_target + 1, :].T) # ::-1 because the array is flipped in x

# Axial plane - one scrolled down
o1.set_position(x_t, y_t, z_t)
event.button = 'down'
o1._on_scroll(event)
sag = o1._ims[0].get_array()
cor = o1._ims[1].get_array()
axi = o1._ims[2].get_array()
assert_array_equal(sag, data1[i_target, :, :])
assert_array_equal(cor, data1[::-1, :, k_target].T)
assert_array_equal(axi, data1[::-1, j_target - 1, :].T)
return None
30 changes: 18 additions & 12 deletions nibabel/viewers.py
Original file line number Diff line number Diff line change
Expand Up @@ -103,15 +103,15 @@ def __init__(self, data, affine=None, axes=None, title=None):
# | | | |
# | | | |
# +---------+ +---------+
# A --> <-- R
# A --> R -->
# ^ +---------+ +---------+
# | | | | |
# | Axial | | Vol |
# A | 2 | | 3 |
# | | | |
# | | | |
# +---------+ +---------+
# <-- R <-- t -->
# R --> <-- t -->

fig, axes = plt.subplots(2, 2)
fig.set_size_inches((8, 8), forward=True)
Expand Down Expand Up @@ -419,7 +419,7 @@ def _set_position(self, x, y, z, notify=True):
# deal with crosshairs
loc = self._data_idx[ii]
if self._flips[ii]:
loc = self._sizes[ii] - loc
loc = self._sizes[ii] - 1 - loc
loc = [loc] * 2
if ii == 0:
self._crosshairs[2]['vert'].set_xdata(loc)
Expand Down Expand Up @@ -468,12 +468,17 @@ def _on_scroll(self, event):
dv *= 1.0 if event.button == 'up' else -1.0
dv *= -1 if self._flips[ii] else 1
val = self._data_idx[ii] + dv

if ii == 3:
self._set_volume_index(val)
else:
coords = [self._data_idx[k] for k in range(3)] + [1.0]
coords = [self._data_idx[k] for k in range(3)]
coords[ii] = val
self._set_position(*np.dot(self._affine, coords)[:3])
coords_ordered = [0, 0, 0, 1]
for k in range(3):
coords_ordered[self._order[k]] = coords[k]
position = np.dot(self._affine, coords_ordered)[:3]
self._set_position(*position)
self._draw()

def _on_mouse(self, event):
Expand All @@ -488,18 +493,19 @@ def _on_mouse(self, event):
self._set_volume_index(event.xdata)
else:
# translate click xdata/ydata to physical position
xax, yax = [[1, 2], [0, 2], [0, 1]][ii]
xax, yax = [[self._order[1], self._order[2]],
[self._order[0], self._order[2]],
[self._order[0], self._order[1]]][ii]
x, y = event.xdata, event.ydata
x = self._sizes[xax] - x if self._flips[xax] else x
y = self._sizes[yax] - y if self._flips[yax] else y
x = self._sizes[xax] - x - 1 if self._flips[xax] else x
y = self._sizes[yax] - y - 1 if self._flips[yax] else y
idxs = np.ones(4)
idxs[xax] = x
idxs[yax] = y
idxs[ii] = self._data_idx[ii]
idxs[:3] = idxs[self._order]
self._set_position(*np.dot(self._affine, idxs)[:3])
idxs[self._order[ii]] = self._data_idx[ii]
self._set_position(*np.dot(self._affine, idxs)[:3])
self._draw()

def _on_keypress(self, event):
"""Handle mpl keypress events"""
if event.key is not None and 'escape' in event.key:
Expand Down
Loading