Skip to content

visualizer

tit.plotting.nilearn.visualizer

TI-Toolbox Visualization Module Provides comprehensive visualization tools for electric field distributions from TES simulations.

Features: 1. Multiple surface views (lateral, medial, left/right hemispheres) saved as high-res PDF 2. Interactive 3D visualization saved as HTML using plotly 3. Atlas contour overlays for anatomical context 4. Support for both subject-specific and MNI space visualizations

Based on: https://nilearn.github.io/dev/auto_examples/01_plotting/plot_3d_map_to_surface_projection.html

NilearnVisualizer

NilearnVisualizer(subject_id: str | None = None)

Main visualization class for electric field distributions.

Provides methods for creating both static PDF visualizations and interactive HTML plots. Uses PathManager for consistent path handling and supports multiple atlas overlays.

Parameters

subject_id : str or None, optional Subject identifier. When None, PathManager auto-detection is used instead.

Attributes

pm : PathManager Resolved path-manager instance. subject_id : str or None Subject identifier passed at construction time. output_dir : str Directory where visualizations are saved.

See Also

tit.plotting.nilearn.img_slices : Convenience entry-points for PDF creation. tit.plotting.nilearn.img_glass : Convenience entry-points for glass-brain PNGs. tit.plotting.nilearn.html_report : Convenience entry-point for HTML reports.

Create a new visualizer instance.

Parameters

subject_id : str or None, optional Subject identifier. When None, PathManager auto-detection is used instead.

Source code in tit/plotting/nilearn/visualizer.py
def __init__(self, subject_id: str | None = None):
    """Create a new visualizer instance.

    Parameters
    ----------
    subject_id : str or None, optional
        Subject identifier.  When *None*, PathManager auto-detection
        is used instead.
    """
    self.pm = get_path_manager()
    self.subject_id = subject_id
    self.output_dir = None

    # Set up output directory
    self._setup_output_directory()

create_pdf_visualization

create_pdf_visualization(subject_id: str, simulation_name: str, min_cutoff: float = 0.3, max_cutoff: float = None, atlas_name: str = 'harvard_oxford_sub', selected_regions: list[int] | None = None) -> str | None

Create a multi-slice PDF with atlas contours for a single subject.

Generates sagittal, coronal, and axial slice rows with the electric field overlaid as a hot colormap and optional atlas contour lines.

Parameters

subject_id : str Subject identifier (e.g. '070'). simulation_name : str Name of the simulation folder. min_cutoff : float, optional Lower display threshold in V/m. Default is 0.3. max_cutoff : float or None, optional Upper display threshold in V/m. When None the 99.9th percentile of non-zero voxels is used. atlas_name : str, optional Nilearn atlas key. One of 'harvard_oxford', 'harvard_oxford_sub', 'aal', 'schaefer_2018'. selected_regions : list of int or None, optional 0-indexed region indices to include in the atlas overlay. When None, all regions are shown.

Returns

str or None Path to the saved PDF, or None on failure.

