Repository: nilearn/nilearn

Issue:
Can't plot regions with nilearn 0.5.1 (because new_img_like() errs with non-iterable Headers)
Referencing (private Repo): https://github.com/NeuroLang/NeuroLang/issues/182
I'll investigate & attempt to replicate the problem.

> After update Nilearn to the last version available (nilearn=0.5.2) I can't plot neurolang regions. Is not the case of nilearn=0.5.0 that works well.
> 
> The code in '[plot_neurolang_datalog_example.py'](https://github.com/NeuroLang/NeuroLang/blob/64eac3f0dee3fde9101166685e430d1134b20c08/examples/plot_neurolang_datalog_example.py) is enough to reproduce the problem.
> 
> ```
> ---------------------------------------------------------------------------
> TypeError                                 Traceback (most recent call last)
> <ipython-input-12-bd2b51b68e5c> in <module>
>      19 )
>      20 for name, region in res.value:
> ---> 21     plotting.plot_roi(region.spatial_image(), title=name)
> 
> ~/miniconda3/envs/neurolang/lib/python3.7/site-packages/nilearn/plotting/img_plotting.py in plot_roi(roi_img, bg_img, cut_coords, output_file, display_mode, figure, axes, title, annotate, draw_cross, black_bg, threshold, alpha, cmap, dim, vmin, vmax, resampling_interpolation, **kwargs)
>     706         threshold=threshold, bg_vmin=bg_vmin, bg_vmax=bg_vmax,
>     707         resampling_interpolation=resampling_interpolation,
> --> 708         alpha=alpha, cmap=cmap, vmin=vmin, vmax=vmax, **kwargs)
>     709     return display
>     710 
> 
> ~/miniconda3/envs/neurolang/lib/python3.7/site-packages/nilearn/plotting/img_plotting.py in _plot_img_with_bg(img, bg_img, cut_coords, output_file, display_mode, colorbar, figure, axes, title, threshold, annotate, draw_cross, black_bg, vmin, vmax, bg_vmin, bg_vmax, interpolation, display_factory, cbar_vmin, cbar_vmax, brain_color, **kwargs)
>     153 
>     154     if img is not False and img is not None:
> --> 155         img = _utils.check_niimg_3d(img, dtype='auto')
>     156         data = _safe_get_data(img, ensure_finite=True)
>     157         affine = img.affine
> 
> ~/miniconda3/envs/neurolang/lib/python3.7/site-packages/nilearn/_utils/niimg_conversions.py in check_niimg_3d(niimg, dtype)
>     320     Its application is idempotent.
>     321     """
> --> 322     return check_niimg(niimg, ensure_ndim=3, dtype=dtype)
>     323 
>     324 
> 
> ~/miniconda3/envs/neurolang/lib/python3.7/site-packages/nilearn/_utils/niimg_conversions.py in check_niimg(niimg, ensure_ndim, atleast_4d, dtype, return_iterator, wildcards)
>     269 
>     270     # Otherwise, it should be a filename or a SpatialImage, we load it
> --> 271     niimg = load_niimg(niimg, dtype=dtype)
>     272 
>     273     if ensure_ndim == 3 and len(niimg.shape) == 4 and niimg.shape[3] == 1:
> 
> ~/miniconda3/envs/neurolang/lib/python3.7/site-packages/nilearn/_utils/niimg.py in load_niimg(niimg, dtype)
>     120         if niimg.header is not None:
>     121             niimg = new_img_like(niimg, niimg.get_data().astype(dtype),
> --> 122                                 niimg.affine, copy_header=True)
>     123             niimg.header.set_data_dtype(dtype)
>     124         else:
> 
> ~/miniconda3/envs/neurolang/lib/python3.7/site-packages/nilearn/image/image.py in new_img_like(ref_niimg, data, affine, copy_header)
>     637     if copy_header:
>     638         header = copy.deepcopy(ref_niimg.header)
> --> 639         if 'scl_slope' in header:
>     640             header['scl_slope'] = 0.
>     641         if 'scl_inter' in header:
> 
> TypeError: argument of type 'SpatialHeader' is not iterable
> ```




Discussion:
link seems broken?
Oh I forgot it's a private repo. I'll post the details here after lunch.

On Fri, Oct 4, 2019, 11:47 jeromedockes <notifications@github.com> wrote:

> link seems broken?
>
> —
> You are receiving this because you authored the thread.
> Reply to this email directly, view it on GitHub
> <https://github.com/nilearn/nilearn/issues/2147?email_source=notifications&email_token=AB6SXRBFGO2D7JMOYEBOYKDQM4GMZA5CNFSM4I5NSFEKYY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJKTDN5WW2ZLOORPWSZGOEALEJZI#issuecomment-538330341>,
> or mute the thread
> <https://github.com/notifications/unsubscribe-auth/AB6SXREOKAPBNFP6Z7E3JF3QM4GMZANCNFSM4I5NSFEA>
> .
>


> Oh I forgot it's a private repo. I'll post the details here after lunch.

thanks!

The example can't be accessed by non-members currently. I'll do some investigating and then update here, then we can see if we need more access or not.
I think we just need the image that can't be loaded, not the full example
@gzanitti @tgy
They are using the `datasets.fetch_atlas_destrieux_2009()` in their example.
The issue stems from PR #1872 . Reversing the changes made in that PR fixes this.
I am going to explore more and figure out what to do about this without losing the benefits of PR #1872 
SpatialHeader is not iterable and hence does not support the containership test (the `in` operator) that `new_img_like(copy_header=True)` performs.

This implies to me that usual Nifti Image Headers are iterable which is why this hasn't been a problem for us generally.
However, PR #1872 was merged without tests to test the behaviour , so I will write one.

Resolving patch:
diff --git a/nilearn/image/image.py b/nilearn/image/image.py
--- a/nilearn/image/image.py
+++ b/nilearn/image/image.py
@@ -704,18 +704,23 @@ def new_img_like(ref_niimg, data, affine=None, copy_header=False):
     header = None
     if copy_header:
         header = copy.deepcopy(ref_niimg.header)
-        if 'scl_slope' in header:
-            header['scl_slope'] = 0.
-        if 'scl_inter' in header:
-            header['scl_inter'] = 0.
-        # 'glmax' is removed for Nifti2Image. Modify only if 'glmax' is
-        # available in header. See issue #1611
-        if 'glmax' in header:
-            header['glmax'] = 0.
-        if 'cal_max' in header:
-            header['cal_max'] = np.max(data) if data.size > 0 else 0.
-        if 'cal_min' in header:
-            header['cal_min'] = np.min(data) if data.size > 0 else 0.
+        try:
+            'something' in header
+        except TypeError:
+            pass
+        else:
+            if 'scl_slope' in header:
+                header['scl_slope'] = 0.
+            if 'scl_inter' in header:
+                header['scl_inter'] = 0.
+            # 'glmax' is removed for Nifti2Image. Modify only if 'glmax' is
+            # available in header. See issue #1611
+            if 'glmax' in header:
+                header['glmax'] = 0.
+            if 'cal_max' in header:
+                header['cal_max'] = np.max(data) if data.size > 0 else 0.
+            if 'cal_min' in header:
+                header['cal_min'] = np.min(data) if data.size > 0 else 0.
     klass = ref_niimg.__class__
     if klass is nibabel.Nifti1Pair:
         # Nifti1Pair is an internal class, without a to_filename,


Test patch:
diff --git a/nilearn/image/tests/test_image.py b/nilearn/image/tests/test_image.py
--- a/nilearn/image/tests/test_image.py
+++ b/nilearn/image/tests/test_image.py
@@ -488,6 +488,20 @@ def test_new_img_like():
     np.testing.assert_array_equal(get_data(img_nifti2), get_data(img2_nifti2))
 
 
+def test_new_img_like_non_iterable_header():
+    """
+    Tests that when an niimg's header is not iterable
+    & it is set to be copied, an error is not raised.
+    """
+    fake_fmri_data = np.random.rand(10, 10, 10, 10)
+    fake_affine = np.random.rand(4, 4)
+    fake_spatial_image = nibabel.spatialimages.SpatialImage(fake_fmri_data,
+                                                            fake_affine)
+    assert new_img_like(fake_spatial_image,
+                        data=fake_fmri_data,
+                        copy_header=True)
+
+
 def test_validity_threshold_value_in_threshold_img():
     shape = (6, 8, 10)
     maps, _ = data_gen.generate_maps(shape, n_regions=2)