Quick Start

This guide will get you up and running with Med-TDA in 5 minutes.

Installation

If you haven’t already, install Med-TDA:

pip install medtda

All dependencies (including SimpleITK for medical images) will be installed automatically.

Basic Usage: Single Image

Extract TDA features from a single medical image with a few lines of code:

from medtda import FeatureExtractor

# Initialize the feature extractor
extractor = FeatureExtractor(
    normalize=True,
    vectorization_method='PersStats'
)

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

# features is a dictionary of TDA features
print(f"Extracted {len(features)} features")
print(list(features.keys())[:5])  # Show first 5 feature names

Expected Output:

Extracted 114 features
['PersStats_H0_mean', 'PersStats_H0_std',
 'PersStats_H0_min', 'PersStats_H0_max',
 'PersStats_H0_median']

Working with 2D Images

MedTDA also works with 2D images (PNG, JPG, TIFF):

from medtda import FeatureExtractor
import numpy as np
from PIL import Image

# Load 2D image
image = np.array(Image.open('image.png').convert('L'))

# Extract features
extractor = FeatureExtractor(
    normalize=True,
    vectorization_method='PersStats'
)
features = extractor.execute(image)

Without a Mask

You can extract features without a mask:

# Extract features from full image (no mask)
features = extractor.execute(image='image.nii.gz')

Using NumPy Arrays

Pass NumPy arrays directly instead of file paths:

import numpy as np
import SimpleITK as sitk

# Load image as NumPy array
image_sitk = sitk.ReadImage('image.nii.gz')
image_array = sitk.GetArrayFromImage(image_sitk)

# Extract features from array
features = extractor.execute(image=image_array)

Multiple Vectorization Methods

Extract features using multiple vectorization methods simultaneously:

extractor = FeatureExtractor(
    normalize=True,
    vectorization_method=[
        'PersStats',
        'BettiCurve',
        'EntropySummary'
    ]
)

features = extractor.execute('image.nii.gz', 'mask.nii.gz')
print(f"Total features from all methods: {len(features)}")

This combines features from all specified methods into a single dictionary.

Preprocessing Options

Customize preprocessing with various options:

extractor = FeatureExtractor(
    # Preprocessing
    spacing=(1.0, 1.0, 1.0),      # Resample to 1mm isotropic
    window=(40, 400),              # CT windowing (center, width)
    normalize=True,                # Normalize intensities
    normalize_method='minmax',     # Normalization method
    crop_to_roi=True,             # Crop to ROI for efficiency
    roi_padding=2,                # Padding around ROI

    # Persistent Homology
    filtration_type='sublevel',   # Filtration type
    construction='T',             # Cubical complex construction
    max_dimension=2,              # Compute H0, H1, H2

    # Vectorization
    vectorization_method='PersStats'
)

features = extractor.execute('image.nii.gz', 'mask.nii.gz')

Getting Barcodes

Return persistence barcodes along with features:

extractor = FeatureExtractor(
    normalize=True,
    vectorization_method='PersStats',
    return_barcodes=True  # Return barcodes
)

# Now returns a tuple: (features, barcodes)
features, barcodes = extractor.execute('image.nii.gz', 'mask.nii.gz')

# barcodes is a dictionary: {'H0': array, 'H1': array, 'H2': array}
# where keys are homology dimension labels
print(f"H0 features: {barcodes['H0'].shape}")  # (n_features, 2) birth-death pairs
print(f"H1 features: {barcodes['H1'].shape}")

Batch Processing (Python)

Process multiple images in a loop:

import pandas as pd
from medtda import FeatureExtractor

# List of images to process
cases = [
    {'id': 'case001', 'image': 'scan1.nii.gz', 'mask': 'mask1.nii.gz'},
    {'id': 'case002', 'image': 'scan2.nii.gz', 'mask': 'mask2.nii.gz'},
    {'id': 'case003', 'image': 'scan3.nii.gz', 'mask': 'mask3.nii.gz'},
]

# Initialize extractor
extractor = FeatureExtractor(
    normalize=True,
    vectorization_method='PersStats'
)

# Extract features for all cases
results = []
for case in cases:
    try:
        features = extractor.execute(case['image'], case['mask'])
        features['id'] = case['id']
        results.append(features)
    except Exception as e:
        print(f"Error processing {case['id']}: {e}")

# Convert to DataFrame
df = pd.DataFrame(results)
print(df.head())

# Save to CSV
df.to_csv('tda_features.csv', index=False)

Command-Line Interface

MedTDA provides a powerful CLI for batch processing:

Single File

# Basic usage
medtda image.nii.gz --output-dir ./results

# With mask and preprocessing
medtda image.nii.gz --mask mask.nii.gz --output-dir ./results \
    --normalize --spacing 1.0 1.0 1.0 \
    --methods persistence_stats betti_curve

Batch Processing

Create a CSV file with your cases:

id,image_path,mask_path
case001,/data/scan1.nii.gz,/data/mask1.nii.gz
case002,/data/scan2.nii.gz,/data/mask2.nii.gz
case003,/data/scan3.nii.gz,/data/mask3.nii.gz

Process all cases:

# Sequential processing
medtda cases.csv --output-dir ./results --normalize --verbose

# Parallel processing with 4 workers
medtda cases.csv --output-dir ./results --workers 4 --verbose

# Use all CPU cores
medtda cases.csv --output-dir ./results --workers -1

Configuration Files

Use YAML configuration files for reproducibility:

# config.yaml
preprocessing:
  normalize: true
  normalize_method: minmax
  spacing: [1.0, 1.0, 1.0]
  crop_roi: true

persistent_homology:
  filtration: sublevel
  max_dimension: 2

vectorization:
  methods:
    - persistence_stats
    - betti_curve

output:
  output_dir: ./results

Then run:

medtda cases.csv --config config.yaml

Outputs

Single File Processing

When processing a single file, MedTDA creates:

  • image_name_features.csv - Feature vectors (one row)

  • image_name_config.yaml - Configuration used (for reproducibility)

  • image_name_barcodes.pkl - Persistence barcodes (if --save-barcodes)

Batch Processing

When processing multiple files, MedTDA creates:

  • batch_features.csv - All features combined (one row per case)

  • config.yaml - Configuration used

  • case_id_barcodes.pkl - Individual barcodes (if --save-barcodes)

Using Features in Machine Learning

The extracted features are ready for machine learning:

import pandas as pd
from sklearn.ensemble import RandomForestClassifier
from sklearn.model_selection import train_test_split

# Load extracted features
features_df = pd.read_csv('batch_features.csv')

# Prepare data (assuming you have labels)
X = features_df.drop(['id', 'label'], axis=1)
y = features_df['label']

# Split data
X_train, X_test, y_train, y_test = train_test_split(
    X, y, test_size=0.2, random_state=42
)

# Train classifier
clf = RandomForestClassifier(n_estimators=100, random_state=42)
clf.fit(X_train, y_train)

# Evaluate
score = clf.score(X_test, y_test)
print(f"Accuracy: {score:.3f}")

Next Steps

Now that you know the basics:

Common Next Questions

How do I choose a vectorization method?

See Vectorization for guidance on selecting methods.

What preprocessing should I use?

See Preprocessing for detailed preprocessing options.

How does persistent homology work?

See Persistent Homology for TDA background.

How can I visualize persistence diagrams?

See Basic Usage for visualization examples.

My processing is slow, how can I speed it up?

See Frequently Asked Questions for performance optimization tips.