.. _api_featureextractor:

================
FeatureExtractor
================

.. currentmodule:: medtda

The ``FeatureExtractor`` class is the main high-level interface for extracting topological features from medical images.

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

Overview
========

``FeatureExtractor`` combines preprocessing, persistent homology computation, and vectorization into a single convenient interface. It is the recommended entry point for most users.

**Key Features:**

* End-to-end feature extraction pipeline
* Configurable preprocessing options
* Multiple vectorization methods
* Batch processing support
* Optional raw barcode extraction

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

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

Extract features from a single image::

    from medtda import FeatureExtractor

    # Create extractor with default settings
    extractor = FeatureExtractor()

    # Extract features
    features = extractor.execute('path/to/image.nii.gz')

With Mask
---------

Process an image with a binary mask::

    extractor = FeatureExtractor(
        normalize=True,
       vectorization_method='PersImage'
    features = extractor.execute(
        image='path/to/image.nii.gz',
        mask='path/to/mask.nii.gz'
    )

Multiple Vectorization Methods
-------------------------------

Extract features using multiple methods simultaneously::

    extractor = FeatureExtractor()
    extractor.enable_vectorization_methods([
        'PersStats',
        'BettiCurve',
        'PersImage'
    ])

    features = extractor.execute('image.nii.gz')
    # features is a dict with keys like: 'PersStats_H0_mean', 'BettiCurve_H0_f0', etc.

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

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

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

Preprocessing 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)``)
   * - ``window``
     - tuple or None
     - Windowing as ``(center, width)`` for CT images
   * - ``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-detected if ``None``)
   * - ``label``
     - int or None
     - Extract specific label from multi-label mask (default: ``1``)
   * - ``crop_to_roi``
     - bool
     - Crop to ROI bounding box (default: ``True``)
   * - ``roi_padding``
     - int
     - Padding around ROI in pixels (default: ``1``)

Persistent Homology Parameters
-------------------------------

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

   * - Parameter
     - Type
     - Description
   * - ``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``)

Vectorization Parameters
-------------------------

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

   * - Parameter
     - Type
     - Description
   * - ``vectorization_method``
     - str or list
     - Vectorization method(s) to use (default: ``'PersStats'``)
   * - ``return_barcodes``
     - bool
     - Return raw barcodes with features (default: ``False``)

Valid vectorization methods:

* ``'PersStats'`` - Statistical summaries
* ``'BettiCurve'`` - Betti numbers over filtration
* ``'PersImage'`` - 2D histogram representation
* ``'PersLandscape'`` - Functional landscape
* ``'PersSilhouette'`` - Average landscape
* ``'EntropySummary'`` - Information-theoretic features
* ``'PersLifespan'`` - Lifespan distribution
* ``'PersTropicalCoordinates'`` - Tropical algebra

Methods
=======

execute
-------

.. automethod:: medtda.FeatureExtractor.execute
   :noindex:

**Signature:**

.. code-block:: python

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

**Parameters:**

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

**Returns:**

* **features** (*dict*) - Dictionary mapping feature names to values.
  Keys follow format: '{method}_{dimension}_{feature}' for dict-based methods
  or '{method}_{dimension}_f{i}' for array-based methods.
* **OR (features, barcodes)** (*tuple*) - If ``return_barcodes=True``

**Example:**

::

    extractor = FeatureExtractor(
        normalize=True,
        vectorization_method='PersImage',
        return_barcodes=True
    )

    features, barcodes = extractor.execute('image.nii.gz', mask='mask.nii.gz')
    
    # features is a dict: {'PersImage_H0_f0': 0.12, 'PersImage_H0_f1': 0.45, ...}
    # barcodes is a dict: {0: array([[b,d],...]), 1: array([[b,d],...])}

set_vectorization_method
-------------------------

.. automethod:: medtda.FeatureExtractor.set_vectorization_method
   :noindex:

Change the vectorization method and its parameters.

**Signature:**

.. code-block:: python

   def set_vectorization_method(self, method: str, **params) -> None

