Repository: nilearn/nilearn

Issue:
get_clusters_table does not allow file input and does not check that image is 3D
<!--Provide a brief description of the bug.-->
get_clusters_table does not let me pass in a filename, and does not ensure the image is 3D, which can raise an error of there is an "extra dimension" in the data.

<!--Please fill in the following information, to the best of your ability.-->
Nilearn version: 0.7.0

### Expected behavior

`get_clusters_table` should take filenames as input and ensure/check inputs are 3D

### Actual behavior

raises error if passed filename or 4D input (but effectively 3D) is passed.

### Steps and code to reproduce bug

```python
>>> from nilearn.reporting import get_clusters_table
>>> import numpy as np
>>> import nibabel as nib
>>> shape = (9, 10, 11)
>>> data = np.zeros(shape)
>>> data[2:4, 5:7, 6:8] = 5.
>>> stat_img = nib.Nifti1Image(data[...,np.newaxis], np.eye(4))
>>> get_clusters_table(stat_img, 0.0)

```

Error:
```
  File "/home/jdkent/projects/nilearn/nilearn/reporting/_get_clusters_table.py", line 186, in get_clusters_table
    label_map = ndimage.measurements.label(binarized, conn_mat)[0]
  File "/home/jdkent/projects/nilearn/.nilearn/lib/python3.8/site-packages/scipy/ndimage/measurements.py", line 183, in label
    raise RuntimeError('structure and input must have equal rank')
RuntimeError: structure and input must have equal rank
```



Discussion:

Resolving patch:
diff --git a/nilearn/reporting/_get_clusters_table.py b/nilearn/reporting/_get_clusters_table.py
--- a/nilearn/reporting/_get_clusters_table.py
+++ b/nilearn/reporting/_get_clusters_table.py
@@ -14,6 +14,7 @@
 
 from nilearn.image import get_data
 from nilearn.image.resampling import coord_transform
+from nilearn._utils import check_niimg_3d
 
 
 def _local_max(data, affine, min_distance):
@@ -159,6 +160,8 @@ def get_clusters_table(stat_img, stat_threshold, cluster_threshold=None,
     """
     cols = ['Cluster ID', 'X', 'Y', 'Z', 'Peak Stat', 'Cluster Size (mm3)']
 
+    # check that stat_img is niimg-like object and 3D
+    stat_img = check_niimg_3d(stat_img)
     # If cluster threshold is used, there is chance that stat_map will be
     # modified, therefore copy is needed
     if cluster_threshold is None:


Test patch:
diff --git a/nilearn/reporting/tests/test_reporting.py b/nilearn/reporting/tests/test_reporting.py
--- a/nilearn/reporting/tests/test_reporting.py
+++ b/nilearn/reporting/tests/test_reporting.py
@@ -69,7 +69,7 @@ def test_local_max():
     assert np.all(np.isnan(vals))
 
 
-def test_get_clusters_table():
+def test_get_clusters_table(tmp_path):
     shape = (9, 10, 11)
     data = np.zeros(shape)
     data[2:4, 5:7, 6:8] = 5.
@@ -87,6 +87,18 @@ def test_get_clusters_table():
     cluster_table = get_clusters_table(stat_img, 4, 9)
     assert len(cluster_table) == 0
 
+    # test with filename
+    fname = str(tmp_path / "stat_img.nii.gz")
+    stat_img.to_filename(fname)
+    cluster_table = get_clusters_table(fname, 4, 0)
+    assert len(cluster_table) == 1
+
+    # test with extra dimension
+    data_extra_dim = data[..., np.newaxis]
+    stat_img_extra_dim = nib.Nifti1Image(data_extra_dim, np.eye(4))
+    cluster_table = get_clusters_table(stat_img_extra_dim, 4, 0)
+    assert len(cluster_table) == 1
+
 
 def test_get_clusters_table_not_modifying_stat_image():
     shape = (9, 10, 11)