Skip to content

Skip brain extraction #116

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

Closed
wants to merge 44 commits into from
Closed

Conversation

adelavega
Copy link
Contributor

Fixes #12 by creating an alternative workflow to init_brain_extraction_wf, init_n4_only_wf.
This wf only does n4 bias field correction, and creates a mask file by thresholding the input T1w image.

My main concern is if this is the right approach.
Alternatively, the init_brain_extraction_wf workflow could be altered in niwokflows, but as this is supposed to be an implementation of ANTS' brain extraction, I thought it was better not to touch that.

I also though about just changing the init_anat_preproc_wf function to change the connections based on if brain extraction would be run, but that got messy quick.

Also, I'm currently just using a parameter to indicate if brain extraction should be skipped. Although the function to detect if brain extraction is needed is easy, I'm not sure how to dynamically create a workflow based on the output of a Node.

Any pointers here?

Copy link
Member

@oesteban oesteban left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll let you keep working on this, but it looks it's taking a good direction.

My main concern is: if the image is skull-stripped, then it is likely to have been INU-corrected too. That makes me think about three issues:

  1. How would you account for INU-corrected images? I mean, it doesn't hurt to run INU-correction twice (in principle) and let N4 find nothing if the image is intensity corrected.
  2. The current antsBrainExtraction workflow is modified to recalculate the B1 field using the Atropos segmentation and passing in the white-matter class. Could we also implement something like that here?
  3. If down the line we decide to implement some B1 field correction in fMRIPrep (and we will certainly do in dMRIPrep), could we break the workflow at construction if some flag (i.e., disallow_premasked) is present set true? This way, dMRIPrep could set it on and prevent this behavior to happen while making noise enough to get the attention of the user.

@adelavega
Copy link
Contributor Author

Thanks for taking a look.

  1. Yes that's what I was thnking and what @effigies had suggested. It shouldn't in principle hurt to run that twice, whereas running brain extraction twice does hurt
  2. Sure, I just wasn't sure if that was necessary, but that'd be easy enough. In that case though, would you rather keep this workflow here, or modify the ants one to just skip brain extraction given a flag? Again, I can understand how keeping that one strictly like the ants workflow makes sense so I have no strong opinion
  3. Makes sense

Any suggestions re: the brain-mask detection though? Or are you happy to leave it as an explicit CLI option?

@adelavega adelavega force-pushed the enh/skull_strip_skip branch from cef24eb to 0b90071 Compare October 9, 2019 18:33
@adelavega
Copy link
Contributor Author

adelavega commented Oct 11, 2019

I successfully ran ds001110/SherlockMerlin with brain extraction turned off, and the results look decent as far as I can tell.

This is ready for another review whenever you get a chance. No rush from me as preprocessing that dataset was my main goal.

Here are the reports for the dataset if anyone cares to take a look: https://web.corral.tacc.utexas.edu/neuroscout-datasets/SherlockMerlin/fmriprep/

Main points left to discuss:

  1. Should this behavior be handled through a flag or detection? Either way I think that should go into fmriprep and this is fine as is. If we do detection, I guess we need to do it outside of nipype using pybids + the function I posted before. (Unless anyone knows how to do a conditional graph)
  1. Should this new workflow go here or in niworkflows? Up to you. I'm happy to open another PR there with just the init workflow function.
  2. The disallow_premasked flag only makes sense in context of detection, right? If so, I'll wait until we resolve 1)

cc: @effigies

@adelavega
Copy link
Contributor Author

One more thing: I noticed CopyXForm sneakily also does a "pop" off a list. Thus, I put it back in, partly because its used for those outputs in the other workflow, and thus might be necessary.

@effigies
Copy link
Member

Cool. Will have a look now. Should be able to fix the tests by merging/rebasing master.

@pep8speaks
Copy link

pep8speaks commented Oct 11, 2019

Hello @adelavega! Thanks for updating this PR. We checked the lines you've touched for PEP 8 issues, and found:

There are currently no PEP 8 issues detected in this Pull Request. Cheers! 🍻

Comment last updated at 2020-02-07 20:47:11 UTC

@effigies
Copy link
Member

Haven't configured the PEP8 speaks... Ignore the line lengths.

@effigies
Copy link
Member

Also, something's come up, and I might not finish a review this afternoon.

@adelavega
Copy link
Contributor Author

update: I didnt realize brain_extraction_wf gets the average T1w as input. In that case, the thresh_low of 0 wasn't high enough and resulted in masks like:

image

I think this led to a series of errors which prevented confounds from being extracted.

I set it to 10, and that looks pretty close to what you get by threshold a single T1w with 0. A treshold of 1 should also be acceptable. I have no clue what the range of values of T1w images tend to be.

image

I'll re-run with these values.

@adelavega adelavega force-pushed the enh/skull_strip_skip branch from 83ba99c to 7ee3c6a Compare October 14, 2019 23:16
@adelavega
Copy link
Contributor Author

I undid the merge master (at least temporarily), because it seems that fmriprep is lagging behind smriprep (specifically in the naming of the outputs that was recently done)

@effigies
Copy link
Member

What if we ask users to provide a brain mask? Then we just use it.

@adelavega
Copy link
Contributor Author

But then it would have to be per subject, and I think the brain mask extraction works pretty well (so why create more work for the user?)