**Parameters:**

* **method** (*str*) - Vectorization method name
* **params** - Method-specific parameters

**Example:**

::

    extractor = FeatureExtractor()
    
    # Change to persistence image with custom parameters
    extractor.set_vectorization_method(
        'PersImage',
        resolution=50,
        bandwidth=0.1
    )

enable_vectorization_methods
-----------------------------

.. automethod:: medtda.FeatureExtractor.enable_vectorization_methods
   :noindex:

Enable multiple vectorization methods.

**Signature:**

.. code-block:: python

   def enable_vectorization_methods(self, methods: List[str]) -> None

**Parameters:**

* **methods** (*list of str*) - List of vectorization method names

**Example:**

::

    extractor = FeatureExtractor()
    extractor.enable_vectorization_methods([
        'PersStats',
        'BettiCurve',
        'PersImage'
    ])
    
    features = extractor.execute('image.nii.gz')
    # Returns dict with 3 keys

set_vectorization_params
-------------------------------

.. automethod:: medtda.FeatureExtractor.set_vectorization_params
   :noindex:

Configure parameters for a specific vectorization method when using multiple methods.

**Signature:**

.. code-block:: python

   def set_vectorization_params(self, method: str, **params) -> None

**Parameters:**

* **method** (*str*) - Vectorization method name
* **params** - Method-specific parameters

**Example:**

::

    extractor = FeatureExtractor()
    extractor.enable_vectorization_methods(['BettiCurve', 'PersImage'])
    
    # Configure individual method parameters
    extractor.set_vectorization_params('BettiCurve', resolution=200)
    extractor.set_vectorization_params('PersImage', resolution=30)

Complete Example
================

Advanced Usage with All Options
--------------------------------

::

    from medtda import FeatureExtractor
    import numpy as np

    # Initialize with comprehensive configuration
    extractor = FeatureExtractor(
        # Preprocessing
        spacing=(1.0, 1.0, 1.0),
        window=(40, 400),  # CT abdomen window
        normalize=True,
        normalize_method='robust',
        crop_to_roi=True,
        roi_padding=5,
        
        # Persistent Homology
        filtration_type='sublevel',
        construction='T',
        max_dimension=2,
        
        # Vectorization
        vectorization_method='PersImage',
        return_barcodes=True
    )

    # Extract features
    features, barcodes = extractor.execute(
        image='path/to/ct_scan.nii.gz',
        mask='path/to/liver_mask.nii.gz'
    )

    # Access results
    # features is a flat dict: {'PersImage_H0_f0': 0.12, 'PersImage_H0_f1': 0.45, ...}
    h0_barcode = barcodes['H0']  # Connected components
    h1_barcode = barcodes['H1']  # Loops/tunnels
    h2_barcode = barcodes['H2']  # Voids/cavities

    print(f"Feature vector shape: {pi_features.shape}")
    print(f"H1 features: {len(h1_barcode)} persistence pairs")

Multi-Method Extraction
-----------------------

::

    from medtda import FeatureExtractor

    # Create extractor
    extractor = FeatureExtractor(normalize=True)

    # Configure multiple methods
    extractor.enable_vectorization_methods([
        'PersStats',
        'BettiCurve',
        'PersImage',
        'PersLandscape'
    ])

    # Customize parameters for each
    extractor.set_vectorization_params('BettiCurve', resolution=150)
    extractor.set_vectorization_params('PersImage', resolution=25, bandwidth=0.15)
    extractor.set_vectorization_params('PersLandscape', num_landscapes=10)

    # Extract all features — returns flat dict of individual feature values
    features = extractor.execute('image.nii.gz')

    # Use in machine learning
    import numpy as np
    feature_vector = np.array(list(features.values()))

See Also
========

* :doc:`preprocessor` - Preprocessing details
* :doc:`barcodeextractor` - Raw barcode extraction
* :doc:`vectorizers` - Vectorization methods
* :doc:`../user_guide` - User guide and tutorials
* :doc:`../quickstart` - Quick start examples
