.. _tutorial_notebook:

=======================
Interactive Tutorial
=======================

This page provides an interactive Jupyter notebook tutorial for learning MedTDA.

.. contents:: Contents
   :local:
   :depth: 2

Overview
========

The interactive tutorial notebook provides a hands-on introduction to MedTDA with real examples, visualizations, and explanations. It covers:

* Loading 3D (NIfTI) and 2D (JPEG/PNG) medical images
* Preprocessing: resampling, CT windowing, normalization, ROI cropping
* Computing persistence diagrams with ``BarcodeExtractor``
* Visualizing barcodes, persistence diagrams, Betti curves, landscapes, and more
* Comparing sublevel vs superlevel filtrations
* Feature extraction with ``FeatureExtractor`` and all vectorization methods

Tutorial Notebook
=================

The complete tutorial is available as a Jupyter notebook in the ``tutorials/`` directory of the repository.

**Location**: ``tutorials/medtda_tutorial.ipynb``

**Repository**: `MedTDA on GitHub <https://github.com/dashtiali/medtda>`_

Running the Tutorial
====================

Local Installation
------------------

1. **Clone the repository:**

   .. code-block:: bash

       git clone https://github.com/dashtiali/medtda.git
       cd medtda

2. **Install dependencies:**

   .. code-block:: bash

       pip install -e .
       pip install jupyter matplotlib

3. **Launch Jupyter:**

   .. code-block:: bash

       jupyter notebook tutorials/medtda_tutorial.ipynb

Google Colab
------------

Run the tutorial in Google Colab without local installation:

.. raw:: html

   <a href="https://colab.research.google.com/github/dashtiali/medtda/blob/main/tutorials/medtda_tutorial.ipynb" target="_blank">
       <img src="https://colab.research.google.com/assets/colab-badge.svg" alt="Open In Colab"/>
   </a>

Binder
------

Run interactively in your browser using Binder:

.. raw:: html

   <a href="https://mybinder.org/v2/gh/dashtiali/medtda/main?filepath=tutorials%2Fmedtda_tutorial.ipynb" target="_blank">
       <img src="https://mybinder.org/badge_logo.svg" alt="Launch Binder"/>
   </a>

Tutorial Contents
=================

The notebook is divided into two main sections covering 3D medical images (NIfTI) and 2D images (JPEG/PNG).

Part 1 — 3D Medical Images
---------------------------

1. Loading the Data
~~~~~~~~~~~~~~~~~~~~

Load NIfTI images and masks using SimpleITK. Supports ``.nii``, ``.nii.gz``, ``.mha``, ``.mhd``, and ``.nrrd`` formats.

**Code preview:**

.. code-block:: python

    import SimpleITK as sitk
    from medtda import Preprocessor, BarcodeExtractor, FeatureExtractor
    from medtda import plotting

    image_path = 'scan.nii.gz'
    mask_path  = 'mask.nii.gz'

    sitk_image = sitk.ReadImage(image_path)
    image = sitk.GetArrayFromImage(sitk_image)  # (Z, Y, X)

    print(f"Image shape: {image.shape}")
    print(f"Intensity range: [{image.min():.3f}, {image.max():.3f}]")

2. Visualize Loaded Data
~~~~~~~~~~~~~~~~~~~~~~~~~

Display the middle axial slice of the image alongside the corresponding mask slice.

**Code preview:**

.. code-block:: python

    mid_z = image.shape[0] // 2

    fig, axes = plt.subplots(1, 2, figsize=(8, 15))
    axes[0].imshow(slice_image, cmap='gray')
    axes[0].set_title(f'Image - Axial Slice (Z={mid_z})')
    axes[1].imshow(mask[mid_z], cmap='Reds', alpha=0.7)
    axes[1].set_title(f'Mask - Axial Slice (Z={mid_z})')
    plt.tight_layout()
    plt.show()

3. Preprocessing
~~~~~~~~~~~~~~~~~

Configure and apply preprocessing including resampling, CT windowing, normalization, label selection, and ROI cropping.

* **Basic Preprocessing** — create a ``Preprocessor`` and inspect its settings with ``get_preprocessing_info()``
* **Visualize Preprocessing Effects** — compare original vs preprocessed image side by side
* **Test Different Normalization Methods** — compare ``minmax``, ``zscore``, and no normalization

**Code preview:**

.. code-block:: python

    preprocessor = Preprocessor(
        spacing=[1.0, 1.0, 2.5],
        window=[100, 400],     # CT windowing (HU range)
        normalize=True,
        label=2,               # Extract label 2 from multi-label mask
        crop_to_roi=True,
        roi_padding=5
    )

    preprocessor.get_preprocessing_info()
    preprocessed_image, metadata = preprocessor.preprocess(image_path, mask_path)
    print(f"Preprocessed shape: {preprocessed_image.shape}")

