Skip to content

Commit

Permalink
TEST: Add test baseline for shape2D
Browse files Browse the repository at this point in the history
Add testing of shape 2D, using all 5 radiomics testcases. As shape2D only works with single slice segmentations, add new largest slice segmentations for each test case, and update code to allow adding of a "_2D" suffix to the testcase, indicating this 2D label should be returned as mask.

Additionally update the getTestCase function to raise an error instead of returning None in case the test case could not be obtained.
  • Loading branch information
JoostJM committed Oct 19, 2018
1 parent ba07b03 commit 8ef7c8e
Show file tree
Hide file tree
Showing 4 changed files with 86 additions and 32 deletions.
42 changes: 42 additions & 0 deletions data/baseline/baseline_shape2D.csv
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
featureName,brain1_shape2D,brain2_shape2D,breast1_shape2D,lung1_shape2D,lung2_shape2D
diagnostics_Image-original_Maximum,3057.0,1523.0,248.0,3071.0,1671.0
diagnostics_Mask-original_CenterOfMass,"(43.13548018292683, 20.012385670731703, 26.000000000000014)","(57.44906387665198, -22.31087860241864, -10.700000762939446)","(-99.99007990243902, -1.789887073170732, 28.35000000000003)","(-21.721181556195972, -120.73608609510089, -612.4999999999998)","(53.84355387019346, -97.05089679126613, -249.9999999999999)"
diagnostics_Mask-corrected_VolumeNum,1,1,1,1,1
diagnostics_Mask-corrected_Size,"(256, 256, 25)","(256, 256, 23)","(45, 120, 21)","(512, 512, 48)","(512, 512, 61)"
diagnostics_Mask-corrected_Mean,859.3841463414634,422.5947136563877,131.1829268292683,-51.518731988472624,17.4510550996483
diagnostics_Image-original_Minimum,0.0,0.0,6.0,-1024.0,-1024.0
diagnostics_Mask-corrected_Spacing,"(0.7812499999999999, 0.7812499999999999, 6.499999999999998)","(0.7812499999999999, 0.7812499999999999, 6.499999999999998)","(0.664062, 0.664062, 2.1)","(0.5703125, 0.5703125, 5.0)","(0.6269531, 0.6269531, 5.0)"
diagnostics_Versions_Numpy,1.15.1,1.15.1,1.15.1,1.15.1,1.15.1
diagnostics_Mask-corrected_CenterOfMass,"(43.13548018292683, 20.012385670731703, 26.000000000000014)","(57.44906387665198, -22.31087860241864, -10.700000762939446)","(-99.99007990243902, -1.789887073170732, 28.35000000000003)","(-21.721181556195972, -120.73608609510089, -612.4999999999998)","(53.84355387019346, -97.05089679126613, -249.9999999999999)"
diagnostics_Mask-corrected_CenterOfMassIndex,"(182.71341463414635, 101.88414634146342, 16.0)","(214.33480176211455, 160.9339207048458, 8.0)","(24.926829268292682, 69.8048780487805, 9.0)","(217.9135446685879, 358.1613832853026, 33.0)","(357.8314771395076, 362.784876905041, 22.0)"
diagnostics_Mask-corrected_BoundingBox,"(162, 84, 16, 45, 32, 1)","(205, 155, 8, 20, 14, 1)","(21, 64, 9, 9, 12, 1)","(206, 347, 33, 24, 22, 1)","(321, 334, 22, 72, 65, 1)"
diagnostics_Versions_SimpleITK,1.1.0,0.9.1,0.9.1,0.9.1,0.9.1
diagnostics_Mask-original_CenterOfMassIndex,"(182.71341463414635, 101.88414634146342, 0.0)","(214.33480176211455, 160.9339207048458, 0.0)","(24.926829268292682, 69.8048780487805, 0.0)","(217.9135446685879, 358.1613832853026, 0.0)","(357.8314771395076, 362.784876905041, 0.0)"
diagnostics_Configuration_EnabledImageTypes,{'Original': {}},{'Original': {}},{'Original': {}},{'Original': {}},{'Original': {}}
diagnostics_Image-original_Hash,5c9ce3ca174f0f8324aa4d277e0fef82dc5ac566,f2b8fbc4d5d1da08a1a70e79a301f8a830139438,016951a8f9e8e5de21092d9d62b77262f92e04a5,34dca4200809a5e76c702d6b9503d958093057a3,14f57fd04838eb8c9cca2a0dd871d29971585975
diagnostics_Versions_PyWavelet,0.5.1,0.5.2,0.5.2,0.5.2,0.5.2
diagnostics_Mask-original_Size,"(256, 256, 1)","(256, 256, 1)","(45, 120, 1)","(512, 512, 1)","(512, 512, 1)"
diagnostics_Mask-original_VoxelNum,984,227,82,347,3412
diagnostics_Versions_Python,3.5.3,2.7.11,2.7.11,2.7.11,2.7.11
diagnostics_Mask-corrected_Maximum,1261.0,729.0,162.0,106.0,108.0
diagnostics_Image-original_Size,"(256, 256, 25)","(256, 256, 23)","(45, 120, 21)","(512, 512, 48)","(512, 512, 61)"
diagnostics_Configuration_Settings,"{'additionalInfo': True, 'interpolator': 'sitkBSpline', 'minimumROISize': None, 'force2D': True, 'minimumROIDimensions': 2, 'preCrop': False, 'resegmentRange': None, 'correctMask': True, 'normalize': False, 'distances': [1], 'removeOutliers': None, 'label': 1, 'resampledPixelSpacing': None, 'normalizeScale': 1, 'force2Ddimension': 0, 'padDistance': 5}","{'distances': [1], 'additionalInfo': True, 'force2D': True, 'interpolator': 'sitkBSpline', 'resampledPixelSpacing': None, 'label': 1, 'normalizeScale': 1, 'normalize': False, 'force2Ddimension': 0, 'removeOutliers': None, 'minimumROISize': None, 'minimumROIDimensions': 2, 'correctMask': True, 'preCrop': False, 'resegmentRange': None, 'padDistance': 5}","{'distances': [1], 'additionalInfo': True, 'force2D': True, 'interpolator': 'sitkBSpline', 'resampledPixelSpacing': None, 'label': 1, 'normalizeScale': 1, 'normalize': False, 'force2Ddimension': 0, 'removeOutliers': None, 'minimumROISize': None, 'minimumROIDimensions': 2, 'correctMask': True, 'preCrop': False, 'resegmentRange': None, 'padDistance': 5}","{'distances': [1], 'additionalInfo': True, 'force2D': True, 'interpolator': 'sitkBSpline', 'resampledPixelSpacing': None, 'label': 1, 'normalizeScale': 1, 'normalize': False, 'force2Ddimension': 0, 'removeOutliers': None, 'minimumROISize': None, 'minimumROIDimensions': 2, 'correctMask': True, 'preCrop': False, 'resegmentRange': None, 'padDistance': 5}","{'distances': [1], 'additionalInfo': True, 'force2D': True, 'interpolator': 'sitkBSpline', 'resampledPixelSpacing': None, 'label': 1, 'normalizeScale': 1, 'normalize': False, 'force2Ddimension': 0, 'removeOutliers': None, 'minimumROISize': None, 'minimumROIDimensions': 2, 'correctMask': True, 'preCrop': False, 'resegmentRange': None, 'padDistance': 5}"
diagnostics_Configuration_TestCase,brain1_2D,brain2_2D,breast1_2D,lung1_2D,lung2_2D
diagnostics_Mask-corrected_Minimum,546.0,206.0,106.0,-506.0,-677.0
diagnostics_Mask-original_Spacing,"(0.7812499999999999, 0.7812499999999999, 6.499999999999998)","(0.7812499999999999, 0.7812499999999999, 6.499999999999998)","(0.664062, 0.664062, 2.1)","(0.5703125, 0.5703125, 5.0)","(0.6269531, 0.6269531, 5.0)"
diagnostics_Versions_PyRadiomics,2.1.0.post17.dev0+gba07b03,2.1.0.post17.dev0+gba07b03,2.1.0.post17.dev0+gba07b03,2.1.0.post17.dev0+gba07b03,2.1.0.post17.dev0+gba07b03
diagnostics_Mask-original_Hash,c686d815c4383f3ca71a1afb07d6f871172a7dac,1d3b86cb29e4ab89fd40c0fdaddea1110ca21bfc,378ace5e4d190eaeb0d69fdc80b2b7eaeb61ffd6,1c84c30b5030441c864b3569b88919459344af9a,0094f5cfe799b0bd5603f6b7cafdc6387be39d66
diagnostics_Mask-corrected_VoxelNum,984,227,82,347,3412
diagnostics_Image-original_Spacing,"(0.7812499999999999, 0.7812499999999999, 6.499999999999998)","(0.7812499999999999, 0.7812499999999999, 6.499999999999998)","(0.664062, 0.664062, 2.1)","(0.5703125, 0.5703125, 5.0)","(0.6269531, 0.6269531, 5.0)"
diagnostics_Mask-original_VolumeNum,1,1,1,1,1
diagnostics_Image-original_Mean,385.6564080810547,236.20217696480128,63.380440917107585,-577.9148241678873,-433.28809775680793
diagnostics_Mask-original_BoundingBox,"(162, 84, 0, 45, 32, 1)","(205, 155, 0, 20, 14, 1)","(21, 64, 0, 9, 12, 1)","(206, 347, 0, 24, 22, 1)","(321, 334, 0, 72, 65, 1)"
original_shape2D_MeshSurface,600.28076171875,138.244628906,35.9397346973,112.701324463,1340.95895182
original_shape2D_PixelSurface,600.5859374999998,138.54980468749994,36.160223867208,112.86395263671875,1341.1554869138695
original_shape2D_Perimeter,101.0913825153671,48.0908978016,23.6116203532,43.4485541519,156.412404051
original_shape2D_PerimeterSurfaceRatio,0.16840683387206656,0.347868110191,0.656978148339,0.385519463581,0.116642201343
original_shape2D_Sphericity,0.8591480496460207,0.8666947794872987,0.9000498705600878,0.8661520010380676,0.8299300366556801
original_shape2D_MaximumDiameter,35.579054802299055,16.3129789202,8.39979371027,16.3113363151,46.3182155834
original_shape2D_MajorAxisLength,33.85279086841539,16.570640783628463,8.259750107622313,16.33412649719061,45.38958860924436
original_shape2D_MinorAxisLength,23.785948973581707,10.963260379640587,5.729800201543435,8.986202904129604,38.4309041153114
original_shape2D_Elongation,0.7026288930220511,0.6616075094978898,0.6937013985757058,0.5501489721948207,0.8466898531766884
60 changes: 35 additions & 25 deletions radiomics/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -135,53 +135,63 @@ def getTestCase(testCase, dataDirectory=None):
If the test case has been found or downloaded successfully, this function returns a tuple of two strings:
``(path/to/image.nrrd, path/to/mask.nrrd)``. In case of an error ``(None, None)`` is returned.
.. note::
To get the testcase with the corresponding single-slice label, append "_2D" to the testCase.
"""
global logger, testCases
label2D = False
testCase = testCase.lower()
if testCase.endswith('_2d'):
label2D = True
testCase = testCase[:-3]

if testCase not in testCases:
logger.error('Testcase "%s" not recognized!', testCase)
return None, None
raise ValueError('Testcase "%s" not recognized!' % testCase)

logger.debug('Getting test case %s', testCase)

if dataDirectory is None:
dataDirectory = os.path.join(tempfile.gettempdir(), 'pyradiomics', 'data')
logger.debug('No data directory specified, using temporary directory "%s"', dataDirectory)

# Check if test case has already been downloaded.
imageFile = os.path.join(dataDirectory, '%s_image.nrrd' % testCase)
maskFile = os.path.join(dataDirectory, '%s_label.nrrd' % testCase)
if os.path.isfile(imageFile) and os.path.isfile(maskFile):
logger.info('Test case already downloaded')
return imageFile, maskFile
im_name = '%s_image.nrrd' % testCase
ma_name = '%s_label%s.nrrd' % (testCase, '_2D' if label2D else '')

def get_or_download(fname):
target = os.path.join(dataDirectory, fname)
if os.path.exists(target):
logger.debug('File %s already downloaded', fname)
return target

# Test case not found, so try to download it
logger.info("Test case %s not available locally, downloading test case...", testCase)
# Test case file not found, so try to download it
logger.info("Test case file %s not available locally, downloading from github...", fname)

try:
# First check if the folder is available
if not os.path.isdir(dataDirectory):
logger.debug('Creating data directory: %s', dataDirectory)
os.makedirs(dataDirectory)

# Download the test case files (image and label)
url = r'https://github.com/Radiomics/pyradiomics/releases/download/v1.0/%s_%s.nrrd'
url = r'https://github.com/Radiomics/pyradiomics/releases/download/v1.0/%s' % fname

logger.debug('Retrieving image at %s', url % (testCase, 'image'))
fname, headers = urllib.request.urlretrieve(url % (testCase, 'image'), imageFile)
if headers.get('status', '') == '404 Not Found':
logger.warning('Unable to download image file at %s!', url % (testCase, 'image'))
return None, None
logger.debug('Retrieving file at %s', url)
_, headers = urllib.request.urlretrieve(url, target)

logger.debug('Retrieving mask at %s', url % (testCase, 'label'))
fname, headers = urllib.request.urlretrieve(url % (testCase, 'label'), maskFile)
if headers.get('status', '') == '404 Not Found':
logger.warning('Unable to download mask file at %s!', url % (testCase, 'label'))
raise ValueError('Unable to download image file at %s!', url)

logger.info('File %s downloaded', fname)
return target

logger.debug('Getting Image file')
imageFile = get_or_download(im_name)

logger.debug('Getting Mask file')
maskFile = get_or_download(ma_name)

logger.info('Test case %s downloaded', testCase)
return imageFile, maskFile
except Exception:
logger.error('Download failed!', exc_info=True)
return None, None
return imageFile, maskFile


def getParameterValidationFiles():
Expand Down
9 changes: 6 additions & 3 deletions tests/addTest.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,13 @@ def main(argv=None):

radiomics.logger.addHandler(handler)

testcases = list(radiomics.testCases)
testcases += [t + '_2D' for t in testcases]

parser = argparse.ArgumentParser()
parser.add_argument('TestName', type=str, help='Name for the new test, must not be already present in the baseline')
parser.add_argument('TestCase', type=str, choices=list(radiomics.testCases), help='Test image and segmentation to '
'use in the new test')
parser.add_argument('TestCase', type=str, choices=testcases, help='Test image and segmentation to '
'use in the new test')
parser.add_argument('Configuration', metavar='FILE', default=None,
help='Parameter file containing the settings to be used in extraction')
parser.add_argument('--force', '-f', action='store_true', help='When the test is already known for the class, '
Expand All @@ -38,7 +41,7 @@ def main(argv=None):
testutils = RadiomicsTestUtils()

try:
assert args.TestCase in radiomics.testCases
assert args.TestCase.lower().replace('_2d', '') in radiomics.testCases
except AssertionError:
logger.error('Input not valid, cancelling addTest!')
exit(1)
Expand Down
7 changes: 3 additions & 4 deletions tests/testUtils.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
import SimpleITK as sitk
import six

from radiomics import featureextractor, getTestCase, imageoperations, testCases
from radiomics import featureextractor, getTestCase, imageoperations

# Get the logger. This is done outside the class, as it is needed by both the class and the custom_name_func
logger = logging.getLogger('radiomics.testing')
Expand Down Expand Up @@ -154,9 +154,8 @@ class or test case is changed, function returns True.
if self._testCase != self._current_config['TestCase']:
self._testCase = self._current_config['TestCase']
self._logger.info("Reading the image and mask for test case %s", self._testCase)
assert self._current_config['TestCase'] in testCases

imageName, maskName = getTestCase(self._testCase)
imageName, maskName = getTestCase(self._testCase) # Throws ValueError if test case is not recognized

assert imageName is not None
assert maskName is not None
Expand Down Expand Up @@ -419,7 +418,7 @@ def writeBaselineFile(self, baselineDir):
header = ['featureName'] + cases
csvWriter.writerow(header)

config = self.configuration[cases[1]].keys()
config = self.configuration[cases[0]].keys()
self._configKeys += list(set(config) - set(self._configKeys))
for c in self._configKeys:
if c not in config:
Expand Down

0 comments on commit 8ef7c8e

Please sign in to comment.