Repository: nilearn/nilearn

Issue:
smoothing_fwhm passed to connected_regions in RegionExtractor hard coded: problem with mouse data
`smoothing_fwhm` is hard-coded when calling function `connected_regions` in `RegionExtractor`,
https://github.com/nilearn/nilearn/blob/master/nilearn/regions/region_extractor.py#L401
so the default value `smoothing_fwhm=6.` of function `connected_regions` is systematically applied and this is not suitable for my mice images (voxel size is 0.2x0.2x0.2)

Another point is that `min_region_size` is assumed to be an `int` and in my case it may be lower than 1.

I can do a PR to try fix this


Discussion:
> I can do a PR to try fix this

The first problem yes. The second one, I don't understand.

If want 50 voxels as minimal, if I understand correctly the doc, I have to put min_region_size to `50 * (0.2 ** 3)` so it will be `min_region_size=.4`, not an int as said in the doc 
It is not hard-coded if you import as a function, ```from nilearn.regions import connected_regions``` and use your own smoothing. I don't see a PR in this.
Indeed, either the docs or the code, or both should be fixed. Kamalakar,
what's your take?

>It is not hard-coded if you import as a function, from nilearn.regions import connected_regions and use your own smoothing. I don't see a PR in this

you mean I should use the function `connected_regions` and never use `RegionExtractor` ?
Both I guess.
>you mean I should use the function connected_regions and never use RegionExtractor ?

Please use it.
Yes but when I use `RegionExtractor` I can not set the smoothing_fwhm. So it smoothes my data at 6mm and I have no more regions :)
>I don't see a PR in this.

@KamalakerDadi may be I was unclear. So first my mouse fMRI images have different range than human images, e.g.` print func_imgs[0].header.get_zooms()` gives `(0.2, 0.2, 0.2, 1.0)`. Now I am using DictionaryLearning to extract my networks, with a smoothing FWHM suitable for my data
```Python
from nilearn.decomposition import DictLearning

dict_learn = DictLearning(n_components=20, smoothing_fwhm=.3, n_jobs=-3)
dict_learn.fit(func_imgs)
components_img = dict_learn.masker_.inverse_transform(dict_learn.components_)
```
At this point everything works fine. Now I want to keep only large enough regions
```Python
extractor = RegionExtractor(components_img, thresholding_strategy='ratio_n_voxels',
    extractor='local_regions', min_region_size=.4)
extractor.fit()
```
and here I have an error
```Python
ValueError: zero-size array to reduction operation maximum which has no identity
```

However when I set `smoothing_fwhm=.3` here
```Python
https://github.com/nilearn/nilearn/blob/master/nilearn/regions/region_extractor.py#L401
```
I get my regions extracted.
I agree with you. I was wrong in understanding. PR welcome please.
Thanks

Resolving patch:
diff --git a/nilearn/regions/region_extractor.py b/nilearn/regions/region_extractor.py
--- a/nilearn/regions/region_extractor.py
+++ b/nilearn/regions/region_extractor.py
@@ -260,7 +260,7 @@ class RegionExtractor(NiftiMapsMasker):
         Mask to be applied to input data, passed to NiftiMapsMasker.
         If None, no masking is applied.
 
-    min_region_size: int, default 1350 mm^3, optional
+    min_region_size: float, default 1350 mm^3, optional
         Minimum volume in mm3 for a region to be kept. For example, if
         the voxel size is 3x3x3 mm then the volume of the voxel is
         27mm^3. By default, it is 1350mm^3 which means we take minimum
@@ -294,6 +294,12 @@ class RegionExtractor(NiftiMapsMasker):
         random walker segementation algorithm on these markers for region
         separation.
 
+    smoothing_fwhm: scalar, default 6mm, optional
+        To smooth an image to extract most sparser regions. This parameter
+        is passed to `connected_regions` and exists only for extractor
+        'local_regions'. Please set this parameter according to maps
+        resolution, otherwise extraction will fail.
+
     standardize: bool, True or False, default False, optional
         If True, the time series signals are centered and normalized by
         putting their mean to 0 and variance to 1. Recommended to
@@ -358,11 +364,13 @@ class RegionExtractor(NiftiMapsMasker):
     """
     def __init__(self, maps_img, mask_img=None, min_region_size=1350,
                  threshold=1., thresholding_strategy='ratio_n_voxels',
-                 extractor='local_regions', standardize=False, detrend=False,
+                 extractor='local_regions', smoothing_fwhm=6,
+                 standardize=False, detrend=False,
                  low_pass=None, high_pass=None, t_r=None,
                  memory=Memory(cachedir=None), memory_level=0, verbose=0):
         super(RegionExtractor, self).__init__(
             maps_img=maps_img, mask_img=mask_img,
+            smoothing_fwhm=smoothing_fwhm,
             standardize=standardize, detrend=detrend, low_pass=low_pass,
             high_pass=high_pass, t_r=t_r, memory=memory,
             memory_level=memory_level, verbose=verbose)
@@ -371,6 +379,7 @@ def __init__(self, maps_img, mask_img=None, min_region_size=1350,
         self.thresholding_strategy = thresholding_strategy
         self.threshold = threshold
         self.extractor = extractor
+        self.smoothing_fwhm = smoothing_fwhm
 
     def fit(self, X=None, y=None):
         """ Prepare the data and setup for the region extraction
@@ -400,7 +409,8 @@ def fit(self, X=None, y=None):
         # connected component extraction
         self.regions_img_, self.index_ = connected_regions(threshold_maps,
                                                            self.min_region_size,
-                                                           self.extractor)
+                                                           self.extractor,
+                                                           self.smoothing_fwhm)
 
         self.maps_img = self.regions_img_
         super(RegionExtractor, self).fit()


Test patch:
diff --git a/nilearn/regions/tests/test_region_extractor.py b/nilearn/regions/tests/test_region_extractor.py
--- a/nilearn/regions/tests/test_region_extractor.py
+++ b/nilearn/regions/tests/test_region_extractor.py
@@ -173,6 +173,18 @@ def test_region_extractor_fit_and_transform():
         signal = extractor.transform(img)
         assert_equal(expected_signal_shape, signal.shape)
 
+    # smoke test with high resolution image
+    maps, mask_img = generate_maps((20, 20, 20), n_regions=n_regions,
+                                   affine=.2 * np.eye(4))
+
+    extract_ratio = RegionExtractor(maps,
+                                    thresholding_strategy='ratio_n_voxels',
+                                    smoothing_fwhm=.6,
+                                    min_region_size=.4)
+    extract_ratio.fit()
+    assert_not_equal(extract_ratio.regions_img_, '')
+    assert_true(extract_ratio.regions_img_.shape[-1] >= 9)
+
 
 def test_error_messages_connected_label_regions():
     shape = (13, 11, 12)