Skip to content

Commit

Permalink
Merge pull request #1350 from gjpcbecq/correct_viewers
Browse files Browse the repository at this point in the history
BF: `_on_mouse` and `_on_scroll` in viewers for non RAS matrices
  • Loading branch information
effigies authored Sep 23, 2024
2 parents 8c962e2 + 5d89d2f commit f51dced
Show file tree
Hide file tree
Showing 2 changed files with 213 additions and 10 deletions.
195 changes: 195 additions & 0 deletions nibabel/tests/test_viewers.py
Original file line number Diff line number Diff line change
Expand Up @@ -134,3 +134,198 @@ 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

[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
28 changes: 18 additions & 10 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,15 +493,18 @@ 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]
idxs[self._order[ii]] = self._data_idx[ii]
self._set_position(*np.dot(self._affine, idxs)[:3])
self._draw()

Expand Down

0 comments on commit f51dced

Please sign in to comment.