Repository: nilearn/nilearn

Issue:
nilearn.plotting.find_cuts.find_xyz_cut_coords should operate on a niimg
It should operate on a niimg return coordinnates in voxel space.

Typically, the following code (in slicers.OrthoSlicer.find_cut_coords):

<pre>
                x_map, y_map, z_map = find_xyz_cut_coords(img.get_data(),
                                        activation_threshold=threshold)
                cut_coords = coord_transform(x_map, y_map, z_map,
                                             img.get_affine())
</pre>

would be rewritten as only one function call.



Discussion:
Cc @lesteve : you might be interested in taking up that one when you have spare cycles.


Resolving patch:
diff --git a/nilearn/plotting/__init__.py b/nilearn/plotting/__init__.py
--- a/nilearn/plotting/__init__.py
+++ b/nilearn/plotting/__init__.py
@@ -31,8 +31,8 @@ def _set_mpl_backend():
 
 from . import cm
 from .img_plotting import plot_img, plot_anat, plot_epi, \
-        plot_roi, plot_stat_map
-
-__all__ = ['cm', 'plot_img', 'plot_anat', 'plot_roi', 'plot_epi',
-           'plot_stat_map']
+    plot_roi, plot_stat_map
+from .find_cuts import find_xyz_cut_coords
 
+__all__ = ['cm', 'plot_img', 'plot_anat', 'plot_epi',
+           'plot_roi', 'plot_stat_map', 'find_xyz_cut_coords']
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
@@ -21,29 +21,30 @@
 ################################################################################
 
 
-def find_xyz_cut_coords(data, mask=None, activation_threshold=None):
+def find_xyz_cut_coords(img, mask=None, activation_threshold=None):
     """ Find the center of the largest activation connected component.
 
         Parameters
         -----------
-        data : 3D ndarray
-            The activation map, as a 3D numpy array.
+        img : 3D Nifti1Image
+            The brain map.
         mask : 3D ndarray, boolean, optional
             An optional brain mask.
         activation_threshold : float, optional
             The lower threshold to the positive activation. If None, the
-            activation threshold is computed using the 80% percentil of
+            activation threshold is computed using the 80% percentile of
             the absolute value of the map.
 
         Returns
         -------
         x : float
-            the x coordinate in voxels.
+            the x world coordinate.
         y : float
-            the y coordinate in voxels.
+            the y world coordinate.
         z : float
-            the z coordinate in voxels.
+            the z world coordinate.
     """
+    data = img.get_data()
     # To speed up computations, we work with partial views of the array,
     # and keep track of the offset
     offset = np.zeros(3)
@@ -84,7 +85,11 @@ def find_xyz_cut_coords(data, mask=None, activation_threshold=None):
     if second_mask.sum() > 50:
         my_map *= largest_connected_component(second_mask)
     cut_coords = ndimage.center_of_mass(np.abs(my_map))
-    return cut_coords + offset
+    x_map, y_map, z_map = cut_coords + offset
+
+    return coord_transform(x_map, y_map, z_map,
+                           img.get_affine())
+
 
 
 ################################################################################
@@ -121,7 +126,7 @@ def find_cut_slices(img, direction='z', n_cuts=12, spacing='auto'):
     Parameters
     ----------
     img: 3D Nifti1Image
-        the data under consideration
+        the brain map
     direction: string, optional (default "z")
         sectional direction; possible values are "x", "y", or "z"
     n_cuts: int, optional (default 12)
diff --git a/nilearn/plotting/slicers.py b/nilearn/plotting/slicers.py
--- a/nilearn/plotting/slicers.py
+++ b/nilearn/plotting/slicers.py
@@ -547,10 +547,8 @@ def find_cut_coords(self, img=None, threshold=None, cut_coords=None):
             if img is None or img is False:
                 cut_coords = (0, 0, 0)
             else:
-                x_map, y_map, z_map = find_xyz_cut_coords(img.get_data(),
-                                        activation_threshold=threshold)
-                cut_coords = coord_transform(x_map, y_map, z_map,
-                                             img.get_affine())
+                cut_coords = find_xyz_cut_coords(img,
+                                                 activation_threshold=threshold)
             cut_coords = [cut_coords['xyz'.find(c)]
                           for c in sorted(self._cut_displayed)]
         return cut_coords


Test patch:
diff --git a/nilearn/plotting/test/test_find_cuts.py b/nilearn/plotting/test/test_find_cuts.py
--- a/nilearn/plotting/test/test_find_cuts.py
+++ b/nilearn/plotting/test/test_find_cuts.py
@@ -6,14 +6,31 @@
 
 from ..find_cuts import find_xyz_cut_coords, find_cut_slices
 
+
 def test_find_cut_coords():
     data = np.zeros((100, 100, 100))
     x_map, y_map, z_map = 50, 10, 40
     data[x_map-30:x_map+30, y_map-3:y_map+3, z_map-10:z_map+10] = 1
-    x, y, z = find_xyz_cut_coords(data, mask=np.ones(data.shape, np.bool))
-    np.testing.assert_array_equal(
-                        (int(round(x)), int(round(y)), int(round(z))),
-                                (x_map, y_map, z_map))
+
+    # identity affine
+    affine = np.eye(4)
+    img = nibabel.Nifti1Image(data, affine)
+    x, y, z = find_xyz_cut_coords(img, mask=np.ones(data.shape, np.bool))
+    np.testing.assert_allclose((x, y, z),
+                               (x_map, y_map, z_map),
+                               # Need such a high tolerance for the test to
+                               # pass. x, y, z = [49.5, 9.5, 39.5]
+                               rtol=6e-2)
+
+    # non-trivial affine
+    affine = np.diag([1./2, 1/3., 1/4., 1.])
+    img = nibabel.Nifti1Image(data, affine)
+    x, y, z = find_xyz_cut_coords(img, mask=np.ones(data.shape, np.bool))
+    np.testing.assert_allclose((x, y, z),
+                               (x_map / 2., y_map / 3., z_map / 4.),
+                               # Need such a high tolerance for the test to
+                               # pass. x, y, z = [24.75, 3.17, 9.875]
+                               rtol=6e-2)
 
 
 def test_find_cut_slices():
@@ -44,4 +61,4 @@ def test_find_cut_slices():
     for direction in 'xz':
         # Only a smoke test
         cuts = find_cut_slices(img, direction=direction,
-                                n_cuts=n_cuts, spacing=2)
+                               n_cuts=n_cuts, spacing=2)