Coverage for nilearn/plotting/surface/html_surface.py: 0%
99 statements
« prev ^ index » next coverage.py v7.9.1, created at 2025-06-16 12:32 +0200
« prev ^ index » next coverage.py v7.9.1, created at 2025-06-16 12:32 +0200
1"""Handle plotting of surfaces for html rendering."""
3import json
4from warnings import warn
6import numpy as np
8from nilearn import DEFAULT_DIVERGING_CMAP
9from nilearn._utils import check_niimg_3d, fill_doc
10from nilearn._utils.html_document import HTMLDocument
11from nilearn._utils.logger import find_stack_level
12from nilearn._utils.param_validation import check_params
13from nilearn.plotting import cm
14from nilearn.plotting.js_plotting_utils import (
15 add_js_lib,
16 colorscale,
17 get_html_template,
18 mesh_to_plotly,
19)
20from nilearn.plotting.surface._utils import (
21 DEFAULT_ENGINE,
22 DEFAULT_HEMI,
23 check_surface_plotting_inputs,
24 get_surface_backend,
25)
26from nilearn.surface import (
27 PolyMesh,
28 SurfaceImage,
29 load_surf_data,
30 load_surf_mesh,
31)
32from nilearn.surface.surface import (
33 check_mesh_and_data,
34 check_mesh_is_fsaverage,
35 combine_hemispheres_meshes,
36 get_data,
37)
40class SurfaceView(HTMLDocument): # noqa: D101
41 pass
44def _one_mesh_info(
45 surf_map,
46 surf_mesh,
47 threshold=None,
48 cmap=cm.cold_hot,
49 black_bg=False,
50 bg_map=None,
51 symmetric_cmap=True,
52 bg_on_data=False,
53 darkness=0.7,
54 vmax=None,
55 vmin=None,
56):
57 """Prepare info for plotting one surface map on a single mesh.
59 This computes the dictionary that gets inserted in the web page,
60 which contains the encoded mesh, colors, min and max values, and
61 background color.
63 """
64 colors = colorscale(
65 cmap,
66 surf_map,
67 threshold,
68 symmetric_cmap=symmetric_cmap,
69 vmax=vmax,
70 vmin=vmin,
71 )
72 info = {"inflated_both": mesh_to_plotly(surf_mesh)}
73 backend = get_surface_backend(DEFAULT_ENGINE)
74 info["vertexcolor_both"] = backend._get_vertexcolor(
75 surf_map,
76 colors["cmap"],
77 colors["norm"],
78 absolute_threshold=colors["abs_threshold"],
79 bg_map=bg_map,
80 bg_on_data=bg_on_data,
81 darkness=darkness,
82 )
83 info["cmin"], info["cmax"] = float(colors["vmin"]), float(colors["vmax"])
84 info["black_bg"] = black_bg
85 info["full_brain_mesh"] = False
86 info["colorscale"] = colors["colors"]
87 return info
90def one_mesh_info(
91 surf_map,
92 surf_mesh,
93 threshold=None,
94 cmap=DEFAULT_DIVERGING_CMAP,
95 black_bg=False,
96 bg_map=None,
97 symmetric_cmap=True,
98 bg_on_data=False,
99 darkness=0.7,
100 vmax=None,
101 vmin=None,
102):
103 """Deprecate public function. See _one_mesh_info."""
104 warn(
105 category=DeprecationWarning,
106 message="one_mesh_info is a private function and is renamed "
107 "to _one_mesh_info. Using the deprecated name will "
108 "raise an error in release 0.13",
109 stacklevel=find_stack_level(),
110 )
112 return _one_mesh_info(
113 surf_map,
114 surf_mesh,
115 threshold=threshold,
116 cmap=cmap,
117 black_bg=black_bg,
118 bg_map=bg_map,
119 symmetric_cmap=symmetric_cmap,
120 bg_on_data=bg_on_data,
121 darkness=darkness,
122 vmax=vmax,
123 vmin=vmin,
124 )
127def _get_combined_curvature_map(mesh_left, mesh_right):
128 """Get combined curvature map from left and right hemisphere maps.
129 Only used in _full_brain_info.
130 """
131 curv_left = load_surf_data(mesh_left)
132 curv_right = load_surf_data(mesh_right)
133 curv_left_sign = np.sign(curv_left)
134 curv_right_sign = np.sign(curv_right)
135 curv_left_sign[np.isnan(curv_left)] = 0
136 curv_right_sign[np.isnan(curv_right)] = 0
137 curv_combined = np.concatenate([curv_left_sign, curv_right_sign])
138 return curv_combined
141def _full_brain_info(
142 volume_img,
143 mesh="fsaverage5",
144 threshold=None,
145 cmap=DEFAULT_DIVERGING_CMAP,
146 black_bg=False,
147 symmetric_cmap=True,
148 bg_on_data=False,
149 darkness=0.7,
150 vmax=None,
151 vmin=None,
152 vol_to_surf_kwargs=None,
153):
154 """Project 3D map on cortex; prepare info to plot both hemispheres.
156 This computes the dictionary that gets inserted in the web page,
157 which contains encoded meshes, colors, min and max values, and
158 background color.
160 """
161 if vol_to_surf_kwargs is None:
162 vol_to_surf_kwargs = {}
163 info = {}
164 mesh = check_mesh_is_fsaverage(mesh)
165 surface_maps = SurfaceImage.from_volume(
166 mesh=PolyMesh(
167 left=mesh["pial_left"],
168 right=mesh["pial_right"],
169 ),
170 volume_img=volume_img,
171 inner_mesh=PolyMesh(
172 left=mesh.get("white_left", None),
173 right=mesh.get("white_right", None),
174 ),
175 **vol_to_surf_kwargs,
176 )
177 colors = colorscale(
178 cmap,
179 get_data(surface_maps).ravel(),
180 threshold,
181 symmetric_cmap=symmetric_cmap,
182 vmax=vmax,
183 vmin=vmin,
184 )
186 for hemi, surf_map in surface_maps.data.parts.items():
187 curv_map = load_surf_data(mesh[f"curv_{hemi}"])
188 bg_map = np.sign(curv_map)
190 info[f"pial_{hemi}"] = mesh_to_plotly(mesh[f"pial_{hemi}"])
191 info[f"inflated_{hemi}"] = mesh_to_plotly(mesh[f"infl_{hemi}"])
193 backend = get_surface_backend(DEFAULT_ENGINE)
194 info[f"vertexcolor_{hemi}"] = backend._get_vertexcolor(
195 surf_map,
196 colors["cmap"],
197 colors["norm"],
198 absolute_threshold=colors["abs_threshold"],
199 bg_map=bg_map,
200 bg_on_data=bg_on_data,
201 darkness=darkness,
202 )
204 # also add info for both hemispheres
205 for mesh_type in ["infl", "pial"]:
206 if mesh_type == "infl":
207 info["inflated_both"] = mesh_to_plotly(
208 combine_hemispheres_meshes(
209 PolyMesh(
210 left=mesh[f"{mesh_type}_left"],
211 right=mesh[f"{mesh_type}_right"],
212 )
213 )
214 )
215 else:
216 info[f"{mesh_type}_both"] = mesh_to_plotly(
217 combine_hemispheres_meshes(
218 PolyMesh(
219 left=mesh[f"{mesh_type}_left"],
220 right=mesh[f"{mesh_type}_right"],
221 )
222 )
223 )
224 backend = get_surface_backend(DEFAULT_ENGINE)
225 info["vertexcolor_both"] = backend._get_vertexcolor(
226 get_data(surface_maps),
227 colors["cmap"],
228 colors["norm"],
229 absolute_threshold=colors["abs_threshold"],
230 bg_map=_get_combined_curvature_map(
231 mesh["curv_left"], mesh["curv_right"]
232 ),
233 bg_on_data=bg_on_data,
234 darkness=darkness,
235 )
236 info["cmin"], info["cmax"] = float(colors["vmin"]), float(colors["vmax"])
237 info["black_bg"] = black_bg
238 info["full_brain_mesh"] = True
239 info["colorscale"] = colors["colors"]
240 return info
243def full_brain_info(
244 volume_img,
245 mesh="fsaverage5",
246 threshold=None,
247 cmap=DEFAULT_DIVERGING_CMAP,
248 black_bg=False,
249 symmetric_cmap=True,
250 bg_on_data=False,
251 darkness=0.7,
252 vmax=None,
253 vmin=None,
254 vol_to_surf_kwargs=None,
255):
256 """Deprecate public function. See _full_brain_info."""
257 warn(
258 category=DeprecationWarning,
259 message="full_brain_info is a private function and is renamed to "
260 "_full_brain_info. Using the deprecated name will raise an error "
261 "in release 0.13",
262 stacklevel=find_stack_level(),
263 )
265 return _full_brain_info(
266 volume_img,
267 mesh=mesh,
268 threshold=threshold,
269 cmap=cmap,
270 black_bg=black_bg,
271 symmetric_cmap=symmetric_cmap,
272 bg_on_data=bg_on_data,
273 darkness=darkness,
274 vmax=vmax,
275 vmin=vmin,
276 vol_to_surf_kwargs=vol_to_surf_kwargs,
277 )
280def _fill_html_template(info, embed_js=True):
281 as_json = json.dumps(info)
282 as_html = get_html_template("surface_plot_template.html").safe_substitute(
283 {
284 "INSERT_STAT_MAP_JSON_HERE": as_json,
285 "INSERT_PAGE_TITLE_HERE": info["title"] or "Surface plot",
286 }
287 )
288 as_html = add_js_lib(as_html, embed_js=embed_js)
289 return SurfaceView(as_html)
292@fill_doc
293def view_img_on_surf(
294 stat_map_img,
295 surf_mesh="fsaverage5",
296 threshold=None,
297 cmap=DEFAULT_DIVERGING_CMAP,
298 black_bg=False,
299 vmax=None,
300 vmin=None,
301 symmetric_cmap=True,
302 bg_on_data=False,
303 darkness=0.7,
304 colorbar=True,
305 colorbar_height=0.5,
306 colorbar_fontsize=25,
307 title=None,
308 title_fontsize=25,
309 vol_to_surf_kwargs=None,
310):
311 """Insert a surface plot of a statistical map into an HTML page.
313 Parameters
314 ----------
315 stat_map_img : Niimg-like object, 3D
316 See :ref:`extracting_data`.
318 surf_mesh : :obj:`str` or :obj:`dict`, default='fsaverage5'
319 If a string, it should be one of the following values:
320 %(fsaverage_options)s
321 If a dictionary, it should have the same structure as those returned by
322 nilearn.datasets.fetch_surf_fsaverage, i.e. keys should be 'infl_left',
323 'pial_left', 'sulc_left', 'infl_right', 'pial_right', and 'sulc_right',
324 containing inflated and pial meshes, and sulcal depth values for left
325 and right hemispheres.
327 threshold : :obj:`str`, number or None, default=None
328 If None, no thresholding.
329 If it is a number only values of amplitude greater
330 than threshold will be shown.
331 If it is a string it must finish with a percent sign,
332 e.g. "25.3%%", and only values of amplitude above the
333 given percentile will be shown.
335 %(cmap)s
336 default="RdBu_r"
338 black_bg : :obj:`bool`, default=False
339 If True, image is plotted on a black background. Otherwise on a
340 white background.
342 %(bg_on_data)s
344 %(darkness)s
345 Default=1.
347 vmax : :obj:`float` or None, default=None
348 upper bound for the colorbar. if None, use the absolute max of the
349 brain map.
351 vmin : :obj:`float` or None, default=None
352 min value for mapping colors.
353 If `symmetric_cmap` is `True`, `vmin` is always equal to `-vmax` and
354 cannot be chosen.
355 If `symmetric_cmap` is `False`, `vmin` is equal to the min of the
356 image, or 0 when a threshold is used.
358 symmetric_cmap : :obj:`bool`, default=True
359 Make colormap symmetric (ranging from -vmax to vmax).
360 You can set it to False if you are plotting only positive values.
362 colorbar : :obj:`bool`, default=True
363 Add a colorbar or not.
365 colorbar_height : :obj:`float`, default=0.5
366 Height of the colorbar, relative to the figure height
368 colorbar_fontsize : :obj:`int`, default=25
369 Fontsize of the colorbar tick labels.
371 title : :obj:`str`, default=None
372 Title for the plot.
374 title_fontsize : :obj:`int`, default=25
375 Fontsize of the title.
377 vol_to_surf_kwargs : :obj:`dict`, default=None
378 Dictionary of keyword arguments that are passed on to
379 :func:`nilearn.surface.vol_to_surf` when extracting a surface from
380 the input image. See the function documentation for details.This
381 parameter is especially useful when plotting an atlas. See
382 https://nilearn.github.io/stable/auto_examples/01_plotting/plot_3d_map_to_surface_projection.html
383 Will default to ``{}`` if ``None`` is passed.
385 Returns
386 -------
387 SurfaceView : plot of the stat map.
388 It can be saved as an html page or rendered (transparently) by the
389 Jupyter notebook. Useful methods are :
391 - 'resize' to resize the plot displayed in a Jupyter notebook
392 - 'save_as_html' to save the plot to a file
393 - 'open_in_browser' to save the plot and open it in a web browser.
395 See Also
396 --------
397 nilearn.plotting.view_surf: plot from a surface map on a cortical mesh.
399 """
400 if vol_to_surf_kwargs is None:
401 vol_to_surf_kwargs = {}
402 stat_map_img = check_niimg_3d(stat_map_img)
403 info = _full_brain_info(
404 volume_img=stat_map_img,
405 mesh=surf_mesh,
406 threshold=threshold,
407 cmap=cmap,
408 black_bg=black_bg,
409 vmax=vmax,
410 vmin=vmin,
411 bg_on_data=bg_on_data,
412 darkness=darkness,
413 symmetric_cmap=symmetric_cmap,
414 vol_to_surf_kwargs=vol_to_surf_kwargs,
415 )
416 info["colorbar"] = colorbar
417 info["cbar_height"] = colorbar_height
418 info["cbar_fontsize"] = colorbar_fontsize
419 info["title"] = title
420 info["title_fontsize"] = title_fontsize
421 return _fill_html_template(info, embed_js=True)
424@fill_doc
425def view_surf(
426 surf_mesh=None,
427 surf_map=None,
428 bg_map=None,
429 hemi=DEFAULT_HEMI,
430 threshold=None,
431 cmap=DEFAULT_DIVERGING_CMAP,
432 black_bg=False,
433 vmax=None,
434 vmin=None,
435 bg_on_data=False,
436 darkness=0.7,
437 symmetric_cmap=True,
438 colorbar=True,
439 colorbar_height=0.5,
440 colorbar_fontsize=25,
441 title=None,
442 title_fontsize=25,
443):
444 """Insert a surface plot of a surface map into an HTML page.
446 Parameters
447 ----------
448 %(surf_mesh)s
449 If None is passed, then ``surf_map`` must be a
450 :obj:`~nilearn.surface.SurfaceImage` instance and the mesh from that
451 :obj:`~nilearn.surface.SurfaceImage` instance will be used.
453 surf_map : :obj:`str` or :class:`numpy.ndarray`, \
454 or :obj:`~nilearn.surface.SurfaceImage` or None, \
455 default=None
456 Data to be displayed on the surface :term:`mesh`.
457 Can be a file (valid formats are .gii, .mgz, .nii, .nii.gz,
458 or Freesurfer specific files such as
459 .thickness, .area, .curv, .sulc, .annot, .label) or
460 a Numpy array.
461 If None is passed for ``surf_mesh``
462 then ``surf_map``
463 must be a :obj:`~nilearn.surface.SurfaceImage` instance
464 and its the mesh will be used for plotting.
466 %(bg_map)s
468 %(hemi)s
469 It is only used if ``surf_map`` is :obj:`~nilearn.surface.SurfaceImage`
470 and / or ``surf_mesh`` is :obj:`~nilearn.surface.PolyMesh`.
471 Otherwise a warning will be displayed.
473 .. versionadded:: 0.11.0
475 %(bg_on_data)s
477 %(darkness)s
478 Default=1.
480 threshold : :obj:`str`, number or None, default=None
481 If None, no thresholding.
482 If it is a number only values of amplitude greater
483 than threshold will be shown.
484 If it is a string it must finish with a percent sign,
485 e.g. "25.3%%", and only values of amplitude above the
486 given percentile will be shown.
488 %(cmap)s
489 default="RdBu_r"
491 black_bg : :obj:`bool`, default=False
492 If True, image is plotted on a black background. Otherwise on a
493 white background.
495 symmetric_cmap : :obj:`bool`, default=True
496 Make colormap symmetric (ranging from -vmax to vmax).
497 Set it to False if you are plotting a surface atlas.
499 vmax : :obj:`float` or None, default=None
500 upper bound for the colorbar. if None, use the absolute max of the
501 brain map.
503 vmin : :obj:`float` or None, default=None
504 min value for mapping colors.
505 If `symmetric_cmap` is `True`, `vmin` is always equal to `-vmax` and
506 cannot be chosen.
507 If `symmetric_cmap` is `False`, `vmin` defaults to the min of the
508 image, or 0 when a threshold is used.
510 colorbar : :obj:`bool`, default=True
511 Add a colorbar or not.
513 colorbar_height : :obj:`float`, default=0.5
514 Height of the colorbar, relative to the figure height.
516 colorbar_fontsize : :obj:`int`, default=25
517 Fontsize of the colorbar tick labels.
519 title : :obj:`str`, default=None
520 Title for the plot.
522 title_fontsize : :obj:`int`, default=25
523 Fontsize of the title.
525 Returns
526 -------
527 SurfaceView : plot of the stat map.
528 It can be saved as an html page or rendered (transparently) by the
529 Jupyter notebook. Useful methods are :
531 - 'resize' to resize the plot displayed in a Jupyter notebook
532 - 'save_as_html' to save the plot to a file
533 - 'open_in_browser' to save the plot and open it in a web browser.
535 See Also
536 --------
537 nilearn.plotting.view_img_on_surf: Surface plot from a 3D statistical map.
538 """
539 check_params(locals())
540 surf_map, surf_mesh, bg_map = check_surface_plotting_inputs(
541 surf_map, surf_mesh, hemi, bg_map, map_var_name="surf_map"
542 )
544 surf_mesh = load_surf_mesh(surf_mesh)
545 if surf_map is None:
546 surf_map = np.ones(len(surf_mesh[0]))
547 else:
548 surf_mesh, surf_map = check_mesh_and_data(surf_mesh, surf_map)
549 if bg_map is not None:
550 _, bg_map = check_mesh_and_data(surf_mesh, bg_map)
551 info = _one_mesh_info(
552 surf_map=surf_map,
553 surf_mesh=surf_mesh,
554 threshold=threshold,
555 cmap=cmap,
556 black_bg=black_bg,
557 bg_map=bg_map,
558 bg_on_data=bg_on_data,
559 darkness=darkness,
560 symmetric_cmap=symmetric_cmap,
561 vmax=vmax,
562 vmin=vmin,
563 )
564 info["colorbar"] = colorbar
565 info["cbar_height"] = colorbar_height
566 info["cbar_fontsize"] = colorbar_fontsize
567 info["title"] = title
568 info["title_fontsize"] = title_fontsize
569 return _fill_html_template(info, embed_js=True)