AIM-Harvard / pyradiomics

Open-source python package for the extraction of Radiomics features from 2D and 3D images and binary masks. Support: https://discourse.slicer.org/c/community/radiomics
http://pyradiomics.readthedocs.io/
BSD 3-Clause "New" or "Revised" License
1.16k stars 500 forks source link

Analyzing multiple nii files at same time #451

Closed abhinandanbatra closed 5 years ago

abhinandanbatra commented 5 years ago

Hi, I am having an issue reading multiple files by looping over different cases in pyradiomics. I am using following code for Case_id in range(1,6): path = '/Users/abhinandanbatra/Desktop/pyradiomics/Converted/Case{}/'.format(Case_id) for files in os.listdir(path): if fnmatch.fnmatch(files,'*Image.nii'): print(files) image= sitk.ReadImage(files) for mask in os.listdir(path): if fnmatch.fnmatch(mask,'*lable.nii'): print(mask) mask=sitk.ReadImage(mask) features[case_id] = extractor.execute(image, mask)

I am getting following error:

RuntimeError Traceback (most recent call last)

in () 7 if fnmatch.fnmatch(files,'*Image.nii'): 8 print(files) ----> 9 image= sitk.ReadImage(files) 10 for mask in os.listdir(path): 11 if fnmatch.fnmatch(mask,'*lable.nii'): /anaconda3/lib/python3.7/site-packages/SimpleITK/SimpleITK.py in ReadImage(*args) 8612 8613 """ -> 8614 return _SimpleITK.ReadImage(*args) 8615 class HashImageFilter(ProcessObject): 8616 """ RuntimeError: Exception thrown in SimpleITK ReadImage: /scratch/dashboard/SimpleITK-OSX10.6-x86_64-pkg/SimpleITK/Code/IO/src/sitkImageReaderBase.cxx:89: sitk::ERROR: The file "C06920110818Image.nii" does not exist. I can get an output of images if I print the name of the file but I cannot read them. Is it something to do with how we do naming. Will appreciate your help
JoostJM commented 5 years ago

sitk::ERROR: The file "C06920110818Image.nii" does not exist.

This indicates that the file does not exist. The error is due to the fact that you list files in path, but then try to load just the file name. use sitk.ReadImage(os.path.join(path, files)) instead

abhinandanbatra commented 5 years ago

Thank you so much for the help. I am able to locate the Cases and loop through them. I am running into another issue. When I run the analysis by looping through Cases I am getting the following error

"ValueError: Image/Mask datatype or size mismatch. Potential fix: enable correct mask, see Documentation:Usage:Customizing the Extraction:Settings:correctMask for more information"

But when I run each Case separately I am able to get the results. Here is the code I am using for looping through cases based on your suggestion earlier for Case_id in range(1,6): path = '/Users/abhinandanbatra/Desktop/pyradiomics/Converted/Case{}/'.format(Case_id) print(path) for image in os.listdir(path): if fnmatch.fnmatch(image,'*Image.nii'): print(image) image=sitk.ReadImage(os.path.join(path,image)) for mask in os.listdir(path): if fnmatch.fnmatch(mask,'*label.nii'): print(mask) mask=sitk.ReadImage(os.path.join(path,mask)) result = extractor.execute(image, mask) print('Result type:', type(result)) # result is returned in a Python ordered dictionary) print('') print('Calculated features for Case {}:'.format(Case_id)) for key, value in six.iteritems(result): print('\t', key, ':', value)

I would appreciate your help

JoostJM commented 5 years ago

@abhinandanbatra The error you are seeing should also happen for the case separately. It indicates there is a difference in size/direction/origin (i.e. geometry) of the image and mask. This does could indicate that the wrong mask is used, but is also the case when the mask represents a cropped area of the image (3D slicer does this to preserve space).

PyRadiomics can handle this, you only need to specify correctMask: True in the configuration.

On a side note, PyRadiomics does support parallel processing of a batch of cases, where you can provide the settings in a handy yaml-structured config-file (see examples and documentation of config file and command line usage)

