Skip to content

images

tit.reporting.reportlets.images

Image-based reportlets for TI-Toolbox reports.

This module provides specialized reportlets for brain imaging visualizations, including multi-slice brain views and electrode montage displays.

SliceSeriesReportlet

SliceSeriesReportlet(title: str | None = None, slices: list[dict[str, Any]] | None = None, orientation: str = 'axial', caption: str | None = None)

Bases: BaseReportlet

Reportlet for displaying a series of brain slices.

Displays multiple slices (typically 7) across axial, sagittal, or coronal views, commonly used for QC visualizations.

Initialize the slice series reportlet.

Parameters:

Name Type Description Default
title str | None

Title for the slice series

None
slices list[dict[str, Any]] | None

List of slice data dicts with 'base64' and optional 'label'

None
orientation str

View orientation (axial, sagittal, coronal)

'axial'
caption str | None

Optional caption text

None
Source code in tit/reporting/reportlets/images.py
def __init__(
    self,
    title: str | None = None,
    slices: list[dict[str, Any]] | None = None,
    orientation: str = "axial",
    caption: str | None = None,
):
    """
    Initialize the slice series reportlet.

    Args:
        title: Title for the slice series
        slices: List of slice data dicts with 'base64' and optional 'label'
        orientation: View orientation (axial, sagittal, coronal)
        caption: Optional caption text
    """
    super().__init__(title)
    self.slices: list[dict[str, Any]] = slices or []
    self.orientation = orientation
    self.caption = caption

add_slice

add_slice(image_data: str | bytes | Path | Any, label: str | None = None, mime_type: str = 'image/png') -> None

Add a slice to the series.

Parameters:

Name Type Description Default
image_data str | bytes | Path | Any

Base64 string, bytes, path, or PIL Image

required
label str | None

Optional label for this slice

None
mime_type str

MIME type of the image

'image/png'
Source code in tit/reporting/reportlets/images.py
def add_slice(
    self,
    image_data: str | bytes | Path | Any,
    label: str | None = None,
    mime_type: str = "image/png",
) -> None:
    """
    Add a slice to the series.

    Args:
        image_data: Base64 string, bytes, path, or PIL Image
        label: Optional label for this slice
        mime_type: MIME type of the image
    """
    base64_data = self._process_image(image_data)
    self.slices.append(
        {
            "base64": base64_data,
            "label": label,
            "mime_type": mime_type,
        }
    )

load_from_files

load_from_files(file_paths: list[str | Path]) -> None

Load slices from a list of image files.

Parameters:

Name Type Description Default
file_paths list[str | Path]

List of paths to slice images

required
Source code in tit/reporting/reportlets/images.py
def load_from_files(self, file_paths: list[str | Path]) -> None:
    """
    Load slices from a list of image files.

    Args:
        file_paths: List of paths to slice images
    """
    for i, path in enumerate(file_paths):
        path = Path(path)
        if path.exists():
            self.add_slice(path, label=f"Slice {i + 1}")

render_html

render_html() -> str

Render the slice series as HTML.

Source code in tit/reporting/reportlets/images.py
def render_html(self) -> str:
    """Render the slice series as HTML."""
    if not self.slices:
        return f"""
        <div class="reportlet slice-series-reportlet" id="{self.reportlet_id}">
            <div class="image-placeholder">
                <em>No slices available</em>
            </div>
        </div>
        """

    slice_images = []
    for slice_data in self.slices:
        mime_type = slice_data.get("mime_type", "image/png")
        label = slice_data.get("label", "")
        label_html = f'<span class="slice-label">{label}</span>' if label else ""

        slice_images.append(f"""
            <div class="slice-image">
                <img src="data:{mime_type};base64,{slice_data["base64"]}"
                     alt="{label or 'Brain slice'}" />
                {label_html}
            </div>
            """)

    title_html = f"<h3>{self._title}</h3>" if self._title else ""
    caption_html = (
        f'<p class="series-caption">{self.caption}</p>' if self.caption else ""
    )

    return f"""
    <div class="reportlet slice-series-reportlet {self.orientation}" id="{self.reportlet_id}">
        {title_html}
        <div class="slice-series">
            {"".join(slice_images)}
        </div>
        {caption_html}
    </div>
    """

MontageImageReportlet

MontageImageReportlet(title: str | None = None, image_source: str | Path | bytes | Any | None = None, electrode_pairs: list[dict[str, Any]] | None = None, montage_name: str | None = None)

