.. _theory_filtrations:

===========
Filtrations
===========

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

Introduction
============

A **filtration** is a nested sequence of topological spaces built from data. For images, filtrations are constructed by thresholding intensity values, creating a series of binary images at different levels.

Filtrations are the foundation of persistent homology, allowing us to track how topological features evolve across scales.

What is a Filtration?
======================

Formal Definition (Simplified)
-------------------------------

A filtration is a sequence of topological spaces:

.. math::

   \emptyset = X_0 \subseteq X_1 \subseteq X_2 \subseteq \cdots \subseteq X_n = X

where each :math:`X_i` is contained in the next.

**For Images:**

Each :math:`X_i` corresponds to regions where intensity is below (or above) a threshold :math:`\tau_i`.

Intuitive Understanding
-----------------------

Think of a filtration as a movie showing structure appearing/disappearing:

1. Start with nothing (empty set)
2. Gradually add pixels/voxels based on intensity
3. End with the complete image

**Analogy:** Watching a photograph develop in a darkroom - structure emerges gradually.

Building Filtrations from Images
=================================

Sublevel Filtration
-------------------

**Definition:** Include pixels with intensity :math:`\leq` threshold.

**Process:**

1. Start with lowest intensity value
2. Gradually increase threshold
3. Add pixels as threshold increases
4. End with all pixels

**Use Cases:**

* Dark features on bright background
* Vessels in angiography (low intensity)
* Air in CT scans (dark regions)

**In MedTDA:**

::

    from medtda import BarcodeExtractor
    
    extractor = BarcodeExtractor(filtration_type='sublevel')
    barcodes = extractor.execute(image)

Superlevel Filtration
---------------------

**Definition:** Include pixels with intensity :math:`\geq` threshold.

**Process:**

1. Start with highest intensity value
2. Gradually decrease threshold
3. Add pixels as threshold decreases
4. End with all pixels

**Use Cases:**

* Bright features on dark background
* Contrast-enhanced regions (high intensity)
* Bright lesions in MRI
* Enhanced tumors in CT

**In MedTDA:**

::

    extractor = BarcodeExtractor(filtration_type='superlevel')
    barcodes = extractor.execute(image)

Choosing Filtration Direction
------------------------------

.. list-table::
   :widths: 40 30 30
   :header-rows: 1

   * - Feature Type
     - Filtration
     - Example
   * - Dark on bright background
     - Sublevel
     - Blood vessels
   * - Bright on dark background
     - Superlevel
     - Contrast-enhanced lesions
   * - Both types present
     - Try both
     - Mixed tissue types

**Tip:** Visualize your image - which features stand out?

Cubical Complexes
=================

What are Cubical Complexes?
----------------------------

**Cubical complexes** are topological structures built from cubes (pixels in 2D, voxels in 3D).

**Why Cubical?**

* Images are naturally defined on grids
* Direct construction from pixels/voxels
* No need for triangulation
* Computationally efficient

**Alternative:** Simplicial complexes (triangulations) - more common in point cloud TDA but less natural for images.

Construction Methods
====================

MedTDA supports two cubical complex construction methods:

T-Construction (Top-Cell)
--------------------------

Each pixel/voxel becomes a **top-dimensional cell** - a 2-cube (square) in 2D images or a 3-cube (cube) in 3D images. The filtration value equals the pixel/voxel intensity.

**Properties:**

* More features detected
* Finer topological detail
* **Default and recommended method**

**In MedTDA:**

::

    extractor = BarcodeExtractor(construction='T')

V-Construction (Vertex-Based)
------------------------------

Each pixel/voxel becomes a **vertex** (0-cube). Higher-dimensional cells are built from the vertex grid, inheriting intensities from the vertices.

**Properties:**

* Fewer features detected
* Coarser topological detail
* Slightly faster - use when speed is critical

**In MedTDA:**

::

    extractor = BarcodeExtractor(construction='V')

Comparison
----------

.. list-table::
   :widths: 30 35 35
   :header-rows: 1

   * - Property
     - T-Construction
     - V-Construction
   * - Cells per pixel/voxel
     - One top-cell
     - One vertex
   * - Feature count
     - More features
     - Fewer features
   * - Detail level
     - Finer
     - Coarser
   * - Computation speed
     - Slightly slower
     - Slightly faster
   * - Recommendation
     - **Default choice**
     - Speed-critical applications

**Practical Difference:**

::

    # T-construction: More detailed
    extractor_t = BarcodeExtractor(construction='T')
    barcodes_t = extractor_t.execute(image)
    print(f"H1 features: {len(barcodes_t['H1'])}")  # e.g., 47 features
    
    # V-construction: Faster, less detail
    extractor_v = BarcodeExtractor(construction='V')
    barcodes_v = extractor_v.execute(image)
    print(f"H1 features: {len(barcodes_v['H1'])}")  # e.g., 32 features

Filtration Process
==================

As we sweep through thresholds during a sublevel filtration:

**H0 (Connected Components):**

* Birth: New component appears
* Death: Two components merge
* One component persists forever (infinite persistence)

**H1 (Loops):**

