Skip to content

Commit

Permalink
Merge pull request #299 from JoostJM/update-resampling
Browse files Browse the repository at this point in the history
Allow a 0 value for output spacing when resampling
  • Loading branch information
JoostJM authored Sep 7, 2017
2 parents 1c52a10 + 0f6a0d1 commit 4f9abf1
Show file tree
Hide file tree
Showing 4 changed files with 23 additions and 8 deletions.
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

0 comments on commit 4f9abf1

Please sign in to comment.