How to Choose a Population AIF¶
Select the appropriate population-based arterial input function for DCE-MRI analysis.
Via CLI (YAML Config)¶
Set population_aif in your pipeline config:
modality: dce
pipeline:
model: extended_tofts
aif_source: population
population_aif: parker # or georgiou, fritz_hansen, weinmann, mcgrath
Available values: parker, georgiou, fritz_hansen, weinmann, mcgrath. See the characteristics table below to choose.
Via Python API¶
osipy provides five population AIF models:
import osipy
import numpy as np
time = np.linspace(0, 300, 100) # seconds
# Parker AIF (most widely used)
parker = osipy.ParkerAIF()(time)
# Georgiou AIF (broader peak)
georgiou = osipy.GeorgiouAIF()(time)
# Fritz-Hansen AIF (earlier model)
fritz_hansen = osipy.FritzHansenAIF()(time)
# Weinmann AIF (classic bi-exponential)
weinmann = osipy.get_population_aif("weinmann")(time)
# McGrath AIF (preclinical, small animal)
mcgrath = osipy.get_population_aif("mcgrath")(time)
Compare AIF Shapes¶
Visualize the differences between population AIFs:
import matplotlib.pyplot as plt
fig, ax = plt.subplots(figsize=(10, 5))
ax.plot(time, parker.concentration, 'r-', linewidth=2, label='Parker')
ax.plot(time, georgiou.concentration, 'b-', linewidth=2, label='Georgiou')
ax.plot(time, fritz_hansen.concentration, 'g-', linewidth=2, label='Fritz-Hansen')
ax.set_xlabel('Time (s)')
ax.set_ylabel('Concentration (mM)')
ax.set_title('Population AIF Comparison')
ax.legend()
ax.grid(True, alpha=0.3)
plt.show()
AIF Characteristics¶
| Model | Shape | Best For |
|---|---|---|
| Parker | Dual Gaussian + exponential tail | Standard DCE, high temporal resolution |
| Georgiou | Dual Gaussian + exponential decay | Lower temporal resolution, smoother |
| Fritz-Hansen | Bi-exponential decay | Cardiac perfusion, early studies |
| Weinmann | Bi-exponential decay | Classic reference, Gd-DTPA pharmacokinetics |
| McGrath | Gamma-variate + exponential washout | Preclinical (small animal) studies |
Selection Guidelines¶
All five AIFs at a glance:
# Default for most applications
aif = osipy.ParkerAIF()(time)
# For slower acquisitions (TR > 5s)
aif = osipy.GeorgiouAIF()(time)
# For cardiac perfusion or historical comparison
aif = osipy.FritzHansenAIF()(time)
# Classic Gd-DTPA reference
aif = osipy.get_population_aif("weinmann")(time)
# Preclinical (small animal) studies
aif = osipy.get_population_aif("mcgrath")(time)
Use Parker AIF When:¶
- You have standard DCE acquisition (TR < 5s)
- Measuring tumors, muscle, or general tissues
- This is your first analysis (most validated)
Use Georgiou AIF When:¶
- Lower temporal resolution (TR > 5s)
- Want smoother concentration curves
- Bolus injection was slow
Use Fritz-Hansen AIF When:¶
- Cardiac perfusion studies
- Comparing with older literature
Use Weinmann AIF When:¶
- Reproducing classic Gd-DTPA pharmacokinetic studies
- Comparing with Weinmann et al. (1984) reference data
Use McGrath AIF When:¶
- Preclinical (small animal) DCE-MRI studies
- Rodent models where human population AIFs are inappropriate
- Need a gamma-variate + exponential AIF model
Scale AIF for Injection Dose¶
Adjust AIF for different contrast agent doses:
from osipy.common.types import AIFType
# Standard dose is 0.1 mmol/kg
# For half dose (0.05 mmol/kg):
dose_factor = 0.05 / 0.1
aif = osipy.ParkerAIF()(time)
aif_scaled = osipy.ArterialInputFunction(
time=time,
concentration=aif.concentration * dose_factor,
aif_type=AIFType.MEASURED,
)
Adjust for Hematocrit¶
Account for blood hematocrit:
from osipy.common.types import AIFType
def adjust_aif_hematocrit(aif, hct=0.45):
"""Adjust AIF for hematocrit.
Contrast agent distributes in plasma, not whole blood.
Standard AIF assumes Hct = 0.45.
"""
# Plasma fraction
plasma_fraction = 1 - hct
standard_plasma = 1 - 0.45
# Scale concentration
scale = standard_plasma / plasma_fraction
adjusted_conc = aif.concentration * scale
return osipy.ArterialInputFunction(
time=aif.time,
concentration=adjusted_conc,
aif_type=AIFType.MEASURED,
)
# For patient with Hct = 0.35
aif_adjusted = adjust_aif_hematocrit(parker, hct=0.35)
Compare Fitting Results¶
Evaluate which AIF works best for your data:
import osipy
# Try different AIFs
aifs = {
'Parker': osipy.ParkerAIF()(time),
'Georgiou': osipy.GeorgiouAIF()(time),
}
results = {}
for name, aif in aifs.items():
result = osipy.fit_model(
"extended_tofts",
concentration=concentration,
aif=aif,
time=time,
mask=mask
)
results[name] = result
# Compare R² values
for name, result in results.items():
valid = result.quality_mask > 0
mean_r2 = result.r_squared_map[valid].mean()
print(f"{name} AIF: Mean R² = {mean_r2:.4f}")
AIF Properties¶
Access AIF characteristics:
aif = osipy.ParkerAIF()(time)
# Peak information
print(f"Peak concentration: {aif.peak_concentration:.2f} mM")
print(f"Peak time: {aif.peak_time:.1f} s")
# Access raw data
print(f"Time points: {len(aif.time)}")
print(f"Concentration array: {aif.concentration.shape}")
When to Use Population vs Measured AIF¶
| Scenario | Recommendation |
|---|---|
| No visible artery in FOV | Population AIF |
| Large artery available | Measured AIF |
| Comparing across subjects | Population AIF (consistent) |
| Absolute quantification | Measured AIF (more accurate) |
| Rapid screening | Population AIF |
| Research publication | Consider both, report which |
Common Issues¶
AIF Peak Too Early/Late¶
Shift AIF using the shift_aif utility:
from osipy.common.aif import shift_aif
# Shift AIF by a known delay (in seconds)
aif = osipy.ParkerAIF()(time)
shifted_conc = shift_aif(aif.concentration, time, delay=10.0, xp=np)
Alternatively, use fit_delay=True to estimate the delay automatically during model fitting:
result = osipy.fit_model(
"extended_tofts", concentration, aif, time,
fit_delay=True # Estimates per-voxel delay
)
delay_map = result.parameter_maps["delay"].values # seconds
Mismatch with Measured Data¶
Scale AIF to match observed peak concentration:
# If population AIF doesn't match your observed curves,
# consider:
# 1. Different injection rate
# 2. Cardiac output differences
# 3. Acquisition timing issues
# Scale to match observed peak
observed_peak = 4.0 # mM
scale = observed_peak / aif.peak_concentration
aif_scaled = osipy.ArterialInputFunction(
time=aif.time,
concentration=aif.concentration * scale,
aif_type=AIFType.MEASURED,
)