Source code in tit/plotting/nilearn/visualizer.py
def create_pdf_visualization(
    self,
    subject_id: str,
    simulation_name: str,
    min_cutoff: float = 0.3,
    max_cutoff: float = None,
    atlas_name: str = "harvard_oxford_sub",
    selected_regions: list[int] | None = None,
) -> str | None:
    """Create a multi-slice PDF with atlas contours for a single subject.

    Generates sagittal, coronal, and axial slice rows with the electric
    field overlaid as a hot colormap and optional atlas contour lines.

    Parameters
    ----------
    subject_id : str
        Subject identifier (e.g. ``'070'``).
    simulation_name : str
        Name of the simulation folder.
    min_cutoff : float, optional
        Lower display threshold in V/m.  Default is ``0.3``.
    max_cutoff : float or None, optional
        Upper display threshold in V/m.  When *None* the 99.9th
        percentile of non-zero voxels is used.
    atlas_name : str, optional
        Nilearn atlas key.  One of ``'harvard_oxford'``,
        ``'harvard_oxford_sub'``, ``'aal'``, ``'schaefer_2018'``.
    selected_regions : list of int or None, optional
        0-indexed region indices to include in the atlas overlay.
        When *None*, all regions are shown.

    Returns
    -------
    str or None
        Path to the saved PDF, or *None* on failure.
    """
    print(f"=== Creating PDF Visualization for {subject_id}/{simulation_name} ===")

    # Get simulation files
    sim_files = self._get_simulation_files(subject_id)
    if simulation_name not in sim_files:
        print(
            f"Error: Simulation '{simulation_name}' not found for subject {subject_id}"
        )
        return None

    ef_filepath = sim_files[simulation_name]
    ef_img = self._load_electric_field_data(ef_filepath)
    if ef_img is None:
        return None

    # Load and analyze data
    data = ef_img.get_fdata()
    data_nonzero = data[data > 0]
    if len(data_nonzero) == 0:
        print("Warning: No non-zero field values found")
        return None

    max_value = np.max(data)
    percentile_999 = np.percentile(data_nonzero, 99.9)
    min_value = np.min(data_nonzero)

    # Use provided max_cutoff or default to 99.9th percentile
    if max_cutoff is None:
        max_cutoff = percentile_999

    print(f"Electric field statistics:")
    print(f"  Absolute maximum: {max_value:.2f} V/m")
    print(f"  99.9th percentile: {percentile_999:.2f} V/m")
    print(f"  Minimum (non-zero): {min_value:.2f} V/m")
    print(f"  Visualization range: {min_cutoff:.2f} - {max_cutoff:.2f} V/m")

    # Create output filename
    pdf_filename = f"{subject_id}_{simulation_name}_multiple_views.pdf"
    pdf_filepath = os.path.join(self.output_dir, pdf_filename)

    # Load atlas for contours
    atlas_img, atlas_display_name = self._load_atlas(atlas_name, selected_regions)

    # Create multi-slice plot with atlas contours
    self._create_multi_slice_plot_with_atlas(
        ef_img, atlas_img, atlas_display_name, pdf_filepath, min_cutoff, max_cutoff
    )

    print(f"✓ Saved: {pdf_filepath}")
    print(f"  Colorbar range: {min_cutoff:.2f} - {max_cutoff:.2f} V/m")

    return pdf_filepath

create_html_visualization

create_html_visualization(subject_id: str, simulation_name: str, min_cutoff: float = 0.3) -> str | None

Create an interactive HTML surface visualization.

Uses nilearn.plotting.view_img_on_surf to project the electric-field map onto the cortical surface and saves the result as a self-contained HTML file.

Parameters

subject_id : str Subject identifier. simulation_name : str Name of the simulation folder. min_cutoff : float, optional Lower display threshold in V/m. Default is 0.3.

Returns

str or None Path to the saved HTML file, or None on failure.

Source code in tit/plotting/nilearn/visualizer.py
def create_html_visualization(
    self, subject_id: str, simulation_name: str, min_cutoff: float = 0.3
) -> str | None:
    """Create an interactive HTML surface visualization.

    Uses ``nilearn.plotting.view_img_on_surf`` to project the
    electric-field map onto the cortical surface and saves the result
    as a self-contained HTML file.

    Parameters
    ----------
    subject_id : str
        Subject identifier.
    simulation_name : str
        Name of the simulation folder.
    min_cutoff : float, optional
        Lower display threshold in V/m.  Default is ``0.3``.

    Returns
    -------
    str or None
        Path to the saved HTML file, or *None* on failure.
    """
    print(f"=== Creating HTML Visualization for {subject_id}/{simulation_name} ===")

    # Get simulation files
    sim_files = self._get_simulation_files(subject_id)
    if simulation_name not in sim_files:
        print(
            f"Error: Simulation '{simulation_name}' not found for subject {subject_id}"
        )
        return None

    ef_filepath = sim_files[simulation_name]
    ef_img = self._load_electric_field_data(ef_filepath)
    if ef_img is None:
        return None

    # Analyze data for thresholds
    data = ef_img.get_fdata()
    data_nonzero = data[data > 0]
    if len(data_nonzero) == 0:
        print("Warning: No non-zero field values found")
        return None

    percentile_999 = np.percentile(data_nonzero, 99.9)

    # Create output filename
    html_filename = f"{subject_id}_{simulation_name}_interactive.html"
    html_filepath = os.path.join(self.output_dir, html_filename)

    # Create interactive visualization
    view = view_img_on_surf(
        stat_map=ef_img,
        threshold=min_cutoff,
        vmax=percentile_999,
        cmap="hot",
        symmetric_cmap=False,
        bg_on_data=True,
        title=f"Electric Field - {subject_id}/{simulation_name}",
    )

    # Save as HTML
    view.save_as_html(html_filepath)
    print(f"✓ Saved interactive HTML: {html_filepath}")

    return html_filepath