Given this is fairly niche (although IMO important, given the # of openneuro datasets like this), I think a CLI flag might be a good and easy solution (especially since we would need an over-ride CLI flag anyways, so were adding yet another option no matter what).

@adelavega
Copy link
Contributor Author

adelavega commented Jan 14, 2020

Before we all forget about this PR, let me summarize:

  • The n4_only workflow is merged and released on niworkflows
  • The skull-strip detection seems to work fairly well, but we are concerned about edge cases

So we need to decide if s/fmriprep should either:

  1. Detect skull stripped T1ws, with a flag to disable detection and --force-skull-strip. This is good if we think there will be false positives (non-stripped T1ws detected as pre-stripped).
  2. The same as above, but instead of force, a --skip-skull-strip flag. This is good if we think there will be false negatives.
  3. A flag with the following options: auto (default) force & skip. This is the most flexible.
  4. No detection, only a --skip-skull-strip flag

Given that the detection could fail either way (although honestly, I think it's probably unlikely), I would vote for 3).

I'm also fine with 4), as I think users with pre-stripped datasets may already know it (although I didn't at first, and I was puzzled as to why my results looked bad).

Copy link
Member

@effigies effigies left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Alas, I forgot about this PR. But thanks for the summary.

I'd be okay with a --skip-skull-strip flag (or maybe --pre-skull-stripped?) to override detection.

Some additional comments, but otherwise this is looking good. We're going to have merge conflicts with #159. IDK if you want to preemptively merge/rebase on that, or try to squeeze this in and make @mgxd do the work.

@adelavega
Copy link
Contributor Author

DK if you want to preemptively merge/rebase on that, or try to squeeze this in and make @mgxd do the work.

Ahem, the latter if possible.

@@ -329,7 +329,7 @@ def init_single_subject_wf(
hires=hires,
longitudinal=longitudinal,
name="anat_preproc_wf",
num_t1w=len(subject_data['t1w']),
t1w=subject_data['t1w'],
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You need to pass skip_brain_extraction here, and up the chain to the CLI.

@effigies
Copy link
Member

@effigies
Copy link
Member

Pulling from a resolved comment:

We also need to update workflow.__desc__ to accurately reflect:

  1. Skull-stripping is run
  2. Skull-stripping is skipped
  3. Pre-skull-stripped images were detected, and so skull-stripping was skipped.

@@ -95,6 +95,9 @@ def get_parser():
g_ants.add_argument('--skull-strip-fixed-seed', action='store_true',
help='do not use a random seed for skull-stripping - will ensure '
'run-to-run replicability when used with --omp-nthreads 1')
g_ants.add_argument(
'--skip-brain-extraction', required=False, action='store_true',
default=False, help='Skip the brain extraction workflow (for pre-extracted T1ws))')
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm. This takes care of false negatives, but I suppose we should consider false positives, where we might incorrectly decide an image is skull-stripped.

Maybe we want --skull-strip {auto,skip,force}?

Or am I over-thinking the problem?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, I previously suggested this, but also worried I was overthinking it. False positives are actually probably a bigger problem in a way (many more people have normal T1s)

If you're comfortable with the additional CLI complexity, I think it this makes the most sense.

Again, we could also remove the detection if we think its not reliable, and only have a skip detection flag.

My preferences in order: 1) CLI argument w/ 3 options 2) skip only CLI flag, with no detection 3) skip flag + detection (current)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I lean toward 1 as well.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@mgxd ? any opinion

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i'm fine with the 3 way approach, with the default being auto. It should definitely document which route was taken

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure, how? You mean which option was select or what auto detected? And if so, where do I log that?

Copy link
Collaborator

@mgxd mgxd left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

cool! couple comments

@@ -22,15 +25,17 @@
)
from niworkflows.interfaces.images import TemplateDimensions, Conform, ValidateImage
from niworkflows.utils.misc import fix_multi_T1w_source_name, add_suffix
from niworkflows.anat.ants import init_brain_extraction_wf
from niworkflows.anat.ants import (
init_brain_extraction_wf, init_n4_only_wf, _pop
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not a huge fan of importing "private" functions

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure, it's just a pop function so we can redefine.

templateflow
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why is this different? spacing?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not sure 🤷

@@ -95,6 +95,9 @@ def get_parser():
g_ants.add_argument('--skull-strip-fixed-seed', action='store_true',
help='do not use a random seed for skull-stripping - will ensure '
'run-to-run replicability when used with --omp-nthreads 1')
g_ants.add_argument(
'--skip-brain-extraction', required=False, action='store_true',
default=False, help='Skip the brain extraction workflow (for pre-extracted T1ws))')
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i'm fine with the 3 way approach, with the default being auto. It should definitely document which route was taken

@effigies
Copy link
Member

effigies commented Feb 5, 2020

Looks like @mgxd got in first. Merge/rebase?

Copy link
Collaborator

@mgxd mgxd left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm gonna cut a new release in order to get an fmriprep release out, but this is close. I can clean up the git history once all the above is addressed 👍

@mgxd mgxd modified the milestones: 0.5.0, 0.6.0 Feb 6, 2020
@effigies
Copy link
Member

Closed in favor of #167.

@effigies effigies closed this Feb 12, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Gracefully handle pre-skull-stripped T1w images
5 participants