Skip to content

Commit

Permalink
ENH: Allow a 0 value for output spacing when resampling
Browse files Browse the repository at this point in the history
When resampling, allow the output spacing for any dimension to be set to 0 (the parameter 'resampledPixelSpacing' still needs to be a sequence of 3 elements, but the value of any element can be 0). Doing so will enable 'preserving' the original spacing, as 0 values are replaced with the original spacing for that dimension (original spacing of the mask). This way, it is possible to only resample in-plane (by setting the out-of-plane output spacing to 0).

Additionally, move the check between original and resampled spacing to after the size check (no resampling over dimensions for which bounding box size = 1) and build in a tolerance (1e-5 + 1e-8 * abs(resampledspacing)).

Finally, fix data type for resampled spacing in the validation schema for parameter files (change from integer to float).
  • Loading branch information
JoostJM committed Sep 4, 2017
1 parent 5ce9f48 commit 850d589
Show file tree
Hide file tree
Showing 3 changed files with 19 additions and 7 deletions.
3 changes: 3 additions & 0 deletions CHANGES.rst
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@ 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 original spacing (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
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 original spacing when applying this function.
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

0 comments on commit 850d589

Please sign in to comment.