Frequently Asked Questions

This page addresses common questions, issues, and best practices when using MedTDA.

Installation and Setup

How do I install MedTDA?

Basic installation:

pip install medtda

From source:

git clone https://github.com/dashtiali/medtda.git
cd medtda
pip install -e .

With optional dependencies:

pip install medtda[visualization,notebook]

See Installation for detailed instructions.

What are the system requirements?

Minimum:

  • Python 3.10+

  • 4 GB RAM

  • NumPy, SciPy

Recommended:

  • Python 3.9+

  • 16 GB RAM

  • GUDHI, scikit-learn

  • GPU (for large-scale processing)

Can I use MedTDA on Windows/Mac/Linux?

Yes! MedTDA is cross-platform and works on:

  • Windows 10/11

  • macOS 10.14+

  • Linux (Ubuntu 18.04+, CentOS 7+, etc.)

Installation is the same on all platforms via pip.

Dependencies won’t install - what should I do?

Common solutions:

  1. Update pip:

    pip install --upgrade pip
    
  2. Use conda for difficult packages:

    conda install -c conda-forge gudhi numpy scipy
    
  3. Check Python version:

    python --version  # Should be 3.8+
    
  4. Install build tools (if compiling from source):

    • Linux: sudo apt-get install build-essential

    • macOS: xcode-select --install

    • Windows: Install Visual Studio Build Tools

Getting Started

Where should I start?

  1. Read the Quick Start (5-minute introduction)

  2. Try the Interactive Tutorial (interactive tutorial)

  3. Explore Basic Usage (practical examples)

  4. Consult User Guide for in-depth coverage

What file formats are supported?

3D Medical Images:

  • NIFTI (.nii, .nii.gz) ✓ Recommended

  • DICOM (.dcm) ✓

  • NRRD (.nrrd, .nhdr) ✓

  • Analyze (.hdr/.img) ✓

2D Images:

  • PNG (.png) ✓

  • TIFF (.tiff, .tif) ✓

  • JPEG (.jpg, .jpeg) ✓

Arrays:

  • NumPy arrays (directly in Python) ✓

See Loaders for loading different formats.

Can I use 2D images?

Yes! MedTDA works with any dimensionality:

  • 2D images: H0 (connected components) and H1 (loops)

  • 3D images: H0, H1, and H2 (voids)

For 2D, set max_dimension=1:

from medtda import FeatureExtractor

extractor = FeatureExtractor(max_dimension=1)
features = extractor.execute('2d_image.png')

Common Errors and Solutions

ValueError: Image and mask shapes don’t match

Problem: Image and mask have different dimensions.

Solution:

  1. Check shapes:

    from medtda.loaders import load_image, load_mask
    
    image, _ = load_image('image.nii.gz')
    mask, _ = load_mask('mask.nii.gz')
    
    print(f"Image: {image.shape}")
    print(f"Mask: {mask.shape}")
    
  2. Resample mask to match image:

    from medtda.utils import resample_mask
    
    mask_resampled = resample_mask(mask, image.shape)
    
  3. Verify loading:

    from medtda.loaders import validate_compatibility
    
    validate_compatibility(image, mask)
    

MemoryError: Unable to allocate array

Problem: Image too large for available RAM.

Solutions:

  1. Downsample the image:

    extractor = FeatureExtractor(
        spacing=(2.0, 2.0, 2.0)  # Halve resolution each axis
    )
    
  2. Crop to ROI:

    extractor = FeatureExtractor(
        crop_to_roi=True,
        roi_padding=5
    )
    
  3. Process 2D slices instead of 3D volume

  4. Use a machine with more RAM

Empty barcodes / No features detected

Problem: Persistent homology returns no features.

Possible causes:

  1. ROI too small or homogeneous:

    # Check ROI size
    import numpy as np
    roi_voxels = np.sum(mask > 0)
    print(f"ROI voxels: {roi_voxels}")
    
  2. Wrong filtration type:

    # Try both sublevel and superlevel
    extractor_sub = FeatureExtractor(filtration_type='sublevel')
    extractor_sup = FeatureExtractor(filtration_type='superlevel')
    
  3. Need normalization:

    extractor = FeatureExtractor(normalize=True)
    

RuntimeError: GUDHI failed to compute

Problem: Underlying GUDHI library error.

Solutions:

  1. Check image values:

    import numpy as np
    
    # Check for NaN/Inf
    if np.isnan(image).any():
        image = np.nan_to_num(image, nan=0.0)
    
    if np.isinf(image).any():
        image = np.nan_to_num(image, posinf=0.0, neginf=0.0)
    
  2. Normalize first:

    extractor = FeatureExtractor(
        normalize=True,
        normalize_method='robust'
    )
    
  3. Reduce max_dimension:

    extractor = FeatureExtractor(max_dimension=1)  # Skip H2
    

Feature Extraction Questions

Which vectorization method should I use?

Quick guide:

  • Need interpretable features?PersStats

  • For machine learning?PersImage

  • Want to see evolution over scale?BettiCurve

  • Statistical analysis?PersLandscape

  • Single complexity measure?EntropySummary

