Skip to content

ENH: Add Rescale interface #2599

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 3 commits into from
Jun 6, 2018
Merged

ENH: Add Rescale interface #2599

merged 3 commits into from
Jun 6, 2018

Conversation

effigies
Copy link
Member

Upstreaming another interface from fMRIPrep. This one is based off of fmriprep.interfaces.images.InvertT1w. It rescales a skull-stripped image to match the rough statistics of another, which is not restricted to the case in which we applied it, so I renamed it.

Differences from InvertT1w:

  • Contrast inversion is optional (invert input)
  • Identifying maxima/minima is replaced with quantile-based statistics, allowing for rescaling that is less sensitive to outliers
  • Pure nibabel instead of nilearn, as the only nilearn feature used was new_img_like

The original behavior is recoverable simply with Rescale(invert=True, percentile=0.).

@effigies effigies added this to the 1.1.0 milestone May 27, 2018
@codecov-io
Copy link

codecov-io commented May 27, 2018

Codecov Report

Merging #2599 into master will decrease coverage by 0.02%.
The diff coverage is 40.74%.

Impacted file tree graph

@@            Coverage Diff             @@
##           master    #2599      +/-   ##
==========================================
- Coverage   67.62%   67.59%   -0.03%     
==========================================
  Files         339      339              
  Lines       42787    42814      +27     
  Branches     5288     5288              
==========================================
+ Hits        28934    28941       +7     
- Misses      13171    13191      +20     
  Partials      682      682
Flag Coverage Δ
#smoketests 50.76% <ø> (ø) ⬆️
#unittests 65.08% <40.74%> (ø) ⬆️
Impacted Files Coverage Δ
nipype/interfaces/image.py 73.07% <40.74%> (-17.12%) ⬇️
nipype/pipeline/plugins/multiproc.py 79.76% <0%> (-2.39%) ⬇️

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 205ea17...27ebb6d. Read the comment docs.

class Rescale(SimpleInterface):
"""Rescale an image

Rescales the non-zero portion of ``in_file`` to match the bounds of the '
Copy link
Contributor

Choose a reason for hiding this comment

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

single comma at the end of the line

ref_file = File(exists=True, mandatory=True,
desc='Skull-stripped reference image')
invert = traits.Bool(desc='Invert contrast of rescaled image')
percentile = traits.Range(low=0., high=50., value=0., usedefault=True,
Copy link
Contributor

Choose a reason for hiding this comment

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

it is very unlikely the same percentile works well for both ends of the range. I would split this option or make it a tuple.

Copy link
Member Author

Choose a reason for hiding this comment

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

Hmm. I expected that people would probably only ever want 0 or 1 (but left room for tweaking), but I can add more flexibility than this. This leads me to wonder if we want to permit different percentiles for the input and reference images. In which case we need 4 values, not 2.

WDYT? If possible, it'd be nice to allow a single knob, to keep it simple until somebody needs more refined control.

Copy link
Contributor

Choose a reason for hiding this comment

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

WDYT?:

    percentile = traits.Either(
        traits.Range(low=0., high=50, value=0),  # One-knob behavior
        traits.Tuple(
            traits.Either(None, traits.Range(low=0., high=50, value=0)),
            traits.Either(None, traits.Range(low=50., high=100, value=100)),
        ), desc='...')

Then, if it is not a tuple, it has your one-knob mirror behavior. Otherwise you have a tuple, and you can always set one of the boundaries.

Copy link
Contributor

Choose a reason for hiding this comment

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

My fixation for this is that T1 images typically show background pixels under the 15% and, except when there are WM hyperintensities, you probably want 98% or higher for the upper limit.

Copy link
Member Author

Choose a reason for hiding this comment

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

So, if I'm reading this correct, we don't need to be concerned with supporting different percentiles for the input and the reference?

My fixation for this is that T1 images typically show background pixels under the 15% and, except when there are WM hyperintensities, you probably want 98% or higher for the upper limit.

This is true for skull-stripped images?

Copy link
Contributor

Choose a reason for hiding this comment

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

Nope, but I think we should aim for this interface to work well in any situation.

Copy link
Contributor

Choose a reason for hiding this comment

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

So, if I'm reading this correct, we don't need to be concerned with supporting different percentiles for the input and the reference?

Okay, gotcha. I'm always thinking of the input. I guess we should specify that this interface expects the reference file to be "consistent" (as in already thresholded).

Copy link
Member Author

Choose a reason for hiding this comment

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

I think we should aim for this interface to work well in any situation.

I don't think this interface will work at all well in non-skull-stripped cases. Calculating the magic numbers needed won't be significantly easier or more robust than even mediocre skull stripping, if I were to make a guess.

I guess we should specify that this interface expects the reference file to be "consistent" (as in already thresholded).

I do say:

in_file = File(exists=True, mandatory=True,
               desc='Skull-stripped image to rescale')
ref_file = File(exists=True, mandatory=True,
                desc='Skull-stripped reference image')

And:

Rescales the non-zero portion of ``in_file`` to match the bounds of the
non-zero portion of ``ref_file``.

But I can very explicitly say in the docstring that they should be skull-stripped. And would you suggest not applying the supplied percentiles to the reference image, as well?

I guess what's your vision for what this interface should do when percentiles != (0, 100)?

@effigies
Copy link
Member Author

effigies commented Jun 6, 2018

@oesteban No rush, but just letting you know that I'm waiting for our discussion to resolve before making further changes.

@oesteban
Copy link
Contributor

oesteban commented Jun 6, 2018

Sorry. We should go ahead with this, I am probably talking of a nonexistent problem. If my suggestion is an actual issue, it'll be straightforward to identify and fix.

@effigies effigies merged commit cd5567b into nipy:master Jun 6, 2018
@effigies effigies deleted the enh/rescale branch June 6, 2018 19:47
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.

3 participants