Bases: BaseReportlet

Reportlet for displaying electrode montage visualizations.

Shows electrode placement with labeled pairs and optional intensity annotations.

Initialize the montage image reportlet.

Parameters:

Name Type Description Default
title str | None

Title for the montage

None
image_source str | Path | bytes | Any | None

Montage image (path, bytes, or PIL Image)

None
electrode_pairs list[dict[str, Any]] | None

List of electrode pair configurations

None
montage_name str | None

Name of the montage

None
Source code in tit/reporting/reportlets/images.py
def __init__(
    self,
    title: str | None = None,
    image_source: str | Path | bytes | Any | None = None,
    electrode_pairs: list[dict[str, Any]] | None = None,
    montage_name: str | None = None,
):
    """
    Initialize the montage image reportlet.

    Args:
        title: Title for the montage
        image_source: Montage image (path, bytes, or PIL Image)
        electrode_pairs: List of electrode pair configurations
        montage_name: Name of the montage
    """
    super().__init__(title)
    self._base64_data: str | None = None
    self._mime_type: str = "image/png"
    self.electrode_pairs = electrode_pairs or []
    self.montage_name = montage_name

    if image_source is not None:
        self._load_image(image_source)

set_base64_data

set_base64_data(data: str, mime_type: str = 'image/png') -> None

Directly set base64 encoded image data.

Source code in tit/reporting/reportlets/images.py
def set_base64_data(self, data: str, mime_type: str = "image/png") -> None:
    """Directly set base64 encoded image data."""
    self._base64_data = data
    self._mime_type = mime_type

add_electrode_pair

add_electrode_pair(name: str, electrode1: str, electrode2: str, intensity: float | None = None) -> None

Add an electrode pair to the montage info.

Parameters:

Name Type Description Default
name str

Name of the pair (e.g., "Pair 1")

required
electrode1 str

First electrode position

required
electrode2 str

Second electrode position

required
intensity float | None

Optional current intensity

None
Source code in tit/reporting/reportlets/images.py
def add_electrode_pair(
    self,
    name: str,
    electrode1: str,
    electrode2: str,
    intensity: float | None = None,
) -> None:
    """
    Add an electrode pair to the montage info.

    Args:
        name: Name of the pair (e.g., "Pair 1")
        electrode1: First electrode position
        electrode2: Second electrode position
        intensity: Optional current intensity
    """
    self.electrode_pairs.append(
        {
            "name": name,
            "electrode1": electrode1,
            "electrode2": electrode2,
            "intensity": intensity,
        }
    )

render_html

render_html() -> str

Render the montage image as HTML.

Source code in tit/reporting/reportlets/images.py
def render_html(self) -> str:
    """Render the montage image as HTML."""
    title_html = (
        f"<h3>{self._title or self.montage_name or 'Electrode Montage'}</h3>"
    )

    # Electrode pairs table
    pairs_html = ""
    if self.electrode_pairs:
        rows = []
        for pair in self.electrode_pairs:
            intensity_value = pair.get("intensity")
            if intensity_value is None or intensity_value == "":
                intensity_str = "—"
            else:
                try:
                    intensity_str = f"{float(intensity_value):.2f} mA"
                except (TypeError, ValueError):
                    intensity_str = str(intensity_value)
            rows.append(f"""
                <tr>
                    <td>{pair.get("name", "")}</td>
                    <td>{pair.get("electrode1", "")}</td>
                    <td>{pair.get("electrode2", "")}</td>
                    <td>{intensity_str}</td>
                </tr>
                """)

        pairs_html = f"""
        <div class="electrode-pairs">
            <table class="data-table compact">
                <thead>
                    <tr>
                        <th>Pair</th>
                        <th>Electrode 1</th>
                        <th>Electrode 2</th>
                        <th>Intensity</th>
                    </tr>
                </thead>
                <tbody>
                    {"".join(rows)}
                </tbody>
            </table>
        </div>
        """

    # Image display
    image_html = ""
    if self._base64_data:
        image_html = f"""
        <figure class="montage-figure">
            <img src="data:{self._mime_type};base64,{self._base64_data}"
                 alt="{self.montage_name or 'Electrode montage'}"
                 class="report-image montage-image" />
        </figure>
        """
    else:
        image_html = """
        <div class="image-placeholder">
            <em>No montage image available</em>
        </div>
        """

    return f"""
    <div class="reportlet montage-reportlet" id="{self.reportlet_id}">
        {title_html}
        <div class="montage-content">
            {image_html}
            {pairs_html}
        </div>
    </div>
    """