4. Barcode Computation
~~~~~~~~~~~~~~~~~~~~~~~

Use ``BarcodeExtractor`` to compute persistence diagrams directly from image and mask paths.

**Code preview:**

.. code-block:: python

    barcode_extractor = BarcodeExtractor(
        spacing=[1.0, 1.0, 2.5],
        crop_to_roi=True,
        roi_padding=5,
        label=2,
        normalize=True,
        normalize_method='minmax',
        max_dimension=2,            # H0, H1, H2
        filtration_type='sublevel', # Track bright features
    )

    persistence_diagram = barcode_extractor.execute(image_path, mask_path)
    print(f"H0 features: {len(persistence_diagram['H0'])}")
    print(f"H1 features: {len(persistence_diagram['H1'])}")

5. Visualizations
~~~~~~~~~~~~~~~~~~

MedTDA provides a full suite of visualization functions for persistence diagrams.

**Code preview:**

.. code-block:: python

    # Persistence diagram and barcode
    plotting.plot_persistence_diagram(persistence_diagram, ax=ax, markersize=5)
    plotting.plot_barcode(persistence_diagram, dimensions=[0])

    # Betti curves, lifespan, entropy summary, silhouette
    fig, ax = plt.subplots(figsize=(20, 4), ncols=4)
    plotting.plot_betti_curve(persistence_diagram, resolution=100, ax=ax[0])
    plotting.plot_lifespan(persistence_diagram, resolution=100, ax=ax[1])
    plotting.plot_entropy_summary(persistence_diagram, resolution=100, ax=ax[2])
    plotting.plot_silhouette(persistence_diagram, resolution=100, ax=ax[3])

    # Persistence landscapes (one panel per homology dimension)
    fig, ax = plt.subplots(figsize=(20, 6), ncols=3)
    for dim in range(3):
        plotting.plot_landscape(persistence_diagram, ax=ax[dim],
                                dimension=dim, num_landscapes=5, resolution=50)

    # Tropical coordinates
    plotting.plot_tropical_coordinates(persistence_diagram, bar_width=0.25)

6. Comparing PH Filtration Types
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Compare sublevel (bright structures) and superlevel (dark structures) filtrations side by side.

**Code preview:**

.. code-block:: python

    extractor_sublevel = BarcodeExtractor(
        max_dimension=2, filtration_type='sublevel',
        crop_to_roi=True, roi_padding=1,
        normalize=True, label=2, normalize_method='minmax'
    )
    barcodes_sublevel = extractor_sublevel.execute(image, mask)

    extractor_superlevel = BarcodeExtractor(
        max_dimension=2, filtration_type='superlevel',
        crop_to_roi=True, roi_padding=1,
        normalize=True, label=2, normalize_method='minmax'
    )
    barcodes_superlevel = extractor_superlevel.execute(image, mask)

    print(f"Sublevel  — H0: {len(barcodes_sublevel['H0'])}, H1: {len(barcodes_sublevel['H1'])}")
    print(f"Superlevel — H0: {len(barcodes_superlevel['H0'])}, H1: {len(barcodes_superlevel['H1'])}")

7. Feature Extraction
~~~~~~~~~~~~~~~~~~~~~~

Use ``FeatureExtractor`` to run the full pipeline (preprocessing → PH → vectorization) in one call.

**Code preview:**

.. code-block:: python

    # Default vectorization (PersStats)
    extractor = FeatureExtractor(
        spacing=[1.0, 1.0, 2.5],
        crop_to_roi=True,
        normalize=True,
        label=2,
        max_dimension=2,
        construction='V',
    )
    features = extractor.execute(image_path, mask_path)
    print(f"Number of features: {len(features)}")

    # Switch to PersLandscape and customize parameters
    extractor.set_vectorization_method('PersLandscape')
    extractor.set_vectorization_params('PersLandscape', num_landscapes=5, resolution=100)
    extractor.get_settings()
    features = extractor.execute(image_path, mask_path)
    print(f"Feature vector shape: {np.array(list(features.values())).shape}")

Part 2 — 2D Examples
----------------------

The same workflow applies to 2D images (JPEG, PNG, TIFF, etc.); no SimpleITK loading is required and masks are optional.

1. Loading & Visualizing
~~~~~~~~~~~~~~~~~~~~~~~~~

**Code preview:**

.. code-block:: python

    image_path = 'image.jpg'

    img_2d = plt.imread(image_path)
    plt.imshow(img_2d, cmap='gray' if img_2d.ndim == 2 else None)
    plt.title(f'Shape: {img_2d.shape}')
    plt.axis('off')
    plt.show()

2. Barcode Computation & Visualizations
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Identical API — pass the image path directly to ``BarcodeExtractor.execute()``.

**Code preview:**

