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

bugfix resample only if type is not contour or contourf #2656

Merged
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
23 changes: 23 additions & 0 deletions nilearn/_utils/niimg.py
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,29 @@ def load_niimg(niimg, dtype=None):
return niimg


def _is_binary_niimg(niimg):
"""Returns whether a given niimg is binary or not.

Parameters
----------
niimg: Niimg-like object
See http://nilearn.github.io/manipulating_images/input_output.html
Image to test.

Returns
-------
is_binary: Boolean
True if binary, False otherwise.

"""
niimg = load_niimg(niimg)
data = _safe_get_data(niimg, ensure_finite=True)
unique_values = np.unique(data)
if len(unique_values) != 2:
return False
return sorted(list(unique_values)) == [0,1]


def copy_img(img):
"""Copy an image to a nibabel.Nifti1Image.

Expand Down
8 changes: 8 additions & 0 deletions nilearn/image/resampling.py
Original file line number Diff line number Diff line change
Expand Up @@ -267,6 +267,14 @@ def _resample_one_img(data, A, b, target_shape,
data = _extrapolate_out_mask(data, np.logical_not(not_finite),
iterations=2)[0]

# If data is binary and interpolation is continuous or linear,
# warn the user as this might be unintentional
if sorted(list(np.unique(data))) == [0,1] and interpolation_order != 0:
warnings.warn("Resampling binary images with continuous or "
"linear interpolation. This might lead to "
"unexpected results. You might consider using "
"nearest interpolation instead.")

# Suppresses warnings in https://github.com/nilearn/nilearn/issues/1363
with warnings.catch_warnings():
if LooseVersion(scipy.__version__) >= LooseVersion('0.18'):
Expand Down
22 changes: 22 additions & 0 deletions nilearn/image/tests/test_resampling.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@

from nibabel import Nifti1Image

from nilearn import _utils
from nilearn.image.resampling import resample_img, resample_to_img, reorder_img
from nilearn.image.resampling import from_matrix_vector, coord_transform
from nilearn.image.resampling import get_bounds
Expand Down Expand Up @@ -249,6 +250,27 @@ def test_resampling_error_checks():
interpolation="an_invalid_interpolation"
)

# Resampling a binary image with continuous or
# linear interpolation should raise a warning.
data_binary = rng.randint(4, size=(1, 4, 4))
data_binary[data_binary>0] = 1
assert sorted(list(np.unique(data_binary))) == [0,1]

rot = rotation(0, np.pi / 4)
img_binary = Nifti1Image(data_binary, np.eye(4))
assert _utils.niimg._is_binary_niimg(img_binary)

with pytest.warns(Warning, match="Resampling binary images with"):
rot_img = resample_img(img_binary,
target_affine=rot,
interpolation='continuous')

with pytest.warns(Warning, match="Resampling binary images with"):
rot_img = resample_img(img_binary,
target_affine=rot,
interpolation='linear')


# Noop
target_shape = shape[:3]

Expand Down
14 changes: 12 additions & 2 deletions nilearn/plotting/displays.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
from .edge_detect import _edge_map
from .find_cuts import find_xyz_cut_coords, find_cut_slices
from .. import _utils
from ..image import new_img_like
from ..image import new_img_like, load_img
from ..image.resampling import (get_bounds, reorder_img, coord_transform,
get_mask_bounds)
from nilearn.image import get_data
Expand Down Expand Up @@ -802,7 +802,17 @@ def add_contours(self, img, threshold=1e-6, filled=False, **kwargs):
def _map_show(self, img, type='imshow',
resampling_interpolation='continuous',
threshold=None, **kwargs):
img = reorder_img(img, resample=resampling_interpolation)
# In the special case where the affine of img is not diagonal,
# the function `reorder_img` will trigger a resampling
# of the provided image with a continuous interpolation
# since this is the default value here. In the special
# case where this image is binary, such as when this function
# is called from `add_contours`, continuous interpolation
# does not make sense and we turn to nearest interpolation instead.
if _utils.niimg._is_binary_niimg(img):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe you should explain the logic implemented here.

img = reorder_img(img, resample='nearest')
else:
img = reorder_img(img, resample=resampling_interpolation)
threshold = float(threshold) if threshold is not None else None

if threshold is not None:
Expand Down
17 changes: 17 additions & 0 deletions nilearn/plotting/tests/test_img_plotting.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@

from scipy import sparse

from nilearn import _utils
from nilearn.image.resampling import coord_transform, reorder_img
from nilearn._utils import data_gen
from nilearn.image import get_data
Expand Down Expand Up @@ -431,6 +432,7 @@ def test_plot_img_with_resampling(testdata_3d):
[0., 0., 1., 0.],
[0., 0., 0., 1.]])
img = nibabel.Nifti1Image(data, affine)
assert not _utils.niimg._is_binary_niimg(img)
display = plot_img(img)
display.add_overlay(img)
display.add_contours(img, contours=2, linewidth=4,
Expand All @@ -440,6 +442,21 @@ def test_plot_img_with_resampling(testdata_3d):
# Save execution time and memory
plt.close()

def test_plot_binary_img_with_resampling(testdata_3d):
data = get_data(testdata_3d['img'])
data[data > 0] = 1
data[data < 0] = 0
affine = np.array([[1., -1., 0., 0.],
[1., 1., 0., 0.],
[0., 0., 1., 0.],
[0., 0., 0., 1.]])
img = nibabel.Nifti1Image(data, affine)
assert _utils.niimg._is_binary_niimg(img)
display = plot_img(img)
display.add_overlay(img)
display.add_contours(img)
plt.close()


def test_plot_noncurrent_axes():
"""Regression test for Issue #450"""
Expand Down