feat: Add body-part aware initial guesses for IVIM fitting (Feature #87)#149
feat: Add body-part aware initial guesses for IVIM fitting (Feature #87)#149Devguru-codes wants to merge 1 commit intoOSIPI:mainfrom
Conversation
…SIPI#87) Add literature-sourced default initial guesses, bounds, and thresholds for 8 anatomical regions: brain, liver, kidney, prostate, pancreas, head_and_neck, breast, placenta. New files: - src/wrappers/ivim_body_part_defaults.py: lookup table module with get_body_part_defaults() and get_available_body_parts() - tests/IVIMmodels/unit_tests/test_body_part_defaults.py: 22 unit tests Modified files: - src/wrappers/OsipiBase.py: add body_part parameter to __init__(), support initial_guess as string (e.g. initial_guess='liver') API usage: OsipiBase(algorithm='X', bvalues=b, body_part='liver') OsipiBase(algorithm='X', bvalues=b, initial_guess='brain') 100% backward compatible: body_part=None uses original generic defaults. User-provided bounds/initial_guess always take priority. No regressions: 1127 passed, 167 skipped, 22 xfailed, 6 xpassed.
oliverchampion
left a comment
There was a problem hiding this comment.
Hey DevGuru, thanks for implementing this! Some general comments. As some of these implementation also requiere expert input and consensus (bounds, initial guesses) I have asked the community to comment too.
| "liver": { | ||
| "initial_guess": {"S0": 1.0, "f": 0.12, "Dp": 0.06, "D": 0.001}, | ||
| "bounds": { | ||
| "S0": [0.7, 1.3], |
There was a problem hiding this comment.
Generally, the S0 bounds are defined by the SNR (i.e. more noisy data will mean S0 varies more). I would be in favour to broaden these bounds (i.e. 0.5-2).
There was a problem hiding this comment.
Ok. I made the it tighter as a conservative starting point for all organs. I will update it and broaden it for better accomodation.
| "Dp": [0.005, 0.05], | ||
| "D": [0.0003, 0.002], | ||
| }, | ||
| "thresholds": [200], |
There was a problem hiding this comment.
In lieu of the IVIM review paper, a cut-off of 300 is adviced for brain
There was a problem hiding this comment.
all other organs have 200 adviced
There was a problem hiding this comment.
I made the threshold of 200 based on general practice like this paper Federau 2017, DOI: 10.1002/nbm.3780), which uses b-value cutoffs around 200 s/mm² in their segmented fittings.
| "initial_guess": {"S0": 1.0, "f": 0.12, "Dp": 0.06, "D": 0.001}, | ||
| "bounds": { | ||
| "S0": [0.7, 1.3], | ||
| "f": [0.0, 0.40], |
There was a problem hiding this comment.
For all organs --> I have asked the IVIM experts to review these bounds :). I personally think they are somewhat tight, but lets see what the verdict is. They should be simple to adapt once we have made up our minds on what they should be
There was a problem hiding this comment.
I really appreciate it. Expert's opinion will help here. I will wait to touch this part and update it after expert opinion.
There was a problem hiding this comment.
Also for expert's reference, all of the bounds that i chose is based of papers and their list is given in this PR.
| raise ValueError( | ||
| f"Unknown body part '{body_part}'. " | ||
| f"Available body parts: {available}" |
There was a problem hiding this comment.
It would be nice to also provide the user with a list of the available/implemented options here
There was a problem hiding this comment.
this could be achieved easily by using the function below
There was a problem hiding this comment.
I have implemented this in lines 134 to 136 of ivim_body_part_defaults.py file. it has this function get_available_body_parts(). This function lists all available options in the error message. It is like this -
available = sorted(IVIM_BODY_PART_DEFAULTS.keys())
raise ValueError(
f"Unknown body part '{body_part}'. "
f"Available body parts: {available}"
)So the user sees:
>`ValueError: Unknown body part 'elbow'. Available body parts: ['brain', 'breast', 'head_and_neck', 'kidney', 'liver', 'pancreas', 'placenta', 'prostate']`
|
|
||
| def __init__(self, bvalues=None, thresholds=None, bounds=None, initial_guess=None, algorithm=None, force_default_settings=True, **kwargs): | ||
| def __init__(self, bvalues=None, thresholds=None, bounds=None, initial_guess=None, algorithm=None, force_default_settings=True, body_part=None, **kwargs): | ||
| from src.wrappers.ivim_body_part_defaults import get_body_part_defaults |
There was a problem hiding this comment.
I think this import should be at the top of the file
There was a problem hiding this comment.
Ok I wlll do it.
|
|
||
| if self.thresholds is None: | ||
| self.thresholds = np.array([200]) | ||
| if body_part is not None: |
There was a problem hiding this comment.
Here, I'd like to see behaviour doing:
If body_part is not None: initialize with body_part-specific values, but then override any non-None values from user input. So if I use body_part = liver, threshold = 500, it takes the initial guess and bounds from the liver, but overrides the threshold by 500.
There was a problem hiding this comment.
Ok. This adds flexibility and user control as well. I update logic so that when body_part is provided it will load body part default values first(initial_guess, bounds, thresholds) and then any non-None user-provided values will selectively override the body-part defaults. I will update this.
|
I will make all updates at once when the expert's review will come. |
|
|
||
| References: | ||
| Brain: Federau 2017 (DOI: 10.1002/nbm.3780) | ||
| Liver: Dyvorne 2013 (DOI: 10.1016/j.ejrad.2013.03.003), |
| References: | ||
| Brain: Federau 2017 (DOI: 10.1002/nbm.3780) | ||
| Liver: Dyvorne 2013 (DOI: 10.1016/j.ejrad.2013.03.003), | ||
| Guiu 2012 (DOI: 10.1002/jmri.23762) |
| Brain: Federau 2017 (DOI: 10.1002/nbm.3780) | ||
| Liver: Dyvorne 2013 (DOI: 10.1016/j.ejrad.2013.03.003), | ||
| Guiu 2012 (DOI: 10.1002/jmri.23762) | ||
| Kidney: Li 2017 (DOI: 10.1002/jmri.25571), |
There was a problem hiding this comment.
Either DOI is incorrect (it does refer to a Li et al. paper from 2016), or the paper is an incorrect source)
| Liver: Dyvorne 2013 (DOI: 10.1016/j.ejrad.2013.03.003), | ||
| Guiu 2012 (DOI: 10.1002/jmri.23762) | ||
| Kidney: Li 2017 (DOI: 10.1002/jmri.25571), | ||
| Ljimani 2020 (DOI: 10.1007/s10334-019-00802-0) |
| Guiu 2012 (DOI: 10.1002/jmri.23762) | ||
| Kidney: Li 2017 (DOI: 10.1002/jmri.25571), | ||
| Ljimani 2020 (DOI: 10.1007/s10334-019-00802-0) | ||
| Prostate: Kuru 2014 (DOI: 10.1007/s00330-014-3165-y) |
| Ljimani 2020 (DOI: 10.1007/s10334-019-00802-0) | ||
| Prostate: Kuru 2014 (DOI: 10.1007/s00330-014-3165-y) | ||
| Pancreas: Barbieri 2020 (DOI: 10.1002/mrm.27910) | ||
| Head/Neck: Sumi 2012 (DOI: 10.1259/dmfr/15696758) |
| Prostate: Kuru 2014 (DOI: 10.1007/s00330-014-3165-y) | ||
| Pancreas: Barbieri 2020 (DOI: 10.1002/mrm.27910) | ||
| Head/Neck: Sumi 2012 (DOI: 10.1259/dmfr/15696758) | ||
| Breast: Lee 2018 (DOI: 10.1097/RCT.0000000000000661) |
There was a problem hiding this comment.
Refers to: Lei et al., 2018, Intravoxel Incoherent Motion Diffusion-Weighted Imaging Versus Dynamic Contrast-Enhanced Magnetic Resonance Imaging: Comparison of the Diagnostic Performance of Perfusion-Related Parameters in Breast. Is that the one you want to cite here?
| Pancreas: Barbieri 2020 (DOI: 10.1002/mrm.27910) | ||
| Head/Neck: Sumi 2012 (DOI: 10.1259/dmfr/15696758) | ||
| Breast: Lee 2018 (DOI: 10.1097/RCT.0000000000000661) | ||
| Placenta: Zhu 2023 (DOI: 10.1002/jmri.28858) |
|
|
||
| IVIM_BODY_PART_DEFAULTS = { | ||
| "brain": { | ||
| "initial_guess": {"S0": 1.0, "f": 0.05, "Dp": 0.01, "D": 0.0008}, |
There was a problem hiding this comment.
What are these initial guesses based on? Are they representing the expected value for each organ?
If so, I would propose to keep this in line with the work in progress IVIM review paper (“Towards Clinical Translation of Intravoxel Incoherent Motion MRI: Acquisition and Analysis Consensus Recommendations”, JMRI, Sigmund et al.) to prevent a proliferation of expected values, standard deviations and bounds.
I would propose to use these values for the following organs with the specified expected value per parameter
Organ | Review | D (10-3 mm2/s) | f (%) | D* (10-3 mm2/s)
Brain | Vieni 2020 [1] | 0.83 | 7.64 | 10.88
Kidney | Ljimani 2020 [2] | 1.89 | 18.88 | 40.53
Liver | Li 2017 [3] | 1.09 | 23.05 | 70.02
Muscle | Englund 2022 [4] | 1.47 | 10.34 | 30.88
Breast(benign) | Liang 2020 [5] | 1.43 | 7.00 | 52.33
Breast(malignant) | Liang 2020 [5] | 0.97 | 11.31 | 37.76
Pancreas(benign) | Zhu 2021 [6] | 1.41 | 20.03 | 25.39
Pancreas(malignant) | Zhu 2021 [6] | 1.40 | 12.39 | 22.16
References:
[1] Vieni, C., et al., Effect of intravoxel incoherent motion on diffusion parameters in normal brain. Neuroimage, 2020. 204: p. 116228.
[2] Ljimani, A., et al., Consensus-based technical recommendations for clinical translation of renal diffusion-weighted MRI. MAGMA, 2020. 33(1): p. 177-195.
[3] Li, Y.T., et al., Liver intravoxel incoherent motion (IVIM) magnetic resonance imaging: a comprehensive review of published data on normal values and applications for fibrosis and tumor evaluation. Quant Imaging Med Surg, 2017. 7(1): p. 59-78.
[4] Englund, E.K., et al., Intravoxel Incoherent Motion Magnetic Resonance Imaging in Skeletal Muscle: Review and Future Directions. J Magn Reson Imaging, 2022. 55(4): p. 988-1012.
[5] Liang, J., et al., Intravoxel Incoherent Motion Diffusion-Weighted Imaging for Quantitative Differentiation of Breast Tumors: A Meta-Analysis. Front Oncol, 2020. 10: p. 585486.
[6] Zhu, M., et al., Accuracy of quantitative diffusion-weighted imaging for differentiating benign and malignant pancreatic lesions: a systematic review and meta-analysis. European Radiology, 2021. 31(10): p. 7746-7759.
| IVIM_BODY_PART_DEFAULTS = { | ||
| "brain": { | ||
| "initial_guess": {"S0": 1.0, "f": 0.05, "Dp": 0.01, "D": 0.0008}, | ||
| "bounds": { |
There was a problem hiding this comment.
How the comment above works out for the bounds then I'm not completely sure. However, I suppose it's better to be safe and use 'wide' margins for the bounds. I would opt for bounds wider than they are implemented here. This might be an impactful decision (since it can be used as base for fitting pipelines down the line), does it require some discussion @oliverchampion ?.
If let's say we were to use 4 SDs from the mean value reported in the Sigmund et al. review paper, and clip the values to the original bounds (f: [0, 100], D to [0, 0.005] and D* to [0.005, 0.2]) we would end up with bounds like:
with f in %, D in mm^2/s and D* in mm^2/s
{
"brain": {
"D": [0.00055, 0.00111],
"Dp": [0.005, 0.03],
"f": [0.0, 20.28]
},
"kidney": {
"D": [0.00129, 0.00249],
"Dp": [0.005, 0.11905],
"f": [0.0, 43.80]
},
"liver": {
"D": [0.00041, 0.00177],
"Dp": [0.005, 0.19406],
"f": [0.0, 56.97]
},
"muscle": {
"D": [0.00035, 0.00259],
"Dp": [0.005, 0.18760],
"f": [0.0, 34.02]
},
"breast_benign": {
"D": [0.0, 0.00291],
"Dp": [0.005, 0.16633],
"f": [0.0, 23.88]
},
"breast_malignant": {
"D": [0.0, 0.00225],
"Dp": [0.005, 0.11424],
"f": [0.0, 29.35]
},
"pancreas_benign": {
"D": [0.0, 0.00429],
"Dp": [0.005, 0.08015],
"f": [0.0, 51.99]
},
"pancreas_malignant": {
"D": [0.0, 0.00332],
"Dp": [0.005, 0.07348],
"f": [0.0, 32.27]
}
}
There was a problem hiding this comment.
I agree that we should have wide bounds. We should be careful as we're often looking for lesions, which means we are looking for values that deviate from healthy tissue (which are the easiest values to find in literature). We cannot have bounds that removes/reduces this desired contrast.
I'd be in favor of being careful and go for physical/theoretical bounds. In the liver bounds that Daan has cooked up with the SD's for example (or many of the examples provided in the PR). Is there really no chance of having diffusivity values between 0.0018 and 0.005? That's currently the forbidden gap between D and D* in bounds that are generated that way. With bounds like these, we're not really allowing the algorithms to interpret the signal.
There was a problem hiding this comment.
Hi Ivan, just discussed this within TF6.3. We agree with your point of view. Summarizing, we'd propose to:
- Use wide bounds and be on the safe side
- Make a distinction in bounds between brain and body (what they will be needs to be determined still)
- initial guess can be organ-specific (based on the reported healthy tissue means in the IVIM review paper)
There was a problem hiding this comment.
Hello @DKuppens and @IvanARashid sir. Thank you for the review. Based on consensus, there is what i am thinking -
- Organ List & Initial Guesses: - I will update the lookup table to strictly mirror the expected healthy tissue means from the upcoming Sigmund et al. consensus paper. This includes adding Muscle, splitting Breast and Pancreas into benign/malignant, removing Prostate and Head/Neck, and updating all the DOIs to match the citations you provided.
- Bounds (Brain vs. Body): - I will discard the 4-SD calculated bounds in favor of wide, physical bounds to ensure algorithms can freely interpret the signal (especially for lesions).
I have 1 doubt regarding the bounds. @DKuppens sir mentioned making a distinction between brain and body ("what they will be needs to be determined still"). Since the 4-SD bounds calculated above (f: 20%, D: 0.001, etc.) are no longer the plan, I will wait for you to finalize those wide physical limits for Brain vs Body (or I propose a set of wide physical limits for you to review? like - D: [0, 0.005], f: [0, 1] for body.
After this, I will work on updates. Thank you.
Hello @etpeterson - I saw no updates were made on this issue so I worked on it. If there is need of improvement, comment it and i will update this pr.
Description
Closes #87. Adds organ-specific initial guesses, bounds, and thresholds for IVIM fitting, sourced from peer-reviewed literature. This enables faster convergence, reduced local minima, and more physiologically plausible results when scanning specific body parts.
Problem
The current generic defaults (
f=0.1, D=0.001, Dp=0.01) are reasonable for the brain but significantly off for other organs:Solution: Literature-Sourced Lookup Table
Each body part also includes organ-specific bounds (tighter than the generic ones) to constrain the optimizer to physiologically plausible regions.
Justification for chosen values:
Changes Made
src/wrappers/ivim_body_part_defaults.pyget_body_part_defaults(),get_available_body_parts()src/wrappers/OsipiBase.pybody_partparam to__init__(), supportinitial_guessas stringtests/.../test_body_part_defaults.pyAPI Usage
Testing
body_part="liver"produced near-perfect results:Known Issue (Pre-existing, Not Introduced by This PR)
When using
body_part=together withalgorithm="IAR_LU_biexp", theIAR_LU_biexpalgorithm's internalset_bounds()crashes withKeyError: 0because it expects bounds as list-of-lists (bounds[0],bounds[1]) but receives dict-format bounds. This is the same bug onmainthat is already documented in Issue #86 and fixed in PR #142 — it reproduces identically withbody_part=None(generic defaults). Once PR #142 is merged, this will work seamlessly.Backward Compatibility
body_part=None(default) → identical to current behaviorinitial_guess=dict→ identical to current behaviorbounds/initial_guessalways override body-part defaultsChecklist
tests/IVIMmodels/unit_tests/test_body_part_defaults.pynumpy,scipyalready required)