MultiViewBrainReportlet

MultiViewBrainReportlet(title: str | None = None, axial_image: str | Path | bytes | None = None, sagittal_image: str | Path | bytes | None = None, coronal_image: str | Path | bytes | None = None, caption: str | None = None)

Bases: BaseReportlet

Reportlet for displaying brain images in multiple views.

Shows the same brain data in axial, sagittal, and coronal orientations side by side.

Initialize the multi-view brain reportlet.

Parameters:

Name Type Description Default
title str | None

Title for the visualization

None
axial_image str | Path | bytes | None

Axial view image

None
sagittal_image str | Path | bytes | None

Sagittal view image

None
coronal_image str | Path | bytes | None

Coronal view image

None
caption str | None

Optional caption

None
Source code in tit/reporting/reportlets/images.py
def __init__(
    self,
    title: str | None = None,
    axial_image: str | Path | bytes | None = None,
    sagittal_image: str | Path | bytes | None = None,
    coronal_image: str | Path | bytes | None = None,
    caption: str | None = None,
):
    """
    Initialize the multi-view brain reportlet.

    Args:
        title: Title for the visualization
        axial_image: Axial view image
        sagittal_image: Sagittal view image
        coronal_image: Coronal view image
        caption: Optional caption
    """
    super().__init__(title)
    self.views: dict[str, str | None] = {
        "axial": None,
        "sagittal": None,
        "coronal": None,
    }
    self.caption = caption

    if axial_image:
        self.set_view("axial", axial_image)
    if sagittal_image:
        self.set_view("sagittal", sagittal_image)
    if coronal_image:
        self.set_view("coronal", coronal_image)

set_view

set_view(view_name: str, image_data: str | Path | bytes | Any) -> None

Set an image for a specific view.

Parameters:

Name Type Description Default
view_name str

One of 'axial', 'sagittal', 'coronal'

required
image_data str | Path | bytes | Any

Image data (path, bytes, base64, or PIL Image)

required
Source code in tit/reporting/reportlets/images.py
def set_view(self, view_name: str, image_data: str | Path | bytes | Any) -> None:
    """
    Set an image for a specific view.

    Args:
        view_name: One of 'axial', 'sagittal', 'coronal'
        image_data: Image data (path, bytes, base64, or PIL Image)
    """
    if view_name not in self.views:
        raise ValueError(f"Invalid view name: {view_name}")

    if isinstance(image_data, (str, Path)):
        path = Path(image_data)
        if path.exists():
            with open(path, "rb") as f:
                self.views[view_name] = base64.b64encode(f.read()).decode("utf-8")
        else:
            # Assume already base64
            self.views[view_name] = str(image_data)
    elif isinstance(image_data, bytes):
        self.views[view_name] = base64.b64encode(image_data).decode("utf-8")
    else:
        # Assume PIL Image
        try:
            buffer = io.BytesIO()
            image_data.save(buffer, format="PNG")
            self.views[view_name] = base64.b64encode(buffer.getvalue()).decode(
                "utf-8"
            )
        except (AttributeError, ValueError):
            pass

render_html

render_html() -> str

Render the multi-view visualization as HTML.

Source code in tit/reporting/reportlets/images.py
def render_html(self) -> str:
    """Render the multi-view visualization as HTML."""
    title_html = f"<h3>{self._title}</h3>" if self._title else ""

    view_panels = []
    for view_name, base64_data in self.views.items():
        if base64_data:
            view_panels.append(f"""
                <div class="view-panel {view_name}">
                    <div class="view-label">{view_name.capitalize()}</div>
                    <img src="data:image/png;base64,{base64_data}"
                         alt="{view_name} view"
                         class="view-image" />
                </div>
                """)
        else:
            view_panels.append(f"""
                <div class="view-panel {view_name}">
                    <div class="view-label">{view_name.capitalize()}</div>
                    <div class="view-placeholder">Not available</div>
                </div>
                """)

    caption_html = (
        f'<p class="multiview-caption">{self.caption}</p>' if self.caption else ""
    )

    return f"""
    <div class="reportlet multiview-reportlet" id="{self.reportlet_id}">
        {title_html}
        <div class="multiview-grid">
            {"".join(view_panels)}
        </div>
        {caption_html}
    </div>
    """