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.

Initialize the visualizer.

Parameters:

Name Type Description Default
subject_id str | None

Subject ID (optional, will use PathManager detection if not provided)

None
Source code in tit/plotting/nilearn/visualizer.py
def __init__(self, subject_id: str | None = None):
    """
    Initialize the visualizer.

    Args:
        subject_id: Subject ID (optional, will use PathManager detection if not provided)
    """
    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 PDF visualization with multiple surface views and atlas contours.

Parameters:

Name Type Description Default
subject_id str

Subject ID

required
simulation_name str

Name of the simulation

required
min_cutoff float

Minimum cutoff for visualization (V/m)

0.3
max_cutoff float

Maximum cutoff for visualization (V/m), if None uses 99.9th percentile

None
atlas_name str

Name of atlas to overlay

'harvard_oxford_sub'
selected_regions list[int] | None

List of region indices to include (0-indexed), if None includes all

None

Returns:

Type Description
str | None

Path to saved PDF file or None if failed

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 PDF visualization with multiple surface views and atlas contours.

    Args:
        subject_id: Subject ID
        simulation_name: Name of the simulation
        min_cutoff: Minimum cutoff for visualization (V/m)
        max_cutoff: Maximum cutoff for visualization (V/m), if None uses 99.9th percentile
        atlas_name: Name of atlas to overlay
        selected_regions: List of region indices to include (0-indexed), if None includes all

    Returns:
        Path to saved PDF file or None if failed
    """
    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 interactive HTML visualization.

Parameters:

Name Type Description Default
subject_id str

Subject ID

required
simulation_name str

Name of the simulation

required
min_cutoff float

Minimum cutoff for visualization (V/m)

0.3

Returns:

Type Description
str | None

Path to saved HTML file or None if failed

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 interactive HTML visualization.

    Args:
        subject_id: Subject ID
        simulation_name: Name of the simulation
        min_cutoff: Minimum cutoff for visualization (V/m)

    Returns:
        Path to saved HTML file or None if failed
    """
    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 PDF visualization with pre-averaged nifti data.

Parameters:

Name Type Description Default
averaged_img

Pre-averaged nibabel Nifti1Image

required
base_filename str

Base filename for output (without extension)

required
output_dir str

Output directory path

required
min_cutoff float

Minimum cutoff for visualization (V/m)

0.3
max_cutoff float

Maximum cutoff for visualization (V/m), if None uses 99.9th percentile

None
atlas_name str

Name of atlas to overlay

'harvard_oxford_sub'
selected_regions list[int] | None

List of region indices to include (0-indexed), if None includes all

None

Returns:

Type Description
str | None

Path to saved PDF file or None if failed

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 PDF visualization with pre-averaged nifti data.

    Args:
        averaged_img: Pre-averaged nibabel Nifti1Image
        base_filename: Base filename for output (without extension)
        output_dir: Output directory path
        min_cutoff: Minimum cutoff for visualization (V/m)
        max_cutoff: Maximum cutoff for visualization (V/m), if None uses 99.9th percentile
        atlas_name: Name of atlas to overlay
        selected_regions: List of region indices to include (0-indexed), if None includes all

    Returns:
        Path to saved PDF file or None if failed
    """
    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 glass brain visualization using nilearn's plot_glass_brain.

Parameters:

Name Type Description Default
subject_id str

Subject ID

required
simulation_name str

Name of the simulation

required
min_cutoff float

Minimum cutoff for visualization (V/m)

0.3
max_cutoff float

Maximum cutoff for visualization (V/m), if None uses 99.9th percentile

None
cmap str

Colormap name for visualization

'hot'

Returns:

Type Description
str | None

Path to saved PNG file or None if failed

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 glass brain visualization using nilearn's plot_glass_brain.

    Args:
        subject_id: Subject ID
        simulation_name: Name of the simulation
        min_cutoff: Minimum cutoff for visualization (V/m)
        max_cutoff: Maximum cutoff for visualization (V/m), if None uses 99.9th percentile
        cmap: Colormap name for visualization

    Returns:
        Path to saved PNG file or None if failed
    """
    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