Repository: nilearn/nilearn

Issue:
`view_img` always masks `bg_img` with MNI mask
when plotting with view_img, it appears the bg image is being "masked" by a different brain (perhaps MNI?)
![image](https://user-images.githubusercontent.com/39420777/148601175-f7c93605-ff76-4cb2-813a-1a1cded3e611.png)

problem persists even when using same image as bg_image and plotted img
`view_img` always masks `bg_img` with MNI mask
when plotting with view_img, it appears the bg image is being "masked" by a different brain (perhaps MNI?)
![image](https://user-images.githubusercontent.com/39420777/148601175-f7c93605-ff76-4cb2-813a-1a1cded3e611.png)

problem persists even when using same image as bg_image and plotted img


Discussion:
Thx for reporting. Can you provide a  snippet of code (and possibly an image) to reproduce this ? Best,
[malf_Labels.nii.gz](https://github.com/nilearn/nilearn/files/7831739/malf_Labels.nii.gz)

```
import nilearn as ni
from nilearn import image, plotting

malf=image.load_img('/Users/danielbrennan/Downloads/c001/malf_Labels.nii.gz')
plotting.view_img(malf,bg_img=malf,cmap="cold_hot",threshold="99%",black_bg=False)
```
from messing around, it seems to be some kind of center-of-mass-like calculation which offsets the viewpoint. removing the `threshold="99%"` fixes the issue. But if I threshold outside of the plotting function, I get the same result 
Looking quickly at it, it seems to amount to the fact that `bg_img` is handled differently than `stat_map_img`: in particular it is not resampled. When these images are not in MNI space this indeed leads to spurious plots.
The point is that in most cases we have dealt with so far, the bg_img would  be an MNI template, or at least in MNI space.
right, I'll see if I can change that behavior; I do most of my analysis in subject-space. Seems strange to allow supplying of bg_img but resample automatically into MNI.

Should also be emphasized again that this behavior goes away (i.e. plots correctly) when omitting the threshold argument 
@dbrennan44 `view_img` resamples the stat map to the background image automatically as can be seen here:

https://github.com/nilearn/nilearn/blob/78b09866b71c532552fd49e5cfd383db65b457b9/nilearn/plotting/html_stat_map.py#L535-L536

In addition, if you don't explicitely set `bg_img=False` in `view_img`, then the MNI152 template is used and the stat map is therefore resampled to the template. 

https://github.com/nilearn/nilearn/blob/78b09866b71c532552fd49e5cfd383db65b457b9/nilearn/plotting/html_stat_map.py#L444-L447

By setting `bg_img` to False, or to the stat map like you did, `view_img` shouldn't resample the stat map.

I'm not sure to understand why the threshold would change this though. It is only used to select the cut slices here:

https://github.com/nilearn/nilearn/blob/78b09866b71c532552fd49e5cfd383db65b457b9/nilearn/plotting/html_stat_map.py#L537
- the bug is [here](https://github.com/nilearn/nilearn/blob/78b09866b71c532552fd49e5cfd383db65b457b9/nilearn/plotting/html_stat_map.py#L316); when preparing the "brain sprite" for the background a mask is computed (not sure why the background needs to be masked, in fact I don't think it should). the problem is that this is done with `compute_brain_mask` which just thresholds the mni152 template after resampling it to its first argument's resolution -- this doesn't work if the image isn't in mni space
- the problem also happens when there is no thresholding, but in that case we just don't see it because the background map is completely hidden behind the foreground map
- it is not a resampling issue, if the passed `bg_img` and stat map (as is the case here) have the same affine and shape the stat map isn't modified
- setting `bg_img=None` (or `False`) doesn't really solve the problem because (i) we do want the background image to be plotted and (ii) the masking of the background still happens so we see the outline of the mni template:

![Screenshot from 2022-01-10 10-34-42](https://user-images.githubusercontent.com/9196501/148793256-55637858-d372-4899-af52-afc0af6cc07b.png)

During core dev meeting yesterday it was decided that:
- the default bg_img (MNI template) would continue to be masked, ie background is transparent outside of the brain
- any other background image will not be masked (so `black_bg` parameter may not be very useful in that case)
Thx for reporting. Can you provide a  snippet of code (and possibly an image) to reproduce this ? Best,
[malf_Labels.nii.gz](https://github.com/nilearn/nilearn/files/7831739/malf_Labels.nii.gz)

```
import nilearn as ni
from nilearn import image, plotting

malf=image.load_img('/Users/danielbrennan/Downloads/c001/malf_Labels.nii.gz')
plotting.view_img(malf,bg_img=malf,cmap="cold_hot",threshold="99%",black_bg=False)
```
from messing around, it seems to be some kind of center-of-mass-like calculation which offsets the viewpoint. removing the `threshold="99%"` fixes the issue. But if I threshold outside of the plotting function, I get the same result 
Looking quickly at it, it seems to amount to the fact that `bg_img` is handled differently than `stat_map_img`: in particular it is not resampled. When these images are not in MNI space this indeed leads to spurious plots.
The point is that in most cases we have dealt with so far, the bg_img would  be an MNI template, or at least in MNI space.
right, I'll see if I can change that behavior; I do most of my analysis in subject-space. Seems strange to allow supplying of bg_img but resample automatically into MNI.

Should also be emphasized again that this behavior goes away (i.e. plots correctly) when omitting the threshold argument 
@dbrennan44 `view_img` resamples the stat map to the background image automatically as can be seen here:

https://github.com/nilearn/nilearn/blob/78b09866b71c532552fd49e5cfd383db65b457b9/nilearn/plotting/html_stat_map.py#L535-L536

In addition, if you don't explicitely set `bg_img=False` in `view_img`, then the MNI152 template is used and the stat map is therefore resampled to the template. 

https://github.com/nilearn/nilearn/blob/78b09866b71c532552fd49e5cfd383db65b457b9/nilearn/plotting/html_stat_map.py#L444-L447

By setting `bg_img` to False, or to the stat map like you did, `view_img` shouldn't resample the stat map.

I'm not sure to understand why the threshold would change this though. It is only used to select the cut slices here:

https://github.com/nilearn/nilearn/blob/78b09866b71c532552fd49e5cfd383db65b457b9/nilearn/plotting/html_stat_map.py#L537
- the bug is [here](https://github.com/nilearn/nilearn/blob/78b09866b71c532552fd49e5cfd383db65b457b9/nilearn/plotting/html_stat_map.py#L316); when preparing the "brain sprite" for the background a mask is computed (not sure why the background needs to be masked, in fact I don't think it should). the problem is that this is done with `compute_brain_mask` which just thresholds the mni152 template after resampling it to its first argument's resolution -- this doesn't work if the image isn't in mni space
- the problem also happens when there is no thresholding, but in that case we just don't see it because the background map is completely hidden behind the foreground map
- it is not a resampling issue, if the passed `bg_img` and stat map (as is the case here) have the same affine and shape the stat map isn't modified
- setting `bg_img=None` (or `False`) doesn't really solve the problem because (i) we do want the background image to be plotted and (ii) the masking of the background still happens so we see the outline of the mni template:

![Screenshot from 2022-01-10 10-34-42](https://user-images.githubusercontent.com/9196501/148793256-55637858-d372-4899-af52-afc0af6cc07b.png)

During core dev meeting yesterday it was decided that:
- the default bg_img (MNI template) would continue to be masked, ie background is transparent outside of the brain
- any other background image will not be masked (so `black_bg` parameter may not be very useful in that case)

Resolving patch:
diff --git a/nilearn/plotting/html_stat_map.py b/nilearn/plotting/html_stat_map.py
--- a/nilearn/plotting/html_stat_map.py
+++ b/nilearn/plotting/html_stat_map.py
@@ -14,7 +14,7 @@
 
 from nibabel.affines import apply_affine
 
-from ..image import resample_to_img, new_img_like, reorder_img
+from ..image import resample_to_img, new_img_like, reorder_img, get_data
 from .js_plotting_utils import get_html_template, colorscale
 from ..plotting import cm
 from ..plotting.find_cuts import find_xyz_cut_coords
@@ -26,7 +26,6 @@
 from .._utils.extmath import fast_abs_percentile
 from .._utils.niimg import _safe_get_data
 from ..datasets import load_mni152_template
-from ..masking import compute_brain_mask
 
 
 def _data_to_sprite(data):
@@ -209,19 +208,23 @@ def _load_bg_img(stat_map_img, bg_img='MNI152', black_bg='auto', dim='auto'):
     Returns: bg_img, bg_min, bg_max, black_bg
 
     """
-    if (bg_img is None or bg_img is False) and black_bg == 'auto':
-        black_bg = False
-
-    if bg_img is not None and bg_img is not False:
+    if (bg_img is None or bg_img is False):
+        if black_bg == 'auto':
+            black_bg = False
+        bg_img = new_img_like(
+            stat_map_img, np.ma.masked_all(stat_map_img.shape))
+        bg_min, bg_max = 0, 0
+    else:
         if isinstance(bg_img, str) and bg_img == "MNI152":
             bg_img = load_mni152_template()
-        bg_img, black_bg, bg_min, bg_max = _load_anat(bg_img, dim=dim,
-                                                      black_bg=black_bg)
-    else:
-        bg_img = new_img_like(stat_map_img, np.zeros(stat_map_img.shape),
-                              stat_map_img.affine)
-        bg_min = 0
-        bg_max = 0
+        else:
+            bg_img = check_niimg_3d(bg_img)
+        masked_data = np.ma.masked_inside(
+            _safe_get_data(bg_img, ensure_finite=True),
+            -1e-6, 1e-6, copy=False)
+        bg_img = new_img_like(bg_img, masked_data)
+        bg_img, black_bg, bg_min, bg_max = _load_anat(
+            bg_img, dim=dim, black_bg=black_bg)
     bg_img = reorder_img(bg_img, resample='nearest')
     return bg_img, bg_min, bg_max, black_bg
 
@@ -313,10 +316,7 @@ def _json_view_size(params):
 
 def _get_bg_mask_and_cmap(bg_img, black_bg):
     """Helper function of _json_view_data."""
-    bg_mask = _safe_get_data(compute_brain_mask(bg_img),
-                             ensure_finite=True)
-    bg_mask = np.logical_not(bg_mask).astype(float)
-    bg_mask[bg_mask == 1] = np.nan
+    bg_mask = np.ma.getmaskarray(get_data(bg_img))
     bg_cmap = copy.copy(matplotlib.cm.get_cmap('gray'))
     if black_bg:
         bg_cmap.set_bad('black')


Test patch:
diff --git a/nilearn/plotting/tests/test_html_stat_map.py b/nilearn/plotting/tests/test_html_stat_map.py
--- a/nilearn/plotting/tests/test_html_stat_map.py
+++ b/nilearn/plotting/tests/test_html_stat_map.py
@@ -187,6 +187,14 @@ def test_load_bg_img():
     _check_affine(bg_img.affine)
 
 
+def test_get_bg_mask_and_cmap():
+    # non-regression test for issue #3120 (bg image was masked with mni
+    # template mask)
+    img, _ = _simulate_img()
+    mask, cmap = html_stat_map._get_bg_mask_and_cmap(img, False)
+    assert (mask == np.zeros(img.shape, dtype=bool)).all()
+
+
 def test_resample_stat_map():
 
     # Start with simple simulated data