Skip to content

NF: add version of Paul Ivanov's slice viewer #251

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

Closed
wants to merge 20 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
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ dist/
.shelf
.tox/
.coverage
cover/

# Logs and databases #
######################
Expand Down
8 changes: 7 additions & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
# munges each line before executing it to print out the exit status. It's okay
# for it to be on multiple physical lines, so long as you remember: - There
# can't be any leading "-"s - All newlines will be removed, so use ";"s

language: python
env:
global:
Expand Down Expand Up @@ -44,7 +45,12 @@ before_install:
- if [[ $DEPENDS == *h5py* ]]; then
sudo apt-get install libhdf5-serial-dev;
fi
# command to install dependencies
# Create a (fake) display on Travis so that mpl tests work
# TODO: Could also use the `agg` backend instead
- if [[ $DEPENDS == "*matplotlib*" ]]; then
export DISPLAY=:99.0;
/sbin/start-stop-daemon --start --quiet --pidfile /tmp/custom_xvfb_99.pid --make-pidfile --background --exec /usr/bin/Xvfb -- :99 -screen 0 1400x900x24 -ac +extension GLX +render -noreset;
fi;
install:
- python setup.py install
# Point to nibabel data directory
Expand Down
1 change: 1 addition & 0 deletions nibabel/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@
from .imageclasses import class_map, ext_map
from . import trackvis
from . import mriutils
from . import viewers

# be friendly on systems with ancient numpy -- no tests, but at least
# importable
Expand Down
18 changes: 18 additions & 0 deletions nibabel/spatialimages.py
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,7 @@

from .filename_parser import types_filenames, TypesFilenamesError
from .fileholders import FileHolder
from .viewers import OrthoSlicer3D
from .volumeutils import shape_zoom_affine


Expand Down Expand Up @@ -744,3 +745,20 @@ def __getitem__(self):
raise TypeError("Cannot slice image objects; consider slicing image "
"array data with `img.dataobj[slice]` or "
"`img.get_data()[slice]`")

def plot(self):
"""Plot the image using OrthoSlicer3D

Returns
-------
viewer : instance of OrthoSlicer3D
The viewer.

Notes
-----
This requires matplotlib. If a non-interactive backend is used,
consider using viewer.show() (equivalently plt.show()) to show
the figure.
"""
return OrthoSlicer3D(self.get_data(), self.get_affine(),
title=self.get_filename())
73 changes: 73 additions & 0 deletions nibabel/tests/test_viewers.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
# emacs: -*- mode: python-mode; py-indent-offset: 4; indent-tabs-mode: nil -*-
# vi: set ft=python sts=4 ts=4 sw=4 et:
### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ##
#
# See COPYING file distributed along with the NiBabel package for the
# copyright and license terms.
#
### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ##

import numpy as np
from collections import namedtuple as nt


from ..optpkg import optional_package
from ..viewers import OrthoSlicer3D

from numpy.testing.decorators import skipif
from numpy.testing import assert_array_equal

from nose.tools import assert_raises, assert_true

matplotlib, has_mpl = optional_package('matplotlib')[:2]
needs_mpl = skipif(not has_mpl, 'These tests need matplotlib')
if has_mpl:
matplotlib.use('Agg')


@needs_mpl
def test_viewer():
# Test viewer
plt = optional_package('matplotlib.pyplot')[0]
a = np.sin(np.linspace(0, np.pi, 20))
b = np.sin(np.linspace(0, np.pi*5, 30))
data = (np.outer(a, b)[..., np.newaxis] * a)[:, :, :, np.newaxis]
data = data * np.array([1., 2.]) # give it a # of volumes > 1
v = OrthoSlicer3D(data)
assert_array_equal(v.position, (0, 0, 0))
assert_true('OrthoSlicer3D' in repr(v))

# fake some events, inside and outside axes
v._on_scroll(nt('event', 'button inaxes key')('up', None, None))
for ax in (v._axes[0], v._axes[3]):
v._on_scroll(nt('event', 'button inaxes key')('up', ax, None))
v._on_scroll(nt('event', 'button inaxes key')('up', ax, 'shift'))
# "click" outside axes, then once in each axis, then move without click
v._on_mouse(nt('event', 'xdata ydata inaxes button')(0.5, 0.5, None, 1))
for ax in v._axes:
v._on_mouse(nt('event', 'xdata ydata inaxes button')(0.5, 0.5, ax, 1))
v._on_mouse(nt('event', 'xdata ydata inaxes button')(0.5, 0.5, None, None))
v.set_volume_idx(1)
v.set_volume_idx(1) # should just pass
v.close()
v._draw() # should be safe

# non-multi-volume
v = OrthoSlicer3D(data[:, :, :, 0])
v._on_scroll(nt('event', 'button inaxes key')('up', v._axes[0], 'shift'))
v._on_keypress(nt('event', 'key')('escape'))

# other cases
fig, axes = plt.subplots(1, 4)
plt.close(fig)
v1 = OrthoSlicer3D(data, pcnt_range=[0.1, 0.9], axes=axes)
aff = np.array([[0, 1, 0, 3], [-1, 0, 0, 2], [0, 0, 2, 1], [0, 0, 0, 1]],
float)
v2 = OrthoSlicer3D(data, affine=aff, axes=axes[:3])
assert_raises(ValueError, OrthoSlicer3D, data[:, :, 0, 0])
assert_raises(ValueError, OrthoSlicer3D, data, affine=np.eye(3))
assert_raises(TypeError, v2.link_to, 1)
v2.link_to(v1)
v2.link_to(v1) # shouldn't do anything
v1.close()
v2.close()
Loading