Repository: nilearn/nilearn

Issue:
find_xyz_cut_coords crashes on some compact binary images
...trace back is:

``` python
---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
<ipython-input-44-802cef648458> in <module>()
----> 1 plotting.find_xyz_cut_coords(nib.load(roi_paths[2]))

/git/nilearn/nilearn/plotting/find_cuts.py in find_xyz_cut_coords(img, mask, activation_threshold)
     90         return .5 * np.array(data.shape)
     91     mask = largest_connected_component(mask)
---> 92     slice_x, slice_y, slice_z = ndimage.find_objects(mask)[0]
     93     my_map = my_map[slice_x, slice_y, slice_z]
     94     mask = mask[slice_x, slice_y, slice_z]
```

The image on which this crashes is attached.

[dmpfc-3.nii.zip](https://github.com/nilearn/nilearn/files/66476/dmpfc-3.nii.zip)



Discussion:
The issue is that the image that you provided is 4D (with the last dimension=1). if you remove the last dimension form your image, it is OK.
plotting.find_xyz_cut_coords(nib.four_to_three(nib.load('dmpfc-3.nii'))[0])

Should we support this kind of case ?

Ok, merci pour le conseil.

It would be nice to support the case because the 3D and Pseudo-4D images contain the same information and the optimal x, y and z cut planes can be expected to be the same. That appears intuitive to me.

As all functions working on img directly (not niimg), it assumes that the input has the right dimension. This has been done because this function is not "opened to public". So, two options:
- you use the regular way to load your image, meaning `check_niimg_3d` (instead of `nibabel.load`). It will convert your 4d image to 3d automatically
- We call `check_niimg_3d` at the beginning of `find_xyz_cut_coords`. It will then work natively on all niimgs.

These days it is exposed to the public. We should probably add a checker
in the input. Is there a downside?

Well, a small computational overhead because we check the input data, that's all.

> Well, a small computational overhead because we check the input data,

It's probably tiny compared to the time of the funtion.

@banilo: do you want to do a PR?

Sure, I'd be happy to.

As the find_\* function is probably typically called for plotting, it should
not be called excessively and thus not typically cause a big overhead.

2015-12-20 15:08 GMT+01:00 Gael Varoquaux notifications@github.com:

> > Well, a small computational overhead because we check the input data,
> 
> It's probably tiny compared to the time of the funtion.
> 
> @banilo: do you want to do a PR?
> 
> —
> Reply to this email directly or view it on GitHub
> https://github.com/nilearn/nilearn/issues/922#issuecomment-166119067.

@banilo I think this is important for 0.2.2. Are you working on it?

I will do it this weekend


Resolving patch:
diff --git a/nilearn/plotting/find_cuts.py b/nilearn/plotting/find_cuts.py
--- a/nilearn/plotting/find_cuts.py
+++ b/nilearn/plotting/find_cuts.py
@@ -15,6 +15,8 @@
 from ..image import new_img_like
 from .._utils.extmath import fast_abs_percentile
 from .._utils.numpy_conversions import as_ndarray
+from .._utils import check_niimg_3d
+from .._utils.niimg import _safe_get_data
 from ..image.resampling import get_mask_bounds, coord_transform
 from ..image.image import _smooth_array
 
@@ -46,7 +48,11 @@ def find_xyz_cut_coords(img, mask=None, activation_threshold=None):
         z : float
             the z world coordinate.
     """
-    data = img.get_data()
+    # if a pseudo-4D image or several images were passed (cf. #922),
+    # we reduce to a single 3D image to find the coordinates
+    img = check_niimg_3d(img)
+    data = _safe_get_data(img)
+
     # To speed up computations, we work with partial views of the array,
     # and keep track of the offset
     offset = np.zeros(3)


Test patch:
diff --git a/nilearn/plotting/tests/test_find_cuts.py b/nilearn/plotting/tests/test_find_cuts.py
--- a/nilearn/plotting/tests/test_find_cuts.py
+++ b/nilearn/plotting/tests/test_find_cuts.py
@@ -42,6 +42,17 @@ def test_find_cut_coords():
         np.array([x, y, z]),
         0.5 * np.array(data.shape).astype(np.float))
 
+    # regression test (cf. #922)
+    # pseudo-4D images as input (i.e., X, Y, Z, 1)
+    # previously raised "ValueError: too many values to unpack"
+    rng = np.random.RandomState(42)
+    data_3d = rng.randn(10, 10, 10)
+    data_4d = data_3d[..., np.newaxis]
+    affine = np.eye(4)
+    img_3d = nibabel.Nifti1Image(data_3d, affine)
+    img_4d = nibabel.Nifti1Image(data_4d, affine)
+    assert_equal(find_xyz_cut_coords(img_3d), find_xyz_cut_coords(img_4d))
+
 
 def test_find_cut_slices():
     data = np.zeros((50, 50, 50))