Repository: nilearn/nilearn

Issue:
User warning when data is being upsampled when masked
<!--Provide a brief description what you would like changes and why.-->
The default beahaviour of maskers is to resample the data to the mask's affine, but when the data has a lower resolution than the mask, it upsamples the data, requiring more computation and memory than necessary. Throwing a warning when it happens could help users to specify a target affine when it is relevant. 


<!--Please fill in the following information, to the best of your ability.-->
### Benefits to the change
Users can realise when they upsample their data, and that they can save computation time and memory by specifying a target affine (or a resampling strategy).

### Pseudocode for the new behavior, if applicable

```python
## in transform_single_imgs
if imgs.resolution < self.mask_img_.resolution:
    raise UserWarning("Images are being upsampled to the mask's resolution, "
        "you might want to provide a target_affine to save memory and computation time.")

```



Discussion:
we can improve the warning message but I like the idea!
Actually now that I'm trying to write a check to catch the upsampling, I'm not so sure anymore that it's always the default behaviour. It for sure has happened to me but I might need to investigate deeper into how I got it to happen.
I'm going to see this warning so often haha :laughing: 
Hi @FrancoisPgm sorry it took me a while to get back to you. So the resampling to the mask is indeed the default behavior of the `NiftiMasker` class (and `MultiNiftiMasker` by inheritance). These maskers use a function called `_filter_and_mask` in `nilearn.maskers.nifti_masker.py` to call another function to perform resampling and timeseries extraction. An appropriate place to add your check and warning would therefore be under this `_filter_and_mask` function. More precisely, in line 103 of `nifti_masker.py` there is a check if resampling is necessary, you can put your code under this conditional.

It is the case that the default behavior for the other maskers is different and they do not need this warning.
@mtorabi59 can you also have a look at this? I think implementing this warning can only be helpful in the meantime while we haven't made a decision about making this behavior consistent across all maskers.
@ymzayek sure, I will put it at the top of my list!


Resolving patch:
diff --git a/nilearn/maskers/nifti_masker.py b/nilearn/maskers/nifti_masker.py
--- a/nilearn/maskers/nifti_masker.py
+++ b/nilearn/maskers/nifti_masker.py
@@ -95,6 +95,14 @@ def _filter_and_mask(
     # as small as possible in order to speed up the process
 
     if not _utils.niimg_conversions._check_same_fov(imgs, mask_img_):
+        warnings.warn(
+            'imgs are being resampled to the mask_img resolution. '
+            'This process is memory intensive. You might want to provide '
+            'a target_affine that is equal to the affine of the imgs '
+            'or resample the mask beforehand '
+            'to save memory and computation time.',
+            UserWarning
+        )
         parameters = copy_object(parameters)
         # now we can crop
         mask_img_ = image.crop_img(mask_img_, copy=False)


Test patch:
diff --git a/nilearn/maskers/tests/test_nifti_masker.py b/nilearn/maskers/tests/test_nifti_masker.py
--- a/nilearn/maskers/tests/test_nifti_masker.py
+++ b/nilearn/maskers/tests/test_nifti_masker.py
@@ -69,6 +69,30 @@ def test_resample():
     assert np.any(X != 0)
 
 
+def test_resample_to_mask_warning():
+    """Check that a warning is raised when data is
+    being resampled to mask's resolution.
+    """
+    data = np.zeros((9, 9, 9))
+    data[3:-3, 3:-3, 3:-3] = 10
+    img = nibabel.Nifti1Image(data, np.eye(4))
+    # defining a mask with different fov than img
+    mask = np.zeros((12, 12, 12))
+    mask[3:-3, 3:-3, 3:-3] = 10
+    mask = mask.astype("uint8")
+    mask_img = nibabel.Nifti1Image(mask, np.eye(4))
+    masker = NiftiMasker(mask_img=mask_img)
+    with pytest.warns(
+        UserWarning,
+        match='imgs are being resampled to the mask_img resolution. '
+            'This process is memory intensive. You might want to provide '
+            'a target_affine that is equal to the affine of the imgs '
+            'or resample the mask beforehand '
+            'to save memory and computation time.'
+    ):
+        masker.fit_transform(img)
+
+
 def test_with_files():
     """Test standard masking with filenames."""
     data = np.zeros((40, 40, 40, 2))