Adding SF amplitude thresholding as a way of create masks#1337
Conversation
…t of StatefulImage)
…branch_b_independent
There was a problem hiding this comment.
Pull request overview
This PR introduces global spherical-function (SF) amplitude thresholding to generate white-matter–like masks from fODF/ODF inputs, and wires that capability into multiple tracking CLIs as optional “on-the-fly” masking.
Changes:
- Added
compute_max_sf_amplitude/compute_sf_threshold_maskutilities to derive per-voxel max SF amplitude and a global-threshold-based mask (with optional postprocessing). - Added new CLI
scil_fodf_global_sf_thresholdand registered it as a console script. - Added optional global SF thresholding arguments to tracking scripts/utilities and applied the resulting mask to tracking constraints.
Reviewed changes
Copilot reviewed 7 out of 7 changed files in this pull request and generated 11 comments.
Show a summary per file
| File | Description |
|---|---|
src/scilpy/tracking/utils.py |
Adds new tracking CLI options for global SF thresholding and adjusts get_direction_getter API to accept already-loaded data. |
src/scilpy/reconst/utils.py |
Adds core implementation for computing max SF amplitude and deriving a global-threshold SF mask. |
src/scilpy/cli/scil_tracking_pft.py |
Adds global SF thresholding options and applies resulting mask to include/exclude maps; adds sphere argument support. |
src/scilpy/cli/scil_tracking_local.py |
Applies optional global SF threshold mask during local tracking (CPU/GPU). |
src/scilpy/cli/scil_tracking_local_dev.py |
Applies optional global SF threshold mask when an ODF is provided; enforces argument validity. |
src/scilpy/cli/scil_fodf_global_sf_threshold.py |
New standalone CLI to compute and save a global SF threshold mask from SH or peaks input. |
pyproject.toml |
Registers the new CLI entrypoint. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## master #1337 +/- ##
==========================================
+ Coverage 72.50% 72.59% +0.09%
==========================================
Files 300 301 +1
Lines 26139 26276 +137
Branches 3671 3699 +28
==========================================
+ Hits 18952 19076 +124
- Misses 5640 5648 +8
- Partials 1547 1552 +5
Flags with carried forward coverage won't be shown. Click here to find out more.
🚀 New features to boost your workflow:
|
arnaudbore
left a comment
There was a problem hiding this comment.
LGTM: Add a test for one of the tractogram to use each param to get a better cover.
CHrlS98
left a comment
There was a problem hiding this comment.
Imports are in the middle of the code, is there a reason for this?
| thr_g = p.add_mutually_exclusive_group(required=True) | ||
| thr_g.add_argument('--relative', type=float, | ||
| help='Global SF threshold relative factor (0-1).') | ||
| thr_g.add_argument('--absolute', type=float, | ||
| help='Global SF absolute threshold.') |
There was a problem hiding this comment.
does it need to be mutually exclusive? Could be both, no?
| sf_mask = None | ||
| if args.global_sf_rel_thr is not None or \ | ||
| args.global_sf_abs_thr is not None: | ||
| from scilpy.tracking.utils import get_global_sf_threshold_mask |
There was a problem hiding this comment.
any reason why this is a conditional import?
|
|
||
| if args.global_sf_rel_thr is not None or \ | ||
| args.global_sf_abs_thr is not None: | ||
| from scilpy.tracking.utils import get_global_sf_threshold_mask |
There was a problem hiding this comment.
again, why is this import inside the main?
| else: | ||
| raise ValueError("Peaks input must be 4D or 5D.") | ||
|
|
||
| norms = np.linalg.norm(peaks, axis=-1) |
There was a problem hiding this comment.
I would consider adding a warning if all peaks have the same norm, meaning they'd be normalized.
| mask = max_amp >= threshold | ||
|
|
||
| if postprocess_mask and np.any(mask): | ||
| import scipy.ndimage as ndi |
| for label, count in enumerate(label_counts_inverted): | ||
| if label == 0: | ||
| continue # Skip background | ||
| if count < 100: # Threshold for filling holes |
There was a problem hiding this comment.
this feels a bit ad hoc no?
| ' "1": {"propagator": "ODF", ' | ||
| '"filename": str,\n' | ||
| ' "sh_basis": str, ' | ||
| '"algo": str,\n' | ||
| ' "theta": float, ' | ||
| '"step_size": float},\n' | ||
| ' "2": {"propagator": "ODF", ' | ||
| '"filename": str,\n' | ||
| ' "sh_basis": str, ' | ||
| '"algo": str,\n' | ||
| ' "theta": float, ' | ||
| '"step_size": float}\n' |
There was a problem hiding this comment.
isn't this formatting very ugly?
| return max_sf | ||
|
|
||
|
|
||
| def compute_sf_threshold_mask(data, sphere_name='repulsion100', |
There was a problem hiding this comment.
given the postprocessing step this feels very tracking-oriented, not sure it's reconst.
CHrlS98
left a comment
There was a problem hiding this comment.
Alright thanks, I have one last request, but I'll approve right away bcause it's not breaking.
| label_counts_inverted = np.bincount(labels_inverted.ravel()) | ||
|
|
||
| # Fill holes smaller than 5% of the largest component size | ||
| hole_threshold = 0.05 * largest_component_size |
There was a problem hiding this comment.
My last request would be that this 0.05 is a parameter to the method, even if it is not exposed in the script.
Quick description
New scripts that use FOD as input to create an improve WM mask based on either global or relative lobe amplitude to determine if the signal in a voxel is sufficient to be included in the mask.
This is also added to all 3 tracking algorithms to make it easier to use ''on-the-fly'', they are optional parameters (that I believe I will always use). It improves upon or even replace the need for an FA mask in some case.
There is still work to do (move duplicated code to a function for tracking), but I would like it if someone could test it on a random FOD file they have and see if the help/description/results are clear as it is a new behavior...
Type of change
Check the relevant options.
Provide data, screenshots, command line to test (if relevant)
...
Checklist