How do I choose between sublevel and superlevel?

Sublevel filtration (default):

  • Grows components from low to high intensities

  • Good for dark features (e.g., vessels in angiography, cells in microscopy)

  • Most common choice

Superlevel filtration:

  • Grows components from high to low intensities

  • Good for bright features (e.g., lesions in T2 MRI, enhancing tumors)

Try both and visualize to decide:

from medtda import FeatureExtractor

# Sublevel
extractor_sub = FeatureExtractor(filtration_type='sublevel')
features_sub, barcodes_sub = extractor_sub.execute(image, return_barcodes=True)

# Superlevel
extractor_sup = FeatureExtractor(filtration_type='superlevel')
features_sup, barcodes_sup = extractor_sup.execute(image, return_barcodes=True)

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

What dimensions should I compute (H0, H1, H2)?

Homology dimensions:

  • H0 (dimension 0): Connected components

    • Always present

    • Counts separate objects/regions

  • H1 (dimension 1): Loops/holes

    • Cavities, vessels, circular structures

    • Most informative for many applications

  • H2 (dimension 2): Voids/cavities

    • Only for 3D images

    • Computationally expensive

    • Often less informative than H0/H1

Recommendations:

  • 2D images: max_dimension=1 (H0, H1)

  • 3D exploratory analysis: max_dimension=1 (faster)

  • 3D detailed analysis: max_dimension=2 (complete)

# Fast: H0 and H1 only
extractor = FeatureExtractor(max_dimension=1)

# Complete: H0, H1, and H2
extractor = FeatureExtractor(max_dimension=2)

How many features will I get?

It depends on the vectorization method:

Method

Feature Count

Notes

PersStats

13

Fixed size

BettiCurve

100 (default)

Configurable via resolution

PersImage

400 (20×20)

resolution²

PersLandscape

500 (5×100)

num_landscapes × resolution

EntropySummary

1

Single value per dimension

PersSilhouette

100 (default)

Configurable

Configuring size:

extractor = FeatureExtractor(vectorization_method='PersImage')

extractor.set_vectorization_params(
    'PersImage',
    resolution=30  # 30×30 = 900 features
)

Performance and Optimization

How long does processing take?

Typical times (256×256×128 image on standard desktop):

  • Preprocessing: 1-5 seconds

  • Persistent homology: 5-30 seconds

  • Vectorization: 0.1-2 seconds

Total: ~10-40 seconds per image

Factors affecting speed:

  • Image size (biggest factor)

  • Number of voxels in ROI

  • max_dimension (H2 is slow)

  • Vectorization method

  • Hardware (CPU, RAM)

How can I speed up processing?

