.. _api_barcodeextractor:

=================
BarcodeExtractor
=================

.. currentmodule:: medtda

The ``BarcodeExtractor`` class provides a higher-level interface for extracting persistence barcodes from images.

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

Overview
========

``BarcodeExtractor`` wraps the persistent homology computation functions into a convenient class interface. It's used internally by :class:`FeatureExtractor` but can also be used standalone.

**Key Features:**

* Simple interface for barcode extraction
* Configurable filtration type and construction method
* Automatic dimension detection
* Flexible configuration

Basic Usage
===========

Quick Start
-----------

::

    from medtda import BarcodeExtractor

    extractor = BarcodeExtractor()
    barcodes = extractor.execute(image_array)

    # Access barcodes by dimension
    h0 = barcodes['H0']
    h1 = barcodes['H1']

Custom Configuration
--------------------

::

    extractor = BarcodeExtractor(
        filtration_type='superlevel',
        construction='V',
        max_dimension=2
    )
    
    barcodes = extractor.execute(image_array)

Dynamic Reconfiguration
------------------------

::

    extractor = BarcodeExtractor()
    
    # Extract with sublevel filtration
    barcodes_sub = extractor.execute(image)
    
    # Change to superlevel
    extractor.set_filtration_type('superlevel')
    barcodes_sup = extractor.execute(image)

Class Documentation
===================

.. autoclass:: medtda.BarcodeExtractor
   :members:
   :undoc-members:
   :show-inheritance:
   :special-members: __init__

Constructor Parameters
======================

.. list-table::
   :widths: 20 20 60
   :header-rows: 1

   * - Parameter
     - Type
     - Description
   * - ``spacing``
     - tuple or None
     - Target voxel spacing for resampling, e.g. ``(1.0, 1.0, 1.0)`` (default: ``None``)
   * - ``window``
     - tuple or None
     - CT windowing as ``(center, width)`` (default: ``None``)
   * - ``normalize``
     - bool
     - Enable intensity normalization (default: ``False``)
   * - ``normalize_method``
     - str
     - Normalization method: ``'minmax'``, ``'zscore'``, or ``'robust'`` (default: ``'minmax'``)
   * - ``clip_percentiles``
     - tuple or None
     - Clip intensities to ``(lower, upper)`` percentiles before normalization (default: ``None``)
   * - ``background_value``
     - float or None
     - Background pixel value, auto-determined if ``None`` (default: ``None``)
   * - ``label``
     - int or None
     - Label to extract from multi-label mask (default: ``1``)
   * - ``crop_to_roi``
     - bool
     - Crop to ROI bounding box when mask is provided (default: ``True``)
   * - ``roi_padding``
     - int
     - Padding around ROI in pixels (default: ``1``)
   * - ``filtration_type``
     - str
     - ``'sublevel'`` or ``'superlevel'`` (default: ``'sublevel'``)
   * - ``construction``
     - str
     - Cubical complex construction: ``'T'`` or ``'V'`` (default: ``'T'``)
   * - ``max_dimension``
     - int
     - Maximum homology dimension, ``-1`` for auto (default: ``-1``)

Methods
=======

execute
-------

.. automethod:: medtda.BarcodeExtractor.execute
   :noindex:

Execute the barcode extraction pipeline.

**Signature:**

.. code-block:: python

   def execute(
       self,
       image: Union[str, Path, np.ndarray],
       mask: Union[str, Path, np.ndarray, None] = None
   ) -> Dict[str, np.ndarray]

**Parameters:**

* **image** (*str, Path, or np.ndarray*) - Input image (file path or array)
* **mask** (*str, Path, np.ndarray, or None*) - Optional binary or multi-label mask

**Returns:**

* **barcodes** (*dict*) - Dictionary mapping dimension to barcode arrays
  
  * Keys: homology dimension strings (``'H0'``, ``'H1'``, ``'H2'``, ...)
  * Values: NumPy arrays of shape ``(n_features, 2)`` with ``[birth, death]`` columns

**Example:**

::

    extractor = BarcodeExtractor()
    barcodes = extractor.execute(image)
    
    for dim_key, barcode in barcodes.items():
        print(f"{dim_key}: {len(barcode)} features")

set_filtration_type
-------------------

.. automethod:: medtda.BarcodeExtractor.set_filtration_type
   :noindex:

Change the filtration type.

**Signature:**

.. code-block:: python

   def set_filtration_type(self, filtration_type: str) -> None

**Parameters:**

* **filtration_type** (*str*) - ``'sublevel'`` or ``'superlevel'``

**Example:**

::

    extractor = BarcodeExtractor()
    extractor.set_filtration_type('superlevel')

set_construction
----------------

.. automethod:: medtda.BarcodeExtractor.set_construction
   :noindex:

Change the cubical complex construction method.

**Signature:**

.. code-block:: python

   def set_construction(self, construction: str) -> None

**Parameters:**

* **construction** (*str*) - ``'T'`` or ``'V'``

**Example:**

::

    extractor = BarcodeExtractor()
    extractor.set_construction('V')

set_max_dimension
-----------------

.. automethod:: medtda.BarcodeExtractor.set_max_dimension
   :noindex:

Change the maximum homology dimension.

**Signature:**

.. code-block:: python

   def set_max_dimension(self, max_dimension: int) -> None

**Parameters:**

* **max_dimension** (*int*) - Maximum dimension, or ``-1`` for auto

**Example:**

::

    extractor = BarcodeExtractor()
    extractor.set_max_dimension(1)  # Only H0 and H1

Complete Examples
=================

Example 1: Basic Barcode Extraction
------------------------------------

