Skip to main content

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

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

ivimfit-0.1.7.1.tar.gz (12.9 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

ivimfit-0.1.7.1-py3-none-any.whl (13.0 kB view details)

Uploaded Python 3

File details

Details for the file ivimfit-0.1.7.1.tar.gz.

File metadata

  • Download URL: ivimfit-0.1.7.1.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

Hashes for ivimfit-0.1.7.1.tar.gz
Algorithm Hash digest
SHA256 80763c6c2dbe08de9babceb153dde41b9b5b800f84cd4c18dd18d89b7f996b90
MD5 f4c472acb960f4fb0735633bf44bce77
BLAKE2b-256 61b5abe405478ba630e4e4dd2027d0c35459ec7dd97095b3c67a96c3130f358f

See more details on using hashes here.

File details

Details for the file ivimfit-0.1.7.1-py3-none-any.whl.

File metadata

  • Download URL: ivimfit-0.1.7.1-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

Hashes for ivimfit-0.1.7.1-py3-none-any.whl
Algorithm Hash digest
SHA256 69f659dd57907eb3a37abb80bb5eba41a452944754f32c912c4a1a5a2ef7d71f
MD5 22bd89b3da6edce1d9c2679332444229
BLAKE2b-256 e4415148fa9626cd6278eb3d8227bbe1cccdb8204fd2a49c06b734d6ac22ae17

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page