.. code-block:: python

    barcode_extractor = BarcodeExtractor(
        max_dimension=1,            # H0 and H1 for 2D
        filtration_type='sublevel',
    )
    persistence_diagram = barcode_extractor.execute(image_path)

    plotting.plot_persistence_diagram(persistence_diagram, ax=ax, markersize=5)
    plotting.plot_barcode(persistence_diagram, linewidth=1)

    fig, ax = plt.subplots(figsize=(20, 4), ncols=4)
    plotting.plot_betti_curve(persistence_diagram, resolution=100, ax=ax[0])
    plotting.plot_lifespan(persistence_diagram, resolution=100, ax=ax[1])
    plotting.plot_entropy_summary(persistence_diagram, resolution=100, ax=ax[2])
    plotting.plot_silhouette(persistence_diagram, resolution=100, ax=ax[3])

    fig, ax = plt.subplots(figsize=(20, 6), ncols=2)
    for dim in range(2):
        plotting.plot_landscape(persistence_diagram, ax=ax[dim], dimension=dim,
                                num_landscapes=5, resolution=50)

    plotting.plot_tropical_coordinates(persistence_diagram, bar_width=0.25)

3. Feature Extraction
~~~~~~~~~~~~~~~~~~~~~~

**Code preview:**

.. code-block:: python

    # Default (PersStats)
    extractor = FeatureExtractor()
    features = extractor.execute(image_path)
    print(f"Number of features: {len(features)}")

    # Switch to PersLandscape
    extractor = FeatureExtractor(normalize=True, normalize_method='minmax')
    extractor.set_vectorization_method('PersLandscape')
    extractor.set_vectorization_params('PersLandscape', num_landscapes=5, resolution=100)
    features = extractor.execute(image_path)
    print(f"Feature vector shape: {np.array(list(features.values())).shape}")

4. Comparing PH Filtration Types
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

**Code preview:**

.. code-block:: python

    ext_sub   = BarcodeExtractor(max_dimension=1, filtration_type='sublevel')
    ext_super = BarcodeExtractor(max_dimension=1, filtration_type='superlevel')

    barcodes_sub   = ext_sub.execute(image_path)
    barcodes_super = ext_super.execute(image_path)

    print(f"Sublevel  — H0: {len(barcodes_sub['H0'])}, H1: {len(barcodes_sub['H1'])}")
    print(f"Superlevel — H0: {len(barcodes_super['H0'])}, H1: {len(barcodes_super['H1'])}")

    fig, axes = plt.subplots(1, 2, figsize=(14, 6))
    plotting.plot_persistence_diagram(barcodes_sub,   ax=axes[0], markersize=5)
    axes[0].set_title('Sublevel (Bright Features)')
    plotting.plot_persistence_diagram(barcodes_super, ax=axes[1], markersize=5)
    axes[1].set_title('Superlevel (Dark Features)')
    plt.tight_layout()
    plt.show()

Sample Data
===========

The notebook is designed to work with your own data. Update the ``image_path`` and ``mask_path`` variables in the relevant cells to point to your files.

**3D section** — expects NIfTI or other volumetric formats supported by SimpleITK (``.nii``, ``.nii.gz``, ``.mha``, ``.mhd``, ``.nrrd``).

**2D section** — expects any standard image format (``.jpg``, ``.png``, ``.tiff``). Masks are optional.

Getting Help
Getting Help
============

If you encounter issues with the tutorial:

1. **Check the FAQ**: :doc:`../faq`
2. **Search GitHub Issues**: `MedTDA Issues <https://github.com/dashtiali/medtda/issues>`_
3. **Ask a question**: `Open a new issue <https://github.com/dashtiali/medtda/issues/new>`_
4. **Join discussion**: `GitHub Discussions <https://github.com/dashtiali/medtda/discussions>`_

Contributing
============

Found a bug in the tutorial or have a suggestion? Please:

* Open an issue: `Report a bug <https://github.com/dashtiali/medtda/issues/new?template=bug_report.md>`_
* Submit a pull request: :doc:`../contributing`
* Share your notebook: We'd love to feature community tutorials!

Citation
========

If you use this tutorial in your research or teaching, please cite MedTDA:

.. code-block:: text

    @software{medtda2024,
      title={MedTDA: Topological Data Analysis for Medical Imaging},
      author={Dashti Ali},
      year={2024},
      url={https://github.com/dashtiali/medtda}
    }

Next Steps
==========

After completing the tutorial:

* :doc:`../user_guide` - Comprehensive user guide
* :doc:`../examples/index` - More examples
* :doc:`../api/index` - API reference
* :doc:`../theory/index` - Theoretical background

See Also
========

* :doc:`../quickstart` - Quick start guide
* :doc:`../examples/basic_usage` - Basic usage examples
* :doc:`../theory/persistent_homology` - Mathematical background
