-
Notifications
You must be signed in to change notification settings - Fork 34
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
pyBabyAFQ #524
pyBabyAFQ #524
Conversation
07e4908
to
27eea49
Compare
examples/plot_baby_afq.py
Outdated
"c405e0dbd9a4091c77b3d1ad200229b4", "ec0aeccc6661d2ee5ed79259383cdcee", | ||
"2802cd227b550f6e85df0fec1d515c29", "385addb999dc6d76957d2a35c4ee74bb", | ||
"b79f01829bd95682faaf545c72b1d52c", "b79f01829bd95682faaf545c72b1d52c", | ||
"e49ba370edca96734d9376f551d413db", "f59e9e69e06325198f70047cd63c3bdc", | ||
"ae3bd2931f95adae0280a8f75cd3ca9b", "c409a0036b8c2dd4d03d11fbc6bfbdcd", | ||
"c2597a474ea5ec9e3126c35fd238f6b2", "67af59c934147c9f9ff6e0b76c4cc6eb", | ||
"72d0bbc0b6162e9291fdc450c103a1f0", "51f5041be63ad0ac10d1ac09e3bf1a8e", | ||
"6200f5cdc1402dce46dedd505468b147", "83cb5bf6b9b1eda63c8643871e84a6d4", | ||
"2a5d8d309b1256d6e48958e112201c2c", "ba24d0915fdff215a403480d0a7745c9", | ||
"1001e833d1344274d543ffd02a66af80", "03e20c5eebcd4d439c4ffb36d26a10d9", | ||
"6200f5cdc1402dce46dedd505468b147", "83cb5bf6b9b1eda63c8643871e84a6d4", | ||
"a6ae325cce2dc4bb21b52ee4c6ca844f", "a96a31df8f96ccf1c5f871a18e4a2d72", | ||
"65b7378ca85689a5f23b1b84a6ed78f0", "ce0d0ea696ef51c671aa118e11192e2d", | ||
"ce4918086ca1e829698576bcf95db62b", "96168d2eff74ec2acf9065f499732526", | ||
"6b20ba319d44472ec21d6ece937605bb", "26b1cf6ec8bd365dde42e3efe9beeac2", | ||
"0b3ccf06564d973bfcfff9a87e74f8b5", "84f3426033a2b225b0920b2622864375", | ||
"5351b3cb7efa9aa8e83e266342809ebe", "4e8a34aaba4e0f22a6149f38620dc39d", | ||
"682c08f66e8c2cf9e4e60f5ce308c76c", "9077affd4f3a8a1d6b44678cde4b3bf4", | ||
"5adf36f00669cc547d5eb978acf46266", "66a8002688ffdf3943722361da90ec6a", | ||
"efb5ae138df92019541861f9aa6a4d57", "757ec61078b2e9f9a073871b3216ff7a", | ||
"ff1e238c52a21f8cc5d44ac614d9627f", "cf16dd2767c6ab2d3fceb2890f6c3e41", | ||
"6016621e244b60b9c69fd44b055e4a03", "fd495a2c94b6b13bfb4cd63e293d3fc0", | ||
"bf81a23d80f55e5f1eb0c16717193105", | ||
"6f8bf8f70216788d14d9a49a3c664b16", | ||
"19df0297d6a2ac21da5e432645d63174", | ||
] | ||
|
||
pediatric_remote_fnames = [ | ||
"24880625", "24880628", "24880631", "24880634", "24880637", "24880640", | ||
"24880643", "24880646", "24880649", "24880652", "24880655", "24880661", | ||
"24880664", "24880667", "24880670", "24880673", | ||
"24880676", "24880679", | ||
"24880685", "24880688", | ||
"24880691", "24880694", "24880697", "24880700", | ||
"24880703", "24880706", "24880712", "24880715", "24880718", "24880721", | ||
"24880724", "24880727", "24880730", "24880733", | ||
"24880736", "24880748", | ||
"24880739", "24880742", | ||
"24880754", "24880757", "24880760", "24880763", | ||
"24880769", "24880772", "24880775", "24880778", | ||
"24880781", "24880787", "24880790", "24880793", "24880796", "24880802", | ||
"24880805", "24880808", | ||
"24880616", | ||
"24880613", | ||
"24986396" | ||
] | ||
|
||
fetch_pediatric_templates = _make_fetcher("fetch_pediatric_templates", | ||
op.join(afq_home, | ||
'pediatric_templates'), | ||
baseurl, pediatric_remote_fnames, | ||
pediatric_fnames, | ||
md5_list=pediatric_md5_hashes, | ||
doc="Download pediatric templates") | ||
|
||
|
||
# TODO coding: this duplicates read_xxx_template pattern | ||
# TODO coding: unit testing | ||
def read_pediatric_templates(resample_to=False): | ||
"""Load pediatric AFQ templates from file | ||
|
||
Returns | ||
------- | ||
dict with: keys: names of template ROIs and values: nibabel Nifti1Image | ||
objects from each of the ROI nifti files. | ||
""" | ||
files, folder = fetch_pediatric_templates() | ||
|
||
print('Loading pediatric templates...') | ||
tic = time.perf_counter() | ||
|
||
pediatric_templates = {} | ||
for f in files: | ||
img = nib.load(op.join(folder, f)) | ||
if resample_to: | ||
if isinstance(resample_to, str): | ||
resample_to = nib.load(resample_to) | ||
img = nib.Nifti1Image(reg.resample(img.get_fdata(), | ||
resample_to, | ||
img.affine, | ||
resample_to.affine), | ||
resample_to.affine) | ||
pediatric_templates[f.split('.')[0]] = img | ||
|
||
toc = time.perf_counter() | ||
print(f'pediatric templates loaded in {toc - tic:0.4f} seconds') | ||
|
||
# For the arcuate (AF/ARC), reuse the SLF ROIs | ||
pediatric_templates['ARC_roi1_L'] = pediatric_templates['SLF_roi1_L'] | ||
pediatric_templates['ARC_roi1_R'] = pediatric_templates['SLF_roi1_R'] | ||
pediatric_templates['ARC_roi2_L'] = pediatric_templates['SLFt_roi2_L'] | ||
pediatric_templates['ARC_roi2_R'] = pediatric_templates['SLFt_roi2_R'] | ||
pediatric_templates['ARC_roi3_L'] = pediatric_templates['SLFt_roi3_L'] | ||
pediatric_templates['ARC_roi3_R'] = pediatric_templates['SLFt_roi3_R'] | ||
|
||
# For the middle longitudinal fasciculus (MdLF) reuse ILF ROI | ||
pediatric_templates['MdLF_roi2_L'] = pediatric_templates['ILF_roi1_L'] | ||
pediatric_templates['MdLF_roi2_R'] = pediatric_templates['ILF_roi1_R'] | ||
|
||
return pediatric_templates |
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.
All of this stuff should move into the data
module.
examples/plot_baby_afq.py
Outdated
bids_path=dhcp_home, | ||
dmriprep='dHCP neonatal dMRI pipeline', | ||
# bids_filters={"suffix": "dwi"}, # default | ||
# custom_tractography_bids_filters=None, # 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.
We'd like to use a BIDS filter to find already-tracked data here.
Both for the pilot data (from KGS lab) and eventually also for dHCP data (where tracking will be done with qsiprep/mrtrix).
examples/plot_baby_afq.py
Outdated
def make_pediatric_bundle_dict(bundle_names=pediatric_bundle_names, | ||
seg_algo="afq", | ||
resample_to=False): | ||
# pediatric probability maps | ||
prob_map_order = ["ATR_L", "ATR_R", "CST_L", "CST_R", "CGC_L", "CGC_R", | ||
"HCC_L", "HCC_R", "FP", "FA", "IFO_L", "IFO_R", "ILF_L", | ||
"ILF_R", "SLF_L", "SLF_R", "UNC_L", "UNC_R", | ||
"ARC_L", "ARC_R", "MdLF_L", "MdLF_R"] | ||
|
||
prob_maps = pediatric_templates['UNCNeo_JHU_tracts_prob-for-babyAFQ'] | ||
prob_map_data = prob_maps.get_fdata() | ||
|
||
# pediatric bundle dict | ||
pediatric_bundles = {} | ||
|
||
# each bundles gets a digit identifier (to be stored in the tractogram) | ||
uid = 1 | ||
|
||
for name in pediatric_bundle_names: | ||
# ROIs that cross the mid-line | ||
if name in ["FA", "FP"]: | ||
pediatric_bundles[name] = { | ||
'ROIs': [pediatric_templates[name + "_L"], | ||
pediatric_templates[name + "_R"], | ||
pediatric_templates["mid-saggital"]], | ||
'rules': [True, True, True], | ||
'cross_midline': True, | ||
'prob_map': prob_map_data[..., | ||
prob_map_order.index(name)], | ||
'uid': uid} | ||
uid += 1 | ||
# SLF is a special case, because it has an exclusion ROI: | ||
elif name == "SLF": | ||
for hemi in ['_R', '_L']: | ||
pediatric_bundles[name + hemi] = { | ||
'ROIs': [pediatric_templates[name + '_roi1' + hemi], | ||
pediatric_templates[name + '_roi2' + hemi], | ||
pediatric_templates["SLFt_roi2" + hemi]], | ||
'rules': [True, True, False], | ||
'cross_midline': False, | ||
'prob_map': prob_map_data[..., | ||
prob_map_order.index(name + hemi)], | ||
'uid': uid} | ||
uid += 1 | ||
# Third ROI for curvy tracts | ||
elif name in ["ARC", "ATR", "CGC", "IFO", "UNC"]: | ||
for hemi in ['_R', '_L']: | ||
pediatric_bundles[name + hemi] = { | ||
'ROIs': [pediatric_templates[name + '_roi1' + hemi], | ||
pediatric_templates[name + '_roi2' + hemi], | ||
pediatric_templates[name + '_roi3' + hemi]], | ||
'rules': [True, True, True], | ||
'cross_midline': False, | ||
'prob_map': prob_map_data[..., | ||
prob_map_order.index(name + hemi)], | ||
'uid': uid} | ||
uid += 1 | ||
elif name == "MdLF": | ||
for hemi in ['_R', '_L']: | ||
pediatric_bundles[name + hemi] = { | ||
'ROIs': [pediatric_templates[name + '_roi1' + hemi], | ||
pediatric_templates[name + '_roi2' + hemi]], | ||
'rules': [True, True], | ||
'cross_midline': False, | ||
# reuse probability map from ILF | ||
'prob_map': prob_map_data[..., | ||
prob_map_order.index("ILF" + hemi)], | ||
'uid': uid} | ||
uid += 1 | ||
# Default: two ROIs within hemisphere | ||
else: | ||
for hemi in ['_R', '_L']: | ||
pediatric_bundles[name + hemi] = { | ||
'ROIs': [pediatric_templates[name + '_roi1' + hemi], | ||
pediatric_templates[name + '_roi2' + hemi]], | ||
'rules': [True, True], | ||
'cross_midline': False, | ||
'prob_map': prob_map_data[..., | ||
prob_map_order.index(name + hemi)], | ||
'uid': uid} | ||
uid += 1 | ||
|
||
return pediatric_bundles |
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.
This probably also moves into data
or possibly into baby.data
One possible API here is that we create a new module The question is what do we do about things that are implemented in |
examples/plot_baby_afq.py
Outdated
max_bval=1000, # override | ||
reg_template=pediatric_templates['UNCNeo-withCerebellum-for-babyAFQ'], | ||
# reg_subject="power_map", # default | ||
reg_subject="b0", # override |
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.
Both of these (reg_template
and reg_subject
) should be skull-stripped using a brain-mask (for pilot data, we have that, for dHCP data it also comes with the anatomical pipeline derivatives). We should over-ride the pyAFQ default brain-mask (which is generated using the median_otsu
algorithm) with the brain-mask that is provided with the respective datasets.
examples/plot_baby_afq.py
Outdated
reg_subject="b0", # override | ||
# brain_mask=B0Mask(), # default | ||
brain_mask=MaskFile("brainmask", | ||
{"scope": "dHCP neonatal dMRI pipeline"}), |
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.
Yep - this is it!
Check if this is a binary mask or a continuous probability mask, and then threshold if needed (there is a class for that!).
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.
Just so I understand what is going on: Which registration algorithm are we using? Are we applying a brain mask in the step above or are we just checking that it is not a probability mask?
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.
reg_algo=None
by default, which should use Symmetric Diffeomorphic Registration (SyN) Algorithm for registration.
In this step we are configuring pyAFQ and nothing is actually run until the myafq.export_all()
command. According to the docs the brain_mask
is applied before registration to a template.
examples/plot_baby_afq.py
Outdated
# min_bval=None, # default | ||
min_bval=1000, # override | ||
# max_bval=None, # default | ||
max_bval=1000, # override |
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.
We might consider adding all the shells here. Proceed with caution.
Eventually, we might have some more denoising coming (through #625), so we could do DKI with multi-shell, and add some more scalars to the profiles (AWF, MK).
Let's punt on this one for now. We'll use the tractography that is already there for the pilot data and do qsiprep/mrtrix with the dHCP data. |
Might not need this for this application. The MATLAB babyAFQ does not use endpoints for filtering. |
Furthermore, the template does have a version of the AAL atlas, so if need be, we can come back to that (with the caveat that we'd need to do some massaging of the template to add some volumes that we have in the template we use with grownups). We'll also need the ALS atlas for the definition of pAF (which, in this PR, is currently being skipped). In this case, a particular part of the AAL atlas is extracted to serve as a waypoint ROI - this is already part of the templates. |
Hello @bloomdt-uw! Thanks for updating this PR. We checked the lines you've touched for PEP 8 issues, and found:
Comment last updated at 2021-08-03 22:23:15 UTC |
341e259
to
69cf219
Compare
69cf219
to
18ac9dd
Compare
18ac9dd
to
638de57
Compare
638de57
to
83a3173
Compare
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.
I think that this is really close to being ready to merge! Just a few small comments here.
@@ -945,6 +1070,9 @@ def _get_best_scalar(self): | |||
return self.scalars[0] | |||
|
|||
def get_reg_template(self): | |||
if isinstance(self.reg_template, nib.Nifti1Image): |
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.
@36000 : what do you think? Does this change make sense? Or should this go somewhere else?
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.
@36000 : do you have any thoughts here? I would like to merge this PR, but need some input from you for this particular line.
examples/plot_baby_afq.py
Outdated
|
||
The following is an example of tractometry for pediatric bundles. | ||
|
||
TODO cite @mareike |
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.
@@ -209,6 +209,7 @@ | |||
'examples_dirs': '../../examples', | |||
# path where to save gallery generated examples | |||
'gallery_dirs': 'auto_examples', | |||
'ignore_pattern': 'plot_baby_afq.py', |
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.
I wonder why this is not in the exclude_patterns
on line 82 of this file (and what the difference is between these), but maybe that's not a specific issue with this PR
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.
I don't recall exactly why I made this change, it was part of the original commit, so it could be OBE
examples/plot_baby_afq.py
Outdated
# ----------------- | ||
# **Diffusion dataset** | ||
# | ||
# .. note:: |
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.
We don't actually use any dataset here, so I would say that this is all somewhat theoretical, and link to the repo that contains the code for the dHCP data analysis, to say that's what we actually do.
I think this is good to go! @36000 : would you mind taking a quick look to confirm? |
OK - I believe that the change alluded to here is not harmful, so I am going to go ahead and merge this, so that we can avoid conflicts down the line. |
#487
Based on Mareike's recommendation, pausing work on dHCP dataset and generally abandoning approach to use FA. Instead will focus on replicating MATLAB AFQ results with Kalanit longitudinal data.
Next Steps:
Kalanit longitudinal data
Processed using MATLAB BabyAFQ
Goal is to reproduce results in:
Gold standard
MATLAB AFQ
use existing tractography data and feed into pyAFQ segmentation
mif
files tonii
if successful, then consider tractography using ACT within pyAFQ
dHCP data
Single Subject data
Revisit BIDS conversion
using default dHCP dataset_description.json generates BIDS validation error.
dHCP uses BIDS 1.0.2 (dHCP Data Release Notes)
converter from BIDS 1.0.2 to BIDS 1.4.0
add BIDSVersion to original file
document and continue to use workaround: replace
"PipelineDescription.Name"
with `"PipelineDescription": { "Name"Error processing
sub-CC#####XX##_ses-#####_desc-preproc_dwi.json
file when present. Currently just excluded filewhen making BIDS compliant pyAFQ required data in AFQ_data
derivatives
folderpyAFQ does not read
nii
files must benii.gz
, see Make s3fs_nifti_read compatible with non-gzipped files #151, therefore needed to gzip nii files.Convert script to Jupyter notebook with CloudKnot (see [ENH] Add cloudknot example #533)
if pyAFQ tractography doesn't work, consider two-step pipeline: use qsiprep/mrtrix for tractography then pyAFQ segmentation
add segmentation end point filtering
dependency: custom ROI monkey patch can be removed once Make bundle input more flexible #519 merged
debugging FA tractography
However if decide to revisit dHCP FA:
Parameters explored thus far (all without endpoint filtering):
default
b0 1000
b0 1000 with FA seed threshold 0.1
b0 1000 with ROIMask
b0 1000 with BrainMask and ROIMask
b0 1000 with BrainMask, ROIMask, and FA stop threshold 0.1
In all cases some bundles were entirely missing and tractography was highly irregular.