diff --git a/CHANGES.rst b/CHANGES.rst index 0b55041d..1e6399fb 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -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 `_) +- 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 `_) Bug fixes ######### diff --git a/radiomics/imageoperations.py b/radiomics/imageoperations.py index d6cfc98e..3fbbc2bc 100644 --- a/radiomics/imageoperations.py +++ b/radiomics/imageoperations.py @@ -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 @@ -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. @@ -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, diff --git a/radiomics/schemas/paramSchema.yaml b/radiomics/schemas/paramSchema.yaml index 38cb1eae..1c044f11 100644 --- a/radiomics/schemas/paramSchema.yaml +++ b/radiomics/schemas/paramSchema.yaml @@ -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