Coverage for nilearn/_utils/masker_validation.py: 11%
67 statements
« prev ^ index » next coverage.py v7.9.1, created at 2025-06-20 10:58 +0200
« prev ^ index » next coverage.py v7.9.1, created at 2025-06-20 10:58 +0200
1import warnings
2from collections.abc import Iterable
3from string import Template
5import numpy as np
7from nilearn._utils.logger import find_stack_level
8from nilearn.surface import SurfaceImage
9from nilearn.typing import NiimgLike
11from .cache_mixin import check_memory
12from .class_inspect import get_params
15def check_embedded_masker(estimator, masker_type="multi_nii", ignore=None):
16 """Create a masker from instance parameters.
18 Base function for using a masker within a BaseEstimator class
20 This creates a masker from instance parameters :
22 - If instance contains a mask image in mask parameter,
23 we use this image as new masker mask_img, forwarding instance parameters to
24 new masker : smoothing_fwhm, standardize, detrend, low_pass= high_pass,
25 t_r, target_affine, target_shape, mask_strategy, mask_args...
27 - If instance contains a masker in mask parameter, we use a copy of
28 this masker, overriding all instance masker related parameters.
29 In all case, we forward system parameters of instance to new masker :
30 memory, memory_level, verbose, n_jobs
32 Parameters
33 ----------
34 instance : object, instance of BaseEstimator
35 The object that gives us the values of the parameters
37 masker_type : {"multi_nii", "nii", "surface"}, default="mutli_nii"
38 Indicates whether to return a MultiNiftiMasker, NiftiMasker, or a
39 SurfaceMasker.
41 ignore : None or list of strings
42 Names of the parameters of the estimator that should not be
43 transferred to the new masker.
45 Returns
46 -------
47 masker : MultiNiftiMasker, NiftiMasker, \
48 or :obj:`~nilearn.maskers.SurfaceMasker`
49 New masker
51 """
52 from nilearn.glm.first_level import FirstLevelModel
53 from nilearn.glm.second_level import SecondLevelModel
54 from nilearn.maskers import MultiNiftiMasker, NiftiMasker, SurfaceMasker
56 if masker_type == "surface":
57 masker_type = SurfaceMasker
58 elif masker_type == "multi_nii":
59 masker_type = MultiNiftiMasker
60 else:
61 masker_type = NiftiMasker
63 estimator_params = get_params(masker_type, estimator, ignore=ignore)
65 mask = getattr(estimator, "mask", None)
66 if isinstance(estimator, (FirstLevelModel, SecondLevelModel)):
67 mask = getattr(estimator, "mask_img", None)
69 if isinstance(mask, (NiftiMasker, MultiNiftiMasker, SurfaceMasker)):
70 # Creating masker from provided masker
71 masker_params = get_params(masker_type, mask)
72 new_masker_params = masker_params
73 else:
74 # Creating a masker with parameters extracted from estimator
75 new_masker_params = estimator_params
76 new_masker_params["mask_img"] = mask
77 # Forwarding system parameters of instance to new masker in all case
78 if issubclass(masker_type, MultiNiftiMasker) and hasattr(
79 estimator, "n_jobs"
80 ):
81 # For MultiNiftiMasker only
82 new_masker_params["n_jobs"] = estimator.n_jobs
84 warning_msg = Template(
85 "Provided estimator has no $attribute attribute set."
86 "Setting $attribute to $default_value by default."
87 )
89 if hasattr(estimator, "memory"):
90 new_masker_params["memory"] = check_memory(estimator.memory)
91 else:
92 warnings.warn(
93 warning_msg.substitute(
94 attribute="memory",
95 default_value="Memory(location=None)",
96 ),
97 stacklevel=find_stack_level(),
98 )
99 new_masker_params["memory"] = check_memory(None)
101 if hasattr(estimator, "memory_level"):
102 new_masker_params["memory_level"] = max(0, estimator.memory_level - 1)
103 else:
104 warnings.warn(
105 warning_msg.substitute(
106 attribute="memory_level", default_value="0"
107 ),
108 stacklevel=find_stack_level(),
109 )
110 new_masker_params["memory_level"] = 0
112 if hasattr(estimator, "verbose"):
113 new_masker_params["verbose"] = estimator.verbose
114 else:
115 warnings.warn(
116 warning_msg.substitute(attribute="verbose", default_value="0"),
117 stacklevel=find_stack_level(),
118 )
119 new_masker_params["verbose"] = 0
121 conflicting_param = [
122 k
123 for k in sorted(estimator_params)
124 if np.any(new_masker_params[k] != estimator_params[k])
125 ]
126 if conflicting_param:
127 conflict_string = "".join(
128 (
129 f"Parameter {k} :\n"
130 f" Masker parameter {new_masker_params[k]}"
131 f" - overriding estimator parameter {estimator_params[k]}\n"
132 )
133 for k in conflicting_param
134 )
135 warn_str = (
136 "Overriding provided-default estimator parameters with"
137 f" provided masker parameters :\n{conflict_string}"
138 )
139 warnings.warn(warn_str, stacklevel=find_stack_level())
141 masker = masker_type(**new_masker_params)
143 # Forwarding potential attribute of provided masker
144 if hasattr(mask, "mask_img_"):
145 # Allow free fit of returned mask
146 masker.mask_img = mask.mask_img_
148 return masker
151def check_compatibility_mask_and_images(mask_img, run_imgs):
152 """Check that mask type and image types are compatible.
154 Images to fit should be a Niimg-Like
155 if the mask is a NiftiImage, NiftiMasker or a path.
156 Similarly, only SurfaceImages can be fitted
157 with a SurfaceImage or a SurfaceMasker as mask.
158 """
159 from nilearn.maskers import NiftiMasker, SurfaceMasker
161 if mask_img is None:
162 return None
164 if not isinstance(run_imgs, Iterable):
165 run_imgs = [run_imgs]
167 msg = (
168 "Mask and images to fit must be of compatible types.\n"
169 f"Got mask of type: {type(mask_img)}, "
170 f"and images of type: {[type(x) for x in run_imgs]}"
171 )
173 volumetric_type = (*NiimgLike, NiftiMasker)
174 surface_type = (SurfaceImage, SurfaceMasker)
175 all_allowed_types = (*volumetric_type, *surface_type)
177 if not isinstance(mask_img, all_allowed_types):
178 raise TypeError(
179 "\nMask should be of type: "
180 f"{[x.__name__ for x in all_allowed_types]}.\n"
181 f"Got : '{mask_img.__class__.__name__}'"
182 )
184 if isinstance(mask_img, volumetric_type) and any(
185 not isinstance(x, NiimgLike) for x in run_imgs
186 ):
187 raise TypeError(
188 f"{msg} "
189 f"where images should be NiftiImage-like instances "
190 f"(Nifti1Image or str or Path)."
191 )
192 elif isinstance(mask_img, surface_type) and any(
193 not isinstance(x, SurfaceImage) for x in run_imgs
194 ):
195 raise TypeError(
196 f"{msg} where SurfaceImage instances would be expected."
197 )