A Python library for IVIM diffusion MRI model fitting
Project description
IVIMfit
IVIMfit is a modular Python library designed to fit intravoxel incoherent motion (IVIM) models to diffusion-weighted MRI (DWI) signals. It includes support for monoexponential, biexponential (free and segmented), triexponential, and Bayesian model fitting. The package is designed for use in clinical and research settings, allowing users to extract physiologically meaningful diffusion parameters from DWI datasets. User can generate synhtethic data.
📦 Installation
Install from source:
git clone https://github.com/Atakanisik/IVIMfit
cd IVIMfit
pip install -e .
Install from pip:
pip install ivimfit
Requirements:
- numpy
- scipy
- matplotlib
- pymc
- pytensor
Supported Models
Monoexponential (ADC)
$$ S(b) = S_0 \cdot e^{-b \cdot ADC} $$
import numpy as np
import matplotlib.pyplot as plt
from ivimfit.utils import plot_fit, calculate_r_squared
from ivimfit.adc import fit_adc, monoexp_model
b = np.array([0, 50, 100, 200, 400, 600, 800])
s = np.array([800, 654,543,423,328,236,121])
adc = fit_adc(b, s)
r2 = calculate_r_squared(s / s[0], monoexp_model(b, adc))
fig, ax = plot_fit(b, s, monoexp_model, [adc], model_name=f"ADC Fit (R² = {r2:.4f})")
plt.show()
Biexponential (Free Fit)
$$ S(b)/S_0 = f \cdot e^{-b D^*} + (1-f) \cdot e^{-b D} $$
import numpy as np
import matplotlib.pyplot as plt
from ivimfit.utils import plot_fit, calculate_r_squared
from ivimfit.biexp import fit_biexp_free, biexp_model
b = np.array([0, 50, 100, 200, 400, 600, 800])
s = np.array([800, 654,543,423,328,236,121])
f, D, D_star = fit_biexp_free(b, s)
r2 = calculate_r_squared(s / s[0], biexp_model(b, f, D, D_star))
fig, ax = plot_fit(b, s, biexp_model, [f, D, D_star], model_name=f"Free Fit (R² = {r2:.4f})")
plt.show()
Biexponential (Segmented Fit)
- Estimates $D$ using high-b values (b>=200 default)
- Then fits $f$, $D^*$ with fixed $D$
import numpy as np
import matplotlib.pyplot as plt
from ivimfit.utils import plot_fit, calculate_r_squared
from ivimfit.segmented import fit_biexp_segmented, biexp_fixed_D_model
b = np.array([0, 50, 100, 200, 400, 600, 800])
s = np.array([800, 654,543,423,328,236,121])
f, D_fixed, D_star = fit_biexp_segmented(b, s)
r2 = calculate_r_squared(s / s[0], biexp_fixed_D_model(b, f, D_star, D_fixed))
fig, ax = plot_fit(
b, s,
lambda b_, f_, D_star_,D_fixed: biexp_fixed_D_model(b_, f_, D_star_, D_fixed),
[f, D_star,D_fixed],
model_name=f"Segmented Fit (R² = {r2:.4f})"
)
plt.show()
Triexponential
S(b)/S₀ = f₁ · exp(–b · D₁*) + f₂ · exp(–b · D₂*) + (1 – f₁ – f₂) · exp(–b · D)
import numpy as np
import matplotlib.pyplot as plt
from ivimfit.utils import plot_fit, calculate_r_squared
from ivimfit.triexp import fit_triexp_free, triexp_model
b = np.array([0, 50, 100, 200, 400, 600, 800])
s = np.array([800, 654,543,423,328,236,121])
f1, f2, D, D1_star, D2_star = fit_triexp_free(b, s)
pred = triexp_model(b, f1, f2, D, D1_star, D2_star)
r2 = calculate_r_squared(s / s[0], pred)
fig, ax = plot_fit(b, s, triexp_model, [f1, f2, D, D1_star, D2_star], model_name=f"Tri-exponential Fit (R² = {r2:.4f})")
plt.show()
Bayesian (MCMC)
import numpy as np
import matplotlib.pyplot as plt
from ivimfit.utils import plot_fit, calculate_r_squared
from ivimfit.bayesian import fit_bayesian
from ivimfit.biexp import biexp_model
b = np.array([0, 50, 100, 200, 400, 600, 800])
s = np.array([800, 654,543,423,328,236,121])
if __name__ == "__main__":
f, D, D_star = fit_bayesian(b, s, draws=500, chains=2)
r2 = calculate_r_squared(s / s[0], biexp_model(b, f, D, D_star))
fig, ax = plot_fit(b, s, biexp_model, [f, D, D_star], model_name=f"Bayesian Fit (R² = {r2:.4f})")
plt.show()
Returns posterior mean estimates for $f$, $D$, and $D^*$.
Whole Package Example Usage
# example.py
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.patches import Rectangle
from ivimfit.synthetic import PhantomParams, generate_measure_signals
from ivimfit.utils import plot_fit, calculate_r_squared
from ivimfit.adc import fit_adc, monoexp_model
from ivimfit.biexp import fit_biexp_free, biexp_model
from ivimfit.segmented import fit_biexp_segmented, biexp_fixed_D_model
def main():
b = np.array([0, 50, 100, 200, 400, 600, 800,900,1000], dtype=float)
pp = PhantomParams(
shape=(128, 128),
square_size=64,
s0_bright=1.0,
s0_dark=0.30,
D_bright=0.0015,
D_dark=0.0030,
noise_sigma=0.02
)
stack, s, roi = generate_measure_signals(b, params=pp, seed=123)
r0, c0, size = roi
img0 = stack[np.argmin(b)]
plt.imshow(img0, cmap="gray", origin="upper")
plt.gca().add_patch(Rectangle((c0, r0), size, size, fill=False, linewidth=2))
plt.title("b (en düşük) + ROI")
plt.show()
from ivimfit.synthetic import show_stack_grid
show_stack_grid(stack, b, roi=roi, cols=4, suptitle="Synthetic DWI (low b → high b)")
adc = fit_adc(b, s)
r2 = calculate_r_squared(s / s[0], monoexp_model(b, adc))
fig, ax = plot_fit(b, s, monoexp_model, [adc], model_name=f"ADC Fit (R² = {r2:.4f})")
plt.show()
f, D, D_star = fit_biexp_free(b, s)
r2 = calculate_r_squared(s / s[0], biexp_model(b, f, D, D_star))
fig, ax = plot_fit(b, s, biexp_model, [f, D, D_star], model_name=f"Free Fit (R² = {r2:.4f})")
plt.show()
f_seg, D_fixed, D_star_seg = fit_biexp_segmented(b, s)
r2 = calculate_r_squared(s / s[0], biexp_fixed_D_model(b, f_seg, D_star_seg, D_fixed))
fig, ax = plot_fit(
b, s,
lambda b_, f_, D_star_, D_fixed_: biexp_fixed_D_model(b_, f_, D_star_, D_fixed_),
[f_seg, D_star_seg, D_fixed],
model_name=f"Segmented Fit (R² = {r2:.4f})"
)
plt.show()
try:
from ivimfit.bayesian import fit_bayesian
f_bay, D_bay, Dstar_bay = fit_bayesian(
b, s, draws=500, chains=2
)
r2 = calculate_r_squared(s / s[0], biexp_model(b, f_bay, D_bay, Dstar_bay))
fig, ax = plot_fit(b, s, biexp_model, [f_bay, D_bay, Dstar_bay],
model_name=f"Bayesian Fit (R² = {r2:.4f})")
plt.show()
except Exception as e:
print("[Bayesian] atlandı:", repr(e))
try:
from ivimfit.triexp import fit_triexp_free, triexp_model
f1, f2, Dm, D1s, D2s = fit_triexp_free(b, s)
pred = triexp_model(b, f1, f2, Dm, D1s, D2s)
r2 = calculate_r_squared(s / s[0], pred)
fig, ax = plot_fit(b, s, triexp_model, [f1, f2, Dm, D1s, D2s],
model_name=f"Tri-exponential Fit (R² = {r2:.4f})")
plt.show()
except Exception as e:
print("[Triexponential] atlandı:", repr(e))
if __name__ == "__main__":
main()
License
This project is licensed under the MIT License.
Contributing
Pull requests are welcome. Please open an issue to discuss your proposed change before submitting a PR.
Acknowledgements
This work was inspired by previous IVIM modeling efforts, including:
- ivim by Jalnefjord et al.
- PyMC Bayesian modeling
- Research on segmented and triexponential IVIM models in liver and kidney imaging
Citation: If you use IVIMfit, please cite it using the following DOI:
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file ivimfit-0.1.7.2.tar.gz.
File metadata
- Download URL: ivimfit-0.1.7.2.tar.gz
- Upload date:
- Size: 12.9 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.14.3
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
36fa8b1859ecb546dd5f241b871f1eca086f8ebdc5922bb06c444c6b48134806
|
|
| MD5 |
0830b8ed18f6f656e7d89cf30cb41ae5
|
|
| BLAKE2b-256 |
f00a640579d0f032ee7072efe839c82666914e8bbd4ad6490f63638a07fef56d
|
File details
Details for the file ivimfit-0.1.7.2-py3-none-any.whl.
File metadata
- Download URL: ivimfit-0.1.7.2-py3-none-any.whl
- Upload date:
- Size: 13.0 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.14.3
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
747397f1890a1ce144c60b580c74566efe6770ddf3eb23f8bebdc68d4e0eaa81
|
|
| MD5 |
0ae72ace7f833f97e5e6672eae080020
|
|
| BLAKE2b-256 |
7c2562d2f73d5ce8c4d4c9aac738251de9325c2577233d65dfc12b31ea3d9631
|