abhinandanbatra commented 5 years ago

Yes, you are right, one case had that an issue. I am really sorry but I am running into another issue when I loop through cases I get the result for the first case but when it loops through the second I get the error

ValueError: Error reading image Filepath or SimpleITK object

I am using the same code as I sent you earlier. Sorry I am new to python

JoostJM commented 5 years ago

@abhinandanbatra No problem. The most likely cause for that particular error is that it can't find the image file or the format is wrong. What happens if you try to load it using 3d Slicer (or a similar program)?

To be precise: PyRadiomics first checks if image is a string (representing a path), and if so, loads it as an sitk image. If not, it checks if it's an SimpleITK image, and uses it if so. If not, the error you're seeing is shown.

If you are new to python, the commandline interface may be easier to use. It only requires you to build a CSV file as input, and contains error handling and optimalization steps.

abhinandanbatra commented 5 years ago

I don't get the error when I run the case separately or load them in 3d slicer. I only get the above error when I am looping through the cases. Here is the whole error, you can see the code was able to analyze the first case but gave an error when looping through the second

/Users/abhinandanbatra/Desktop/pyradiomics/Converted/Case1/ C60620120618Image.nii C60620120618label.nii Result type: <class 'collections.OrderedDict'>

Calculated features for Case 1: diagnostics_Versions_PyRadiomics : 2.1.1 diagnostics_Versions_Numpy : 1.15.1 diagnostics_Versions_SimpleITK : 1.1.0 diagnostics_Versions_PyWavelet : 1.0.0 diagnostics_Versions_Python : 3.7.0 diagnostics_Configuration_Settings : {'minimumROIDimensions': 2, 'minimumROISize': None, 'normalize': False, 'normalizeScale': 1, 'removeOutliers': None, 'resampledPixelSpacing': None, 'interpolator': 'sitkBSpline', 'preCrop': False, 'padDistance': 5, 'distances': [1], 'force2D': False, 'force2Ddimension': 0, 'resegmentRange': None, 'label': 1, 'additionalInfo': True} diagnostics_Configuration_EnabledImageTypes : {'Original': {}} diagnostics_Image-original_Hash : 0f9064b820dd277901caf53ad4eb39c559f08393 diagnostics_Image-original_Spacing : (0.45454493165016174, 0.4545450806617737, 4.999999523162842) diagnostics_Image-original_Size : (176, 176, 5) diagnostics_Image-original_Mean : 0.6588068181818182 diagnostics_Image-original_Minimum : 0.0 diagnostics_Image-original_Maximum : 1.0 diagnostics_Mask-original_Hash : f7c9f6246424ef9b71065a8e2631894b03b7e48d diagnostics_Mask-original_Spacing : (0.45454493165016174, 0.4545450806617737, 4.999999523162842) diagnostics_Mask-original_Size : (176, 176, 5) diagnostics_Mask-original_BoundingBox : (46, 67, 0, 88, 61, 5) diagnostics_Mask-original_VoxelNum : 11233 diagnostics_Mask-original_VolumeNum : 1 diagnostics_Mask-original_CenterOfMassIndex : (93.87892815810558, 101.77664025638742, 1.9518383334817058) diagnostics_Mask-original_CenterOfMass : (-22.959236214174375, -19.89762628720213, -31.168034287612024) original_firstorder_10Percentile : 0.0 original_firstorder_90Percentile : 0.0 original_firstorder_Energy : 703.0 original_firstorder_Entropy : -0.0 original_firstorder_InterquartileRange : 0.0 original_firstorder_Kurtosis : 14.045424506828011 original_firstorder_Maximum : 1.0 original_firstorder_MeanAbsoluteDeviation : 0.11733354010625224 original_firstorder_Mean : 0.0625834594498353 original_firstorder_Median : 0.0 original_firstorder_Minimum : 0.0 original_firstorder_Range : 1.0 original_firstorder_RobustMeanAbsoluteDeviation : 0.0 original_firstorder_RootMeanSquared : 0.2501668632130069 original_firstorder_Skewness : 3.6118450280747103 original_firstorder_TotalEnergy : 726.2381673544756 original_firstorder_Uniformity : 1.0 original_firstorder_Variance : 0.05866677005312614 /Users/abhinandanbatra/Desktop/pyradiomics/Converted/Case2/ C07020110818Image.nii C07020110818label.nii