create_pdf_visualization_group

create_pdf_visualization_group(averaged_img, base_filename: str, output_dir: str, min_cutoff: float = 0.3, max_cutoff: float = None, atlas_name: str = 'harvard_oxford_sub', selected_regions: list[int] | None = None) -> str | None

Create a multi-slice PDF from a pre-averaged group NIfTI image.

Identical layout to :meth:create_pdf_visualization but accepts an already-averaged nibabel.Nifti1Image instead of looking up a per-subject simulation file.

Parameters

averaged_img : nibabel.Nifti1Image Pre-averaged electric-field image. base_filename : str Base filename for output (without extension). output_dir : str Directory where the PDF will be saved. min_cutoff : float, optional Lower display threshold in V/m. Default is 0.3. max_cutoff : float or None, optional Upper display threshold in V/m. When None the 99.9th percentile of non-zero voxels is used. atlas_name : str, optional Nilearn atlas key. selected_regions : list of int or None, optional 0-indexed region indices for the atlas overlay.

Returns

str or None Path to the saved PDF, or None on failure.

Source code in tit/plotting/nilearn/visualizer.py
def create_pdf_visualization_group(
    self,
    averaged_img,
    base_filename: str,
    output_dir: str,
    min_cutoff: float = 0.3,
    max_cutoff: float = None,
    atlas_name: str = "harvard_oxford_sub",
    selected_regions: list[int] | None = None,
) -> str | None:
    """Create a multi-slice PDF from a pre-averaged group NIfTI image.

    Identical layout to :meth:`create_pdf_visualization` but accepts
    an already-averaged ``nibabel.Nifti1Image`` instead of looking up
    a per-subject simulation file.

    Parameters
    ----------
    averaged_img : nibabel.Nifti1Image
        Pre-averaged electric-field image.
    base_filename : str
        Base filename for output (without extension).
    output_dir : str
        Directory where the PDF will be saved.
    min_cutoff : float, optional
        Lower display threshold in V/m.  Default is ``0.3``.
    max_cutoff : float or None, optional
        Upper display threshold in V/m.  When *None* the 99.9th
        percentile of non-zero voxels is used.
    atlas_name : str, optional
        Nilearn atlas key.
    selected_regions : list of int or None, optional
        0-indexed region indices for the atlas overlay.

    Returns
    -------
    str or None
        Path to the saved PDF, or *None* on failure.
    """
    print(f"=== Creating PDF Visualization for Group Averaged Data ===")

    # Load and analyze data
    data = averaged_img.get_fdata()
    data_nonzero = data[data > 0]
    if len(data_nonzero) == 0:
        print("Warning: No non-zero field values found")
        return None

    max_value = np.max(data)
    percentile_999 = np.percentile(data_nonzero, 99.9)
    min_value = np.min(data_nonzero)

    # Use provided max_cutoff or default to 99.9th percentile
    if max_cutoff is None:
        max_cutoff = percentile_999

    print(f"Electric field statistics (averaged data):")
    print(f"  Absolute maximum: {max_value:.2f} V/m")
    print(f"  99.9th percentile: {percentile_999:.2f} V/m")
    print(f"  Minimum (non-zero): {min_value:.2f} V/m")
    print(f"  Visualization range: {min_cutoff:.2f} - {max_cutoff:.2f} V/m")

    # Create output filename
    pdf_filename = f"{base_filename}_multiple_views.pdf"
    pdf_filepath = os.path.join(output_dir, pdf_filename)

    # Load atlas for contours
    atlas_img, atlas_display_name = self._load_atlas(atlas_name, selected_regions)

    # Create multi-slice plot with atlas contours
    self._create_multi_slice_plot_with_atlas(
        averaged_img,
        atlas_img,
        atlas_display_name,
        pdf_filepath,
        min_cutoff,
        max_cutoff,
    )

    print(f"✓ Saved: {pdf_filepath}")
    print(f"  Colorbar range: {min_cutoff:.2f} - {max_cutoff:.2f} V/m")

    return pdf_filepath