Top strategies:

  1. Downsample images:

    extractor = FeatureExtractor(spacing=(2.0, 2.0, 2.0))
    
  2. Crop to ROI:

    extractor = FeatureExtractor(crop_to_roi=True)
    
  3. Reduce max_dimension:

    extractor = FeatureExtractor(max_dimension=1)  # Skip H2
    
  4. Use faster vectorization:

    extractor = FeatureExtractor(
        vectorization_method='PersStats'  # Fastest
    
  5. Parallel batch processing:

    from multiprocessing import Pool
    
    with Pool(processes=8) as pool:
        results = pool.map(process_image, image_paths)
    

How much RAM do I need?

Estimate:

  • Small images (128³): 2-4 GB

  • Medium images (256³): 8-16 GB

  • Large images (512³): 32-64 GB

Reduce memory usage:

  1. Downsample with spacing

  2. Crop with crop_to_roi=True

  3. Process one image at a time

  4. Use max_dimension=1

Can I use GPU acceleration?

Currently, MedTDA uses CPU-based libraries (GUDHI, NumPy). GPU acceleration is not yet supported but is planned for future releases.

For batch processing, CPU multiprocessing provides good parallelization:

from medtda import FeatureExtractor
from multiprocessing import Pool

def process(img_path):
    extractor = FeatureExtractor()
    return extractor.execute(img_path)

with Pool(8) as pool:  # 8 parallel processes
    results = pool.map(process, image_paths)

Machine Learning Integration

How do I integrate features with scikit-learn?

from medtda import FeatureExtractor
from sklearn.ensemble import RandomForestClassifier
import numpy as np

# Extract features for all samples
extractor = FeatureExtractor(
    normalize=True,
    vectorization_method='PersImage'
)

X = []
for img_path in training_images:
    features = extractor.execute(img_path)
    # features is a flat dict: {'PersImage_H0_f0': ..., 'PersImage_H1_f0': ..., ...}
    X.append(list(features.values()))

X = np.array(X)

# Train
clf = RandomForestClassifier()
clf.fit(X, y_train)

See Batch Workflow for complete examples.

Should I normalize features?

Image normalization: Yes, always recommended:

extractor = FeatureExtractor(normalize=True)

Feature scaling for ML:

from sklearn.preprocessing import StandardScaler

scaler = StandardScaler()
X_train_scaled = scaler.fit_transform(X_train)
X_test_scaled = scaler.transform(X_test)

How do I do feature selection?

from sklearn.feature_selection import SelectKBest, f_classif

# Select top 50 features
selector = SelectKBest(f_classif, k=50)
X_train_selected = selector.fit_transform(X_train, y_train)
X_test_selected = selector.transform(X_test)

# Get selected feature indices
selected_idx = selector.get_support()

Can I combine multiple vectorization methods?

Yes! This often improves performance:

from medtda import FeatureExtractor
import numpy as np

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

features = extractor.execute(image)

# features is a flat dict with all method features combined
# e.g., {'PersStats_H0_mean': ..., 'BettiCurve_H0_f0': ..., 'PersImage_H0_f0': ...}
combined = np.array(list(features.values()))

Results Interpretation

What do the barcode plots mean?

Persistence barcodes show features as horizontal bars:

  • X-axis: Filtration value (intensity threshold)

  • Length: Persistence (how long feature survives)

  • Longer bars = more significant features

  • Short bars = noise

By dimension:

  • H0 bars: Connected components merging

  • H1 bars: Loops appearing and disappearing

  • H2 bars: Voids filling

See Barcodes and Diagrams for details.

How do I interpret persistence statistics?

The PersStats method returns 13 values per dimension:

  1. Count: Number of features

  2. Mean: Average persistence

  3. Std: Standard deviation of persistence

  4. Min: Smallest persistence

  5. 25%: First quartile

  6. 50%: Median

  7. 75%: Third quartile

8. Max: Largest persistence 9-13. Additional statistics (skewness, kurtosis, etc.)

Interpretation:

  • High count: Many topological features

  • High mean/max: Prominent features

  • High std: Diverse feature scales

What’s a “good” persistence value?

It depends on your application!

  • Absolute values depend on image intensities and normalization

  • Relative comparisons are meaningful (within same preprocessing)

  • Statistical significance: Compare to random/null distribution

General guidance:

  • Features with persistence < 1% of max are often noise

  • Focus on top 10-20% most persistent features

  • Domain knowledge helps interpret what’s “significant”

Why are my features all similar/different between groups?

All similar (low variance):

  1. Images are actually similar (expected)

  2. ROIs too small → extract larger context

  3. Wrong filtration type → try both sublevel/superlevel

  4. Need different vectorization method

Too different (high variance):

  1. Preprocessing inconsistent → standardize pipeline

  2. Image quality varies → add quality control

  3. ROI placement varies → improve segmentation

  4. Outliers present → check and remove

Troubleshooting

Code runs but gives unexpected results

Checklist:

  1. ✓ Image loaded correctly?

  2. ✓ Mask matches image?

  3. ✓ Normalization applied?

  4. ✓ Correct filtration type?

  5. ✓ ROI not empty?

  6. ✓ Preprocessing consistent?

Debug by visualizing:

import matplotlib.pyplot as plt
from medtda.plotting import plot_persistence_diagram

# Visualize processed image
plt.imshow(processed[:, :, processed.shape[2]//2], cmap='gray')
plt.show()

# Plot persistence diagram
_, barcodes = extractor.execute(image, return_barcodes=True)
plot_persistence_diagram(barcodes)
plt.show()

“Module not found” errors

Problem: Import fails.

Solutions:

  1. Verify installation:

    pip list | grep medtda
    
  2. Reinstall:

    pip uninstall medtda
    pip install medtda
    
  3. Check Python environment:

    which python
    which pip
    
  4. Use correct environment:

    # Activate environment first
    conda activate myenv
    # Then install
    pip install medtda
    

Processing hangs / takes forever

Possible causes:

  1. Image too large → downsample or crop

  2. Computing H2 on large image → use max_dimension=1

  3. Very complex image → increase spacing to reduce resolution

  4. Insufficient RAM → close other programs, use smaller images

Add timeout:

import signal

def timeout_handler(signum, frame):
    raise TimeoutError("Processing took too long")

signal.signal(signal.SIGALRM, timeout_handler)
signal.alarm(300)  # 5 minute timeout

try:
    features = extractor.execute(image)
except TimeoutError:
    print("Processing timed out")
finally:
    signal.alarm(0)

Where to Get Help

I read the FAQ but still have questions

  1. Search documentation: Use search box (top right)

  2. Check examples: Examples

  3. GitHub Issues: Search existing issues

  4. Ask a question: Open new issue

  5. Discussions: GitHub Discussions

How do I report a bug?

Open a bug report with:

  1. MedTDA version: medtda.__version__

  2. Python version: python --version

  3. Operating system

  4. Minimal code to reproduce

  5. Error message / unexpected output

  6. Expected behavior

Can I request a feature?

Yes! Open a feature request describing:

  1. What you want to do

  2. Why current functionality doesn’t work

  3. Proposed solution (if any)

  4. Example use case

How can I contribute?

See Contributing to MedTDA for:

  • Code contributions

  • Documentation improvements

  • Bug reports

  • Feature suggestions

  • Example notebooks

See Also