ValueError Traceback (most recent call last)

in () 24 # print(f) 25 # ---> 26 result = extractor.execute(image, mask) 27 print('Result type:', type(result)) # result is returned in a Python ordered dictionary) 28 print('') /anaconda3/lib/python3.7/site-packages/radiomics/featureextractor.py in execute(self, imageFilepath, maskFilepath, label, voxelBased) 391 # 1. Load the image and mask 392 featureVector = collections.OrderedDict() --> 393 image, mask = self.loadImage(imageFilepath, maskFilepath) 394 395 # 2. Check whether loaded mask contains a valid ROI for feature extraction and get bounding box /anaconda3/lib/python3.7/site-packages/radiomics/featureextractor.py in loadImage(self, ImageFilePath, MaskFilePath) 494 image = ImageFilePath 495 else: --> 496 raise ValueError('Error reading image Filepath or SimpleITK object') 497 498 if isinstance(MaskFilePath, six.string_types) and os.path.isfile(MaskFilePath): ValueError: Error reading image Filepath or SimpleITK object
abhinandanbatra commented 5 years ago

Also, can you please let me know the code for the corrected mask to set it to True. here is what I came up with based on command line and your examples `settings = {} print(settings) settings['binWidth'] = 25 settings['resampledPixelSpacing'] = None

settings['resampledPixelSpacing'] = [3, 3, 3] # This is an example for defining resampling (voxels with size 3x3x3mm)

settings['interpolator'] = 'sitkBSpline' settings['correctMask'] = True

interpolator = settings.get('interpolator') resampledPixelSpacing = settings.get('resampledPixelSpacing') extractor = featureextractor.RadiomicsFeaturesExtractor() extractor.disableAllFeatures() extractor.enableFeatureClassByName('firstorder') extractor.getFeatureNames('firstorder') print('Enabled features:\n\t', extractor._enabledFeatures)

print('Active features:') for cls, features in six.iteritems(extractor._enabledFeatures): if len(features) == 0: features = [f for f, deprecated in six.iteritems(extractor.getFeatureNames(cls)) if not deprecated] for f in features: print(f) `

Unfortunately, I still run into that issue maybe I am writing the code wrong :).

Sorry for bugging you so much

JoostJM commented 5 years ago

extractor = featureextractor.RadiomicsFeaturesExtractor() should be extractor = featureextractor.RadiomicsFeaturesExtractor(**settings)

I.e. you need to pass the settings in order for the extractor to use them. the ** means they are unpacked as keyword arguments (i.e. extractor = featureextractor.RadiomicsFeaturesExtractor(correctMask=True) would also work (only setting correct mask is applied here, rest is default of course)

what is printed out if you add print(type(image)) just before result = extractor.execute(image, mask)

JoostJM commented 5 years ago

This is the part of feature extractor that loads your image and is raising an error in your case:

    if isinstance(ImageFilePath, six.string_types) and os.path.isfile(ImageFilePath):
      image = sitk.ReadImage(ImageFilePath)
    elif isinstance(ImageFilePath, sitk.SimpleITK.Image):
      image = ImageFilePath
    else:
      raise ValueError('Error reading image Filepath or SimpleITK object')
abhinandanbatra commented 5 years ago

Thank you so much. I really appreciate it. It's working now. One of the .nii files was corrupt (as it did not convert properly from dicom to nifti) so it was creating an issue. Also, can you suggest any tool for converting dicom to nifti.

I am really thankful for all your help on this

JoostJM commented 5 years ago

@abhinandanbatra I myself use a python script I put together. Aside from that, checkout this issue: #383.