* Birth: Cycle forms
* Death: Cycle gets filled
* All H1 features eventually die

**H2 (Voids - 3D only):**

* Birth: Enclosed void forms
* Death: Void gets filled
* All H2 features eventually die

Multi-Dimensional Images
=========================

**2D Images** (H0, H1): H0 captures separate regions/blobs; H1 captures loops and enclosed areas.

::

    extractor = BarcodeExtractor(max_dimension=1)
    barcodes = extractor.execute(image_2d)
    h0 = barcodes['H0']  # Connected components
    h1 = barcodes['H1']  # Loops

**3D Images** (H0, H1, H2): H0 captures separate regions; H1 captures tunnels/loops; H2 captures enclosed voids and cavities.

::

    extractor = BarcodeExtractor(max_dimension=2)
    barcodes = extractor.execute(image_3d)
    h0 = barcodes['H0']  # Connected components
    h1 = barcodes['H1']  # Loops/tunnels
    h2 = barcodes['H2']  # Voids/cavities

Practical Considerations
=========================

Image Preprocessing
-------------------

Filtrations work best on preprocessed images:

1. **Normalization:** Consistent intensity ranges

::

    from medtda import Preprocessor
    
    preprocessor = Preprocessor(
        normalize=True,
        normalize_method='robust'
    )
    processed = preprocessor.preprocess(image)

2. **Resampling:** Isotropic voxels

::

    preprocessor = Preprocessor(spacing=(1.0, 1.0, 1.0))

3. **Masking:** Focus on region of interest

::

    preprocessor = Preprocessor(crop_to_roi=True)

Computational Complexity
------------------------

**Time complexity:** Roughly O(n³) for n³ voxels

**Memory:** Stores cubical complex

**Tips for large images:**

* Downsample if possible
* Crop to ROI first
* Use V-construction for speed
* Process 2D slices instead of full 3D

Mathematical Foundations
========================

Cubical Complexes
-----------------

A **cubical complex** is a collection of elementary cubes (vertices, edges, squares, cubes, etc.) in Euclidean space. Medical images naturally form cubical complexes where pixels/voxels are the top-dimensional cubes.

**Elementary Cubes:** In dimension n, an elementary k-cube is a product:

.. math::

   [a_1, b_1] \times [a_2, b_2] \times \cdots \times [a_n, b_n]

where exactly k intervals have :math:`a_i < b_i` (non-degenerate) and the rest have :math:`a_i = b_i` (degenerate).

**Examples:**

* 0-cube (vertex): all intervals degenerate, a single point
* 1-cube (edge): one non-degenerate interval
* 2-cube (square): two non-degenerate intervals  
* 3-cube (cube): three non-degenerate intervals

**Boundary Operator:** The boundary of a cube consists of all faces (cubes of one dimension lower) on its surface. For a 2D pixel at (i, j):

.. math::

   \partial([i,i+1] \times [j,j+1]) = \text{(four edges around the pixel)}

Filtered Complexes
------------------

A **filtration** is an increasing sequence of complexes where each complex is a subcomplex of the next:

.. math::

   X_0 \subseteq X_1 \subseteq X_2 \subseteq \cdots \subseteq X_n

**From Images:** Given a grayscale image I with intensity function f(x), filtrations are built by thresholding:

**Sublevel sets:** :math:`X_t = \{x : f(x) \leq t\}`

**Superlevel sets:** :math:`X_t = \{x : f(x) \geq t\}`

**Lower-star filtration:** For cubical complexes, each lower-dimensional cube (edge, vertex) enters the filtration at the minimum intensity of its cofaces (adjacent pixels):

.. math::

   t(\text{cube}) = \min\{f(\text{pixel}) : \text{cube} \in \partial(\text{pixel})\}

This ensures the filtration respects the cubical complex structure.

Cubical Homology
----------------

**Chain Groups:** The k-th chain group :math:`C_k` consists of formal sums of k-dimensional cubes with coefficients in a field (typically :math:`\mathbb{Z}/2\mathbb{Z}`).

**Boundary Map:** The boundary operator :math:`\partial_k : C_k \to C_{k-1}` maps each k-cube to its boundary (collection of (k-1)-faces).

**Homology Groups:** The k-th homology group is:

.. math::

   H_k = \ker(\partial_k) / \text{im}(\partial_{k+1})

This quotient captures:

* **Cycles:** k-dimensional structures with no boundary (:math:`\partial_k = 0`)
* **Boundaries:** k-dimensional structures that are boundaries of (k+1)-structures
* **Homology classes:** Equivalence classes of cycles that differ by boundaries

**Interpretation:**

* H_0: Connected components (0-cycles not bounding anything)
* H_1: Loops (1-cycles that don't bound 2-dimensional regions)
* H_2: Voids (2-cycles that don't bound 3-dimensional volumes)

See Also
========

* :doc:`persistent_homology` - Computing homology on filtrations
* :doc:`barcodes_diagrams` - Visualizing filtration results
* :doc:`../user_guide/persistent_homology` - Practical PH guide
* :doc:`../api/ph_computer` - PH computation API