create_glass_brain_visualization

create_glass_brain_visualization(subject_id: str, simulation_name: str, min_cutoff: float = 0.3, max_cutoff: float = None, cmap: str = 'hot') -> str | None

Create a glass-brain PNG for a single subject simulation.

Uses nilearn.plotting.plot_glass_brain to render a maximum-intensity projection of the electric-field map.

Parameters

subject_id : str Subject identifier. simulation_name : str Name of the simulation folder. min_cutoff : float, optional Lower display threshold in V/m. Default is 0.3. max_cutoff : float or None, optional Upper display threshold in V/m. When None the 99.9th percentile of non-zero voxels is used. cmap : str, optional Matplotlib colormap name. Default is 'hot'.

Returns

str or None Path to the saved PNG file, or None on failure.

Source code in tit/plotting/nilearn/visualizer.py
def create_glass_brain_visualization(
    self,
    subject_id: str,
    simulation_name: str,
    min_cutoff: float = 0.3,
    max_cutoff: float = None,
    cmap: str = "hot",
) -> str | None:
    """Create a glass-brain PNG for a single subject simulation.

    Uses ``nilearn.plotting.plot_glass_brain`` to render a
    maximum-intensity projection of the electric-field map.

    Parameters
    ----------
    subject_id : str
        Subject identifier.
    simulation_name : str
        Name of the simulation folder.
    min_cutoff : float, optional
        Lower display threshold in V/m.  Default is ``0.3``.
    max_cutoff : float or None, optional
        Upper display threshold in V/m.  When *None* the 99.9th
        percentile of non-zero voxels is used.
    cmap : str, optional
        Matplotlib colormap name.  Default is ``'hot'``.

    Returns
    -------
    str or None
        Path to the saved PNG file, or *None* on failure.
    """
    print(
        f"=== Creating Glass Brain Visualization for {subject_id}/{simulation_name} ==="
    )

    # Get simulation files
    sim_files = self._get_simulation_files(subject_id)
    if simulation_name not in sim_files:
        print(
            f"Error: Simulation '{simulation_name}' not found for subject {subject_id}"
        )
        return None

    ef_filepath = sim_files[simulation_name]
    ef_img = self._load_electric_field_data(ef_filepath)
    if ef_img is None:
        return None

    # Load and analyze data
    data = ef_img.get_fdata()
    data_nonzero = data[data > 0]
    if len(data_nonzero) == 0:
        print("Warning: No non-zero field values found")
        return None

    max_value = np.max(data)
    percentile_999 = np.percentile(data_nonzero, 99.9)
    min_value = np.min(data_nonzero)

    # Use provided max_cutoff or default to 99.9th percentile
    if max_cutoff is None:
        max_cutoff = percentile_999

    print(f"Electric field statistics:")
    print(f"  Absolute maximum: {max_value:.2f} V/m")
    print(f"  99.9th percentile: {percentile_999:.2f} V/m")
    print(f"  Minimum (non-zero): {min_value:.2f} V/m")
    print(f"  Visualization range: {min_cutoff:.2f} - {max_cutoff:.2f} V/m")

    # Create output filename
    png_filename = f"{subject_id}_{simulation_name}_glass_brain.png"
    png_filepath = os.path.join(self.output_dir, png_filename)

    # Create glass brain visualization
    plotting.plot_glass_brain(
        stat_map_img=ef_img,
        threshold=min_cutoff,
        vmax=max_cutoff,
        cmap=cmap,
        colorbar=True,
        plot_abs=False,
        symmetric_cbar=False,
        title=f"Electric Field - {subject_id}/{simulation_name}\n{min_cutoff:.2f}-{max_cutoff:.2f} V/m",
        output_file=png_filepath,
    )

    print(f"✓ Saved glass brain visualization: {png_filepath}")

    return png_filepath