::

    import numpy as np
    from medtda import BarcodeExtractor
    from medtda.loaders import load_image

    # Load image
    image, metadata = load_image('medical_image.nii.gz')

    # Create extractor
    extractor = BarcodeExtractor(
        filtration_type='sublevel',
        construction='T'
    )

    # Extract barcodes
    barcodes = extractor.execute(image)

    # Analyze results
    for dim_key in barcodes:
        barcode = barcodes[dim_key]
        persistence = barcode[:, 1] - barcode[:, 0]
        
        print(f"\n{dim_key}:")
        print(f"  Features: {len(barcode)}")
        print(f"  Avg persistence: {persistence.mean():.4f}")
        print(f"  Max persistence: {persistence.max():.4f}")

Example 2: Comparing Filtrations
---------------------------------

::

    from medtda import BarcodeExtractor

    extractor = BarcodeExtractor()

    # Sublevel: features from low to high intensity
    extractor.set_filtration_type('sublevel')
    barcodes_sub = extractor.execute(image)

    # Superlevel: features from high to low intensity
    extractor.set_filtration_type('superlevel')
    barcodes_sup = extractor.execute(image)

    # Compare H1 features
    print(f"Sublevel H1 features: {len(barcodes_sub['H1'])}")
    print(f"Superlevel H1 features: {len(barcodes_sup['H1'])}")

Example 3: Progressive Dimension Analysis
------------------------------------------

::

    from medtda import BarcodeExtractor

    extractor = BarcodeExtractor(construction='T')

    # Start with H0 only (fast)
    extractor.set_max_dimension(0)
    barcodes_h0 = extractor.execute(image)
    print(f"H0 features: {len(barcodes_h0['H0'])}")

    # Add H1 if needed
    extractor.set_max_dimension(1)
    barcodes_h01 = extractor.execute(image)
    print(f"H1 features: {len(barcodes_h01['H1'])}")

    # Add H2 for full 3D topology (slower)
    extractor.set_max_dimension(2)
    barcodes_full = extractor.execute(image)
    print(f"H2 features: {len(barcodes_full['H2'])}")

Example 4: Integration with Preprocessing
------------------------------------------

::

    from medtda import BarcodeExtractor, Preprocessor
    from medtda.loaders import load_image, load_mask

    # Load data
    image, _ = load_image('ct_scan.nii.gz')
    mask, _ = load_mask('tumor_mask.nii.gz')

    # Preprocess
    preprocessor = Preprocessor(
        normalize=True,
        crop_to_roi=True,
        roi_padding=5
    )
    processed_image, _ = preprocessor.preprocess(image, mask)

    # Extract barcodes
    extractor = BarcodeExtractor(
        filtration_type='sublevel',
        max_dimension=2
    )
    barcodes = extractor.execute(processed_image)

    # Analyze topology
    h1_persistence = barcodes['H1'][:, 1] - barcodes['H1'][:, 0]
    significant_loops = np.sum(h1_persistence > 0.1)
    
    print(f"Significant loops in tumor: {significant_loops}")

Example 5: Batch Processing
----------------------------

::

    from medtda import BarcodeExtractor
    import glob

    extractor = BarcodeExtractor()

    # Process multiple images
    image_paths = glob.glob('images/*.nii.gz')
    
    all_barcodes = []
    for path in image_paths:
        image, _ = load_image(path)
        barcodes = extractor.execute(image)
        all_barcodes.append(barcodes)
        
        # Quick summary
        h1_count = len(barcodes['H1'])
        print(f"{path}: {h1_count} H1 features")

Common Use Cases
================

Use Case 1: Topology Characterization
--------------------------------------

Extract all topological features::

    extractor = BarcodeExtractor(max_dimension=2)
    barcodes = extractor.execute(preprocessed_image)
    
    # Characterize topology
    n_components = len(barcodes['H0'])
    n_loops = len(barcodes['H1'])
    n_voids = len(barcodes['H2'])

Use Case 2: Feature Filtering
------------------------------

Extract and filter by persistence::

    extractor = BarcodeExtractor()
    barcodes = extractor.execute(image)
    
    # Keep only significant features
    threshold = 0.1
    for dim_key in barcodes:
        barcode = barcodes[dim_key]
        persistence = barcode[:, 1] - barcode[:, 0]
        barcodes[dim_key] = barcode[persistence > threshold]

Use Case 3: Method Comparison
------------------------------

Compare T and V construction::

    # T-construction
    extractor_t = BarcodeExtractor(construction='T')
    barcodes_t = extractor_t.execute(image)

    # V-construction
    extractor_v = BarcodeExtractor(construction='V')
    barcodes_v = extractor_v.execute(image)

    # Compare feature counts
    for dim_key in barcodes_t:
        print(f"{dim_key}: T={len(barcodes_t[dim_key])}, V={len(barcodes_v[dim_key])}")

Performance Tips
================

1. **Start with lower dimensions:**
   
   Use ``max_dimension=0`` or ``max_dimension=1`` initially::
   
       extractor = BarcodeExtractor(max_dimension=1)

2. **Choose construction wisely:**
   
   * T-construction: More features per image
   * V-construction: Fewer features, faster for large images

3. **Pass mask directly to execute:**
   
   Let BarcodeExtractor handle preprocessing::
   
       extractor = BarcodeExtractor(crop_to_roi=True)
       barcodes = extractor.execute(image, mask)

4. **Reuse extractor:**
   
   Create once, use multiple times::
   
       extractor = BarcodeExtractor()
       for image in images:
           barcodes = extractor.execute(image)

See Also
========

* :doc:`ph_computer` - Underlying PH computation functions
* :doc:`featureextractor` - High-level feature extraction
* :doc:`vectorizers` - Convert barcodes to feature vectors
* :doc:`../user_guide/persistent_homology` - PH guide
