Skip to content
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

Allow a 0 value for output spacing when resampling #299

Merged
merged 1 commit into from
Sep 7, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGES.rst
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,10 @@ New Features
- Add a row by row customization of the extraction label in the batch processing command line script, as well as both
batchprocessing examples.
(`#262 <https://github.com/Radiomics/pyradiomics/pull/262>`_)
- Allow value 0 for a resampled pixel spacing (per dimension). Values of 0 are replaced by the spacing for that
dimension as it is in the original (non-resampled) mask. This allows resampling over a subset of dimension (e.g. only
in-plane resampling when out-of-plane spacing is set to 0).
(`#299 <https://github.com/Radiomics/pyradiomics/pull/299>`_)

Bug fixes
#########
Expand Down
4 changes: 3 additions & 1 deletion docs/customization.rst
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,9 @@ Feature Extractor Level

*Resampling the image*

- resampledPixelSpacing [None]: List of 3 floats (> 0), sets the size of the voxel in (x, y, z) plane when resampling.
- resampledPixelSpacing [None]: List of 3 floats (>= 0), sets the size of the voxel in (x, y, z) plane when resampling.
A value of 0 is replaced with the spacing for that dimension as it is in the original (non-resampled) mask, thereby
enabling only in-plane resampling, for example.
- interpolator [sitkBSpline]: Simple ITK constant or string name thereof, sets interpolator to use for resampling.
Enumerated value, possible values:

Expand Down
19 changes: 14 additions & 5 deletions radiomics/imageoperations.py
Original file line number Diff line number Diff line change
Expand Up @@ -414,6 +414,9 @@ def resampleImage(imageNode, maskNode, resampledPixelSpacing, interpolator=sitk.
'imageNode' and 'maskNode' are SimpleITK Objects, and 'resampledPixelSpacing' is the output pixel spacing (sequence of
3 elements).

If only in-plane resampling is required, set the output pixel spacing for the out-of-plane dimension (usually the last
dimension) to 0. Spacings with a value of 0 are replaced by the spacing as it is in the original mask.

Only part of the image and labelmap are resampled. The resampling grid is aligned to the input origin, but only voxels
covering the area of the image ROI (defined by the bounding box) and the padDistance are resampled. This results in a
resampled and partially cropped image and mask. Additional padding is required as some filters also sample voxels
Expand Down Expand Up @@ -443,14 +446,13 @@ def resampleImage(imageNode, maskNode, resampledPixelSpacing, interpolator=sitk.
if imageNode is None or maskNode is None:
return None, None # this function is expected to always return a tuple of 2 elements

logger.debug('Comparing resampled spacing to original spacing (image and mask')
maskSpacing = numpy.array(maskNode.GetSpacing())
imageSpacing = numpy.array(imageNode.GetSpacing())

# If current spacing is equal to resampledPixelSpacing, no interpolation is needed
if numpy.array_equal(maskSpacing, resampledPixelSpacing) and numpy.array_equal(imageSpacing, resampledPixelSpacing):
logger.info('New spacing equal to old, no resampling required')
return imageNode, maskNode
# If spacing for a direction is set to 0, use the original spacing (enables "only in-slice" resampling)
logger.debug('Where resampled spacing is set to 0, set it to the original spacing (mask)')
resampledPixelSpacing = numpy.array(resampledPixelSpacing)
resampledPixelSpacing = numpy.where(resampledPixelSpacing == 0, maskSpacing, resampledPixelSpacing)

# Check if the maskNode contains a valid ROI. If ROI is valid, the bounding box needed to calculate the resampling
# grid is returned.
Expand All @@ -463,6 +465,13 @@ def resampleImage(imageNode, maskNode, resampledPixelSpacing, interpolator=sitk.
maskSize = numpy.array(maskNode.GetSize())
resampledPixelSpacing = numpy.where(bb[3:] != 1, resampledPixelSpacing, maskSpacing)

# If current spacing is equal to resampledPixelSpacing, no interpolation is needed
# Tolerance = 1e-5 + 1e-8*abs(resampledSpacing)
logger.debug('Comparing resampled spacing to original spacing (image and mask')
if numpy.allclose(maskSpacing, resampledPixelSpacing) and numpy.allclose(imageSpacing, resampledPixelSpacing):
logger.info('New spacing equal to old, no resampling required')
return imageNode, maskNode

spacingRatio = maskSpacing / resampledPixelSpacing

# Determine bounds of cropped volume in terms of new Index coordinate space,
Expand Down
4 changes: 2 additions & 2 deletions radiomics/schemas/paramSchema.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -43,9 +43,9 @@ mapping:
min-ex: 0
resampledPixelSpacing:
seq:
- type: int
- type: float
range:
min-ex: 0
min: 0
interpolator:
type: any
func: checkInterpolator
Expand Down