.. _api_preprocessor:

============
Preprocessor
============

.. currentmodule:: medtda

The ``Preprocessor`` class handles image preprocessing operations including normalization, resampling, windowing, and masking.

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

Overview
========

The ``Preprocessor`` class provides a unified interface for all preprocessing operations on medical images. It's used internally by :class:`FeatureExtractor` but can also be used standalone for more control.

**Key Features:**

* Intensity normalization (minmax, z-score, robust)
* Image resampling to target spacing
* CT windowing
* ROI cropping with configurable padding
* Multi-label mask handling
* Background detection and masking

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

Default Preprocessing
---------------------

::

    from medtda import Preprocessor

    preprocessor = Preprocessor(normalize=True)
    
    processed_image, metadata = preprocessor.preprocess(image_array)

With Mask and ROI Cropping
---------------------------

::

    preprocessor = Preprocessor(
        normalize=True,
        normalize_method='robust',
        crop_to_roi=True,
        roi_padding=5
    )
    
    processed_image, metadata = preprocessor.preprocess(
        image=image_array,
        mask=mask_array
    )

CT Image with Windowing
-----------------------

::

    # CT abdominal scan with soft tissue window
    preprocessor = Preprocessor(
        window=(40, 400),  # (center, width)
        normalize=True,
        spacing=(1.0, 1.0, 1.0)
    )
    
    processed_image, metadata = preprocessor.preprocess(ct_image)

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

.. autoclass:: medtda.Preprocessor
   :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)`` for isotropic 1mm)
   * - ``window``
     - tuple or None
     - CT windowing as ``(center, width)``
   * - ``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``)

Methods
=======

preprocess
----------

.. automethod:: medtda.Preprocessor.preprocess
   :noindex:

Main preprocessing method that applies all configured operations.

**Signature:**

.. code-block:: python

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

**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:**

* **preprocessed_image** (*np.ndarray*) - Preprocessed image
* **metadata** (*dict*) - Dictionary with keys: ``'original_range'``, ``'final_range'``,
  ``'transforms'``, ``'background_value'``, ``'original_shape'``, ``'final_shape'``,
  ``'label'``, ``'crop_info'``

**Processing Order:**

1. Resampling (if ``spacing`` is set)
2. ROI cropping (if ``crop_to_roi=True`` and mask provided)
3. Windowing (if ``window`` is set)
4. Normalization (if ``normalize=True``)
5. Background masking (if mask provided)

**Example:**

::

    preprocessor = Preprocessor(
        spacing=(1.0, 1.0, 1.0),
        normalize=True,
        crop_to_roi=True
    )
    
    processed, metadata = preprocessor.preprocess(image, mask)

Normalization Methods
=====================

minmax
------

Scales intensities to [0, 1] range::

    preprocessor = Preprocessor(normalize_method='minmax')

**Formula:** ``(x - min) / (max - min)``

zscore
------

Z-score normalization (mean=0, std=1)::

    preprocessor = Preprocessor(normalize_method='zscore')

**Formula:** ``(x - mean) / std``

robust
------

Robust normalization using percentiles::

    preprocessor = Preprocessor(normalize_method='robust')

**Formula:** ``(x - median) / IQR`` where IQR is the interquartile range (75th - 25th percentile)

Windowing
=========

CT Windowing for Different Tissues
-----------------------------------

::

    # Soft tissue window
    preprocessor_soft = Preprocessor(window=(40, 400))

    # Lung window
    preprocessor_lung = Preprocessor(window=(-600, 1500))

    # Bone window
    preprocessor_bone = Preprocessor(window=(300, 1500))

    # Brain window
    preprocessor_brain = Preprocessor(window=(40, 80))

Window values are specified as ``(center, width)``.

ROI Cropping
============

Crop to Bounding Box with Padding
----------------------------------

::

    preprocessor = Preprocessor(
        crop_to_roi=True,
        roi_padding=10  # 10 pixels padding on all sides
    )
    
    # Crops to ROI bounding box with 10-pixel border
    cropped = preprocessor.preprocess(image, mask)

Multi-Label Mask Handling
==========================

Extract Specific Label
----------------------

::

    # Multi-label mask: 0=background, 1=liver, 2=tumor
    preprocessor = Preprocessor(
        label=2,  # Extract tumor only
        crop_to_roi=True
    )
    
    # Only processes the tumor region
    tumor_region = preprocessor.preprocess(image, multi_label_mask)

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

Comprehensive Preprocessing Pipeline
-------------------------------------

::

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

    # Load data
    image, img_metadata = load_image('ct_scan.nii.gz')
    mask, mask_metadata = load_mask('liver_mask.nii.gz')

    # Configure preprocessor
    preprocessor = Preprocessor(
        # Resample to isotropic 1mm spacing
        spacing=(1.0, 1.0, 1.0),
        
        # Apply soft tissue window (CT)
        window=(40, 400),
        
        # Robust normalization (handles artifacts well)
        normalize=True,
        normalize_method='robust',
        
        # Crop to liver ROI with padding
        crop_to_roi=True,
        roi_padding=5
    )

    # Preprocess
    processed_image, metadata = preprocessor.preprocess(image, mask)

    print(f"Original shape: {image.shape}")
    print(f"Processed shape: {processed_image.shape}")
    print(f"Value range: [{processed_image.min():.3f}, {processed_image.max():.3f}]")

Step-by-Step Preprocessing
---------------------------

For manual step-by-step control, individual preprocessing functions from the ``utils`` module can be used::

    from medtda.utils import (
        resample_image,
        normalize_image,
        crop_to_roi,
        extract_label_from_mask
    )

    # Step 1: Resample
    resampled = resample_image(
        image, 
        original_spacing=(1.5, 1.5, 3.0),
        target_spacing=(1.0, 1.0, 1.0)
    )

    # Step 2: Extract label from mask
    binary_mask = extract_label_from_mask(multi_label_mask, label=2)

    # Step 3: Crop to ROI
    cropped_image, cropped_mask = crop_to_roi(
        resampled,
        binary_mask,
        padding=5
    )

    # Step 4: Normalize
    normalized = normalize_image(
        cropped_image,
        method='robust',
        mask=cropped_mask
    )

Common Patterns
===============

Pattern 1: Minimal Preprocessing
---------------------------------

Just normalization::

    preprocessor = Preprocessor(normalize=True)
    processed = preprocessor.preprocess(image)

Pattern 2: CT Preprocessing
----------------------------

Resampling + windowing + normalization::

    preprocessor = Preprocessor(
        spacing=(1.0, 1.0, 1.0),
        window=(40, 400),
        normalize=True
    )
    processed = preprocessor.preprocess(ct_image)

Pattern 3: ROI-Focused Analysis
--------------------------------

Extract and crop to specific region::

    preprocessor = Preprocessor(
        label=2,  # Extract tumor
        crop_to_roi=True,
        roi_padding=10,
        normalize=True,
        normalize_method='robust'
    )
    processed = preprocessor.preprocess(image, multi_label_mask)

Pattern 4: No Preprocessing
----------------------------

Use raw image values::

    preprocessor = Preprocessor(normalize=False)
    processed = preprocessor.preprocess(image)
    # Returns image unchanged (just validates it)

See Also
========

* :doc:`featureextractor` - Main feature extraction interface
* :doc:`utils` - Individual preprocessing utility functions
* :doc:`../user_guide/preprocessing` - Detailed preprocessing guide
* :doc:`loaders` - Image loading and validation
