-
Notifications
You must be signed in to change notification settings - Fork 0
Single subject anatomical workflow #35
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
14 commits
Select commit
Hold shift + click to select a range
266bdaa
Update niwrap, add niwrap-helper dependencies
kaitj 0c0d575
Refactor to use a 'core' submodule
kaitj 128d8f4
Add single subject anat workflow
kaitj 20b7520
Apply suggestions to utils.py
kaitj 3aa6171
Update use of rename in anatomical.py
kaitj 1a3d7c5
Add note about reorientation step
kaitj 9d69739
Add note about image origin
kaitj 35d2e0e
Add init to core with constant seed
kaitj 9a83237
Add template files directly
kaitj 82f43ad
Global apply unit fixture; add test for copy
kaitj bb52a1b
Fix missing bugs in gh workflow
kaitj ed9425f
Add tissue segmentation
kaitj c1218b6
Update dependencies
kaitj dc61903
Fixes for deptry
kaitj File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file was deleted.
Oops, something went wrong.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,3 @@ | ||
| """Core RBC methods for varying workflows.""" | ||
|
|
||
| CPAC_ANTS_SEED = 77742777 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,14 @@ | ||
| """Anatomical processing. | ||
|
|
||
| This module defines anatomical MRI processing methods. | ||
|
|
||
| Anatomical processing prepares structural brain images (e.g. T1-weighted) for | ||
| analysis. The T1w provides high-resolution anatomy that can be used to align | ||
| other modalities (e.g. functional) and identify different tissue types | ||
| (gray matter, white matter, CSF). | ||
| """ | ||
|
|
||
| from .registration import ants_registration | ||
| from .segmentation import ants_brain_extraction, fsl_tissue_segmentation | ||
|
|
||
| __all__ = ["ants_brain_extraction", "ants_registration", "fsl_tissue_segmentation"] |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,147 @@ | ||
| """RBC registration method.""" | ||
|
|
||
| from pathlib import Path | ||
| from types import SimpleNamespace | ||
|
|
||
| from niwrap import ants | ||
|
|
||
| from rbc.core import CPAC_ANTS_SEED | ||
| from rbc.core.resources import MNI_TEMPLATES | ||
|
|
||
|
|
||
| def ants_registration( | ||
| in_file: Path, output_prefix: str, seed: int = CPAC_ANTS_SEED | ||
| ) -> SimpleNamespace: | ||
| """ANTs registration to MNI152 template. | ||
|
|
||
| Args: | ||
| in_file: Path to file to be compute transformation with template. | ||
| output_prefix: Prefix of output file. | ||
| seed: Seed to use for reproducibility. | ||
|
|
||
| Returns: | ||
| A namespace mapping forward and inverse transformation paths. | ||
| """ | ||
| registration = ants.ants_registration( | ||
| stages=[ | ||
| ants.ants_registration_stage( | ||
| transform=ants.ants_registration_transform_rigid(gradient_step=0.05), | ||
| metric=ants.ants_registration_metric_mutual_information( | ||
| fixed_image=MNI_TEMPLATES.brain_1mm, | ||
| moving_image=in_file, | ||
| metric_weight=1, | ||
| number_of_bins=ants.ants_registration_number_of_bins( | ||
| number_of_bins_value=32, | ||
| sampling_strategy=ants.ants_registration_sampling_strategy_1( | ||
| sampling_strategy_value="Regular", | ||
| sampling_percentage=ants.ants_registration_sampling_percentage_1( | ||
| sampling_percentage_value=0.25 | ||
| ), | ||
| ), | ||
| ), | ||
| ), | ||
| convergence=ants.ants_registration_convergence( | ||
| convergence="100x100", | ||
| convergence_threshold=0.000001, | ||
| convergence_window_size=20, | ||
| ), | ||
| smoothing_sigmas="2.0x1.0vox", | ||
| shrink_factors="2x1", | ||
| use_histogram_matching=True, | ||
| ), | ||
| ants.ants_registration_stage( | ||
| transform=ants.ants_registration_transform_affine(gradient_step=0.08), | ||
| metric=ants.ants_registration_metric_mutual_information( | ||
| fixed_image=MNI_TEMPLATES.brain_1mm, | ||
| moving_image=in_file, | ||
| metric_weight=1, | ||
| number_of_bins=ants.ants_registration_number_of_bins( | ||
| number_of_bins_value=32, | ||
| sampling_strategy=ants.ants_registration_sampling_strategy_1( | ||
| sampling_strategy_value="Regular", | ||
| sampling_percentage=ants.ants_registration_sampling_percentage_1( | ||
| sampling_percentage_value=0.25 | ||
| ), | ||
| ), | ||
| ), | ||
| ), | ||
| convergence=ants.ants_registration_convergence( | ||
| convergence="100x100", | ||
| convergence_threshold=0.000001, | ||
| convergence_window_size=20, | ||
| ), | ||
| smoothing_sigmas="1.0x0.0vox", | ||
| shrink_factors="2x1", | ||
| use_histogram_matching=True, | ||
| ), | ||
| ants.ants_registration_stage( | ||
| transform=ants.ants_registration_transform_syn( | ||
| gradient_step=0.1, | ||
| update_field_variance_in_voxel_space=ants.ants_registration_update_field_variance_in_voxel_space( | ||
| update_field_variance_in_voxel_space_value=3, | ||
| total_field_variance_in_voxel_space=ants.ants_registration_total_field_variance_in_voxel_space( | ||
| total_field_variance_in_voxel_space_value=0 | ||
| ), | ||
| ), | ||
| ), | ||
| metric=ants.ants_registration_metric_ants_neighbourhood_cross_correlation( | ||
| fixed_image=MNI_TEMPLATES.brain_1mm, | ||
| moving_image=in_file, | ||
| metric_weight=1, | ||
| radius=ants.ants_registration_radius(radius_value=4), | ||
| ), | ||
| convergence=ants.ants_registration_convergence( | ||
| convergence="100x70x50x20", | ||
| convergence_threshold=0.000001, | ||
| convergence_window_size=10, | ||
| ), | ||
| smoothing_sigmas="3.0x2.0x1.0x0.0vox", | ||
| shrink_factors="8x4x2x1", | ||
| use_histogram_matching=True, | ||
| ), | ||
| ], | ||
| random_seed=seed, | ||
| collapse_output_transforms=True, | ||
| dimensionality=3, | ||
| initial_moving_transform=ants.ants_registration_initial_moving_transform_initialization_feature( | ||
| fixed_image=MNI_TEMPLATES.brain_1mm, | ||
| moving_image=in_file, | ||
| initialization_feature=0, | ||
| ), | ||
| winsorize_image_intensities=ants.ants_registration_winsorize_image_intensities( | ||
| lower_quantile=0.005, upper_quantile=0.995 | ||
| ), | ||
| interpolation="LanczosWindowedSinc", | ||
| output=f"[{output_prefix}_,{output_prefix}_Warped.nii.gz]", | ||
| ) | ||
| fwd = ants.ants_apply_transforms( | ||
| reference_image=MNI_TEMPLATES.brain_1mm, | ||
| transform=[ | ||
| ants.ants_apply_transforms_transform_file_name( | ||
| registration.root / f"{output_prefix}_0GenericAffine.mat" | ||
| ), | ||
| ants.ants_apply_transforms_transform_file_name( | ||
| registration.root / f"{output_prefix}_1Warp.nii.gz" | ||
| ), | ||
| ], | ||
| output=ants.ants_apply_transforms_composite_displacement_field_output( | ||
| composite_displacement_field=f"{output_prefix}_from-T1w_to-template_mode-image_xfm.nii.gz", | ||
| print_out_composite_warp_file=True, | ||
| ), | ||
| ) | ||
| rev = ants.ants_apply_transforms( | ||
| reference_image=in_file, | ||
| transform=[ | ||
| ants.ants_apply_transforms_transform_file_name( | ||
| registration.root / f"{output_prefix}_1InverseWarp.nii.gz" | ||
| ), | ||
| ants.ants_apply_transforms_use_inverse( | ||
| registration.root / f"{output_prefix}_0GenericAffine.mat" | ||
| ), | ||
| ], | ||
| output=ants.ants_apply_transforms_composite_displacement_field_output( | ||
| composite_displacement_field=f"{output_prefix}_from-template_to-T1w_mode-image_xfm.nii.gz", | ||
| print_out_composite_warp_file=True, | ||
| ), | ||
| ) | ||
| return SimpleNamespace(forward=fwd.output, inverse=rev.output) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,63 @@ | ||
| """RBC skull stripping method.""" | ||
|
|
||
| from pathlib import Path | ||
| from types import SimpleNamespace | ||
|
|
||
| from niwrap import ants, fsl | ||
|
|
||
| from rbc.core.resources import OASIS_TEMPLATES | ||
|
|
||
|
|
||
| def ants_brain_extraction( | ||
| in_file: Path, output_prefix: str | ||
| ) -> ants.AntsBrainExtractionShOutputs: | ||
| """ANTs N4 bias correction and brain extraction. | ||
|
|
||
| Args: | ||
| in_file: Input anatomical file to perform brain extraction on. | ||
| output_prefix: Prefix for output file names | ||
|
|
||
| Returns: | ||
| ANTs brain extraction output object. | ||
| """ | ||
| return ants.ants_brain_extraction_sh( | ||
| image_dimension=3, | ||
| anatomical_image=in_file, | ||
| template=OASIS_TEMPLATES.template, | ||
| probability_mask=OASIS_TEMPLATES.probability_mask, | ||
| brain_extraction_registration_mask=OASIS_TEMPLATES.registration_mask, | ||
| output_prefix=output_prefix, | ||
| image_file_suffix="nii.gz", | ||
| random_seeding=False, | ||
| ) | ||
|
|
||
|
|
||
| def fsl_tissue_segmentation(in_file: Path, output_prefix: str) -> SimpleNamespace: | ||
| """FSL Fast tissue classification. | ||
|
|
||
| Args: | ||
| in_file: Input anatomical file to perform tissue classification on. | ||
| output_prefix: Prefix for output file names | ||
|
|
||
| Returns: | ||
| Namespace with paths to each tissue mask. | ||
| """ | ||
| tissues = fsl.fast( | ||
| in_files=[in_file], | ||
| img_type=1, | ||
| number_classes=3, | ||
| segments=True, | ||
| out_basename=output_prefix, | ||
| ) | ||
| masks = { | ||
| tissue_type: fsl.fslmaths( | ||
| input_files=[tissues.root / f"{output_prefix}_pve_{idx}.nii.gz"], | ||
| operations=[ | ||
| fsl.fslmaths_operation(thr=0.95), | ||
| fsl.fslmaths_operation(bin_=True), | ||
| ], | ||
| output=f"{tissue_type}_mask.nii.gz", | ||
| ).output_file | ||
| for idx, tissue_type in enumerate(["csf", "gm", "wm"]) | ||
| } | ||
| return SimpleNamespace(**masks) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,28 @@ | ||
| """General functions useful across modalities.""" | ||
|
|
||
| from pathlib import Path | ||
|
|
||
| from niwrap import afni | ||
|
|
||
| from rbc.core.utils import create_copy | ||
|
|
||
|
|
||
| def reorient(in_file: Path, output_fname: str) -> afni.V3dresampleOutputs: | ||
| """AFNI deobliquing and reorientation to RPI. | ||
|
|
||
| Sets image into a cardinal orientation if it was acquired obliquely from scanner | ||
| and standardize orientation of images ('RPI' is internal assumption from AFNI). | ||
|
|
||
| Args: | ||
| in_file: Input T1w to reorient | ||
| output_fname: Output filename | ||
|
|
||
| Returns: | ||
| An object representing the outputs from AFNI's 3D resample. | ||
| """ | ||
| with create_copy(in_file) as tmp_file: | ||
| afni.v_3drefit(in_file=tmp_file, deoblique=True) | ||
| reorient = afni.v_3dresample( | ||
| in_file=tmp_file, prefix=output_fname, orientation="RPI" | ||
| ) | ||
| return reorient |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,8 @@ | ||
| """Functional processing. | ||
|
|
||
| This module defines functional MRI processing methods. | ||
|
|
||
| Functional MRI measures brain activity over time via the BOLD signal. Raw | ||
| fMRI contains motion artifacts, timing differences, and distortions that | ||
| must be corrected before analysis. | ||
| """ |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,8 @@ | ||
| """Longitudinal processing. | ||
|
|
||
| This module handles longitudinal processing for multi-session data. | ||
|
|
||
| Processing steps include the creation of unbiased within-subject anatomical template | ||
| and processes each session relative to it, improving sensitivity for detecting changes | ||
| over time. | ||
| """ |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,5 @@ | ||
| """Quality control. | ||
|
|
||
| This module defines methods for computing quality control metrics, including metrics | ||
| like framewise displacement (FD), DVARS, motion-DVARS correlation, and tSNR. | ||
| """ |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,21 @@ | ||
| """Module containing paths to resources.""" | ||
|
|
||
| from pathlib import Path | ||
| from types import SimpleNamespace | ||
|
|
||
| RESOURCES_DIR = Path(__file__).parent.resolve() | ||
|
|
||
| # OASIS | ||
| # (sourced from C-PAC container ghcr.io/fcp-indi/c-pac:many_pipes) | ||
| OASIS_DIR = RESOURCES_DIR / "oasis" | ||
| OASIS_TEMPLATES = SimpleNamespace( | ||
| template=OASIS_DIR / "T_template0.nii.gz", | ||
| probability_mask=OASIS_DIR / "T_template0_BrainCerebellumProbabilityMask.nii.gz", | ||
| registration_mask=OASIS_DIR / "T_template0_BrainCerebellumRegistrationMask.nii.gz", | ||
| ) | ||
| # MNI152 | ||
| # (sourced from FSL6.0 in C-PAC container: ghcr.io/fcp-indi/c-pac:many_pipes | ||
| MNI_DIR = RESOURCES_DIR / "mni" | ||
| MNI_TEMPLATES = SimpleNamespace(brain_1mm=MNI_DIR / "MNI152_T1_1mm_brain.nii.gz") | ||
|
|
||
| __all__ = ["MNI_TEMPLATES", "OASIS_TEMPLATES"] |
Binary file not shown.
Binary file not shown.
Binary file added
BIN
+2.21 MB
src/rbc/core/resources/oasis/T_template0_BrainCerebellumProbabilityMask.nii.gz
Binary file not shown.
Binary file added
BIN
+145 KB
src/rbc/core/resources/oasis/T_template0_BrainCerebellumRegistrationMask.nii.gz
Binary file not shown.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
pretty sure this is default
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You would think so, but without it the windows runs fail.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ah because of the linebreaks with \
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
switching to no linebreaks is probably better than emulating bash on windows
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
or alternatively run just pytest without coverage there - dont think we need multi platform cov
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yeah i think i'll probably address this workflow in a separate PR. At least for now this works and gives us some tests, but definitely want to change it up.