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 498 forks source link

How can i get the mask series from RTstructure? #423

Closed wangxs89 closed 4 years ago

wangxs89 commented 6 years ago

Hi, thanks for your work,first. i want to do some reaserch on radiomics,but i don't kown how to get mask files from RTstructures.Can you give me some suggestions?

fedorov commented 6 years ago

I would recommend trying plastimatch convert. If you prefer interactive tools, you can use 3D Slicer and SlicerRT extension, which includes support for importing DICOM RT as segmentations, which in turn you can pass as input mask to the Slicer Radiomics extension.

wangxs89 commented 6 years ago

Thank you! I’ll try.

sdoken commented 6 years ago

Hi @wangxs89, have you been able to use plastimatch to successfully create the files? I am struggling with the same task.

fedorov commented 6 years ago

@sdoken what exactly is the problem you experience? Did plastimatch work for you?

sdoken commented 6 years ago

Hi @fedorov the problem is that plastimatch is outputting nrrds with dimensions that do not make sense and cause pyradiomics to error out. For example, I have a series of 136 dicom images (ct-scans) and 1 RT-struct file that has segmentation information. When I use plastimatch convert to generate an nrrd for the 136 images, I get a 512x512x136 nrrd file for the images. This is expected as 512x512 are the dimensions of one slice and I have 136 of these. However, when I use plastimatch convert to generate an nrrd for the RT_struct file at hand, I get a 512x512x135 nrrd file for all of the masks when I would have expected a 512x512x136 again. And pyradiomics gives 'Image/mask datatype of size mismatch...' error and fails to calculate features. This problem happens with other image series I have. If I have a series of 239 images, then the nrrd corresponding to the mask is 512x512x238 etc. (It is always one less than needed in the 3rd dimension)

sdoken commented 6 years ago

Just posted more details here in the pyradiomics google group.

fedorov commented 6 years ago

You should first visually examine the mask and confirm it matches the structure in the image. If that is the case, you can use setting correctMask: True to resample it to the image matrix. See https://pyradiomics.readthedocs.io/en/latest/customization.html.

sdoken commented 6 years ago

I just checked visually and yes I do see that the mask in its original state RT_struct.dcm file matches the structure in the image correctly.

image

I am confused by your advice here. Could you explain where you are coming from? (I am a beginner) It seems to me that the problem is that I have 135 masks for 136 slices. (which means I am missing one mask, no?)

How is setting correctMask: True, as you say, somehow solving the dimensional mismatch or incorrect number of masks problem? Anyway, I wanted to take your suggestion and settings['correctMask'] = True for featureextractor.RadiomicsFeaturesExtractor(settings**) if that is what you meant. And yes now it does not error out but seems to be calculating which is taking it 10+ mins for one series of 136 images. Not sure if that is normal on a fast macbook pro.

sdoken commented 6 years ago

It now errors out again:

Image/Mask geometry mismatch, attempting to correct Mask FAILED: Traceback (most recent call last): File "/Users/semihcandoken/anaconda3/lib/python3.5/site-packages/radiomics/base.py", line 297, in _calculateFeatures yield True, feature, getattr(self, 'get%sFeatureValue' % feature)() File "/Users/semihcandoken/anaconda3/lib/python3.5/site-packages/radiomics/gldm.py", line 320, in getHighGrayLevelEmphasisFeatureValue hgle = numpy.sum(pg * (ivector ** 2)) / Nz ValueError: operands could not be broadcast together with shapes (4,) (99,)

FAILED: Traceback (most recent call last): File "/Users/semihcandoken/anaconda3/lib/python3.5/site-packages/radiomics/base.py", line 297, in _calculateFeatures yield True, feature, getattr(self, 'get%sFeatureValue' % feature)() File "/Users/semihcandoken/anaconda3/lib/python3.5/site-packages/radiomics/gldm.py", line 234, in getGrayLevelVarianceFeatureValue .....

fedorov commented 6 years ago

@sdoken were you able to resolve that issue?

I was working with plastimatch recently for another project, and I realized something (perhaps, obvious) that is probably related to the issue you observed. RTSS does not contain information about the geometry of the image being annotated. Both the pixel resolution and number of slices are defined using a heuristic! This is quite important, because even though the planar dimensions of the mask created by plastimatch could be 512x512, the pixel spacing can be (and was, in my case) completely different from that of the CT series.

Fortunately, it is quite easy to ensure the output of plastimatch has the same geometry as the annotated CT series. All you have to do is pass --reference-ct <path to directory with the CT series> to `plastimatch convert.

Can you try that and see if it solves your problem?

mattwarkentin commented 5 years ago

@fedorov Can I bring this issue back from the grave? I am also trying to use plastimatch to convert from a RTstructure set to a label map in either NIFTI or NRRD format. No preference.

In order to ensure the label map has the same geometry as the CT, I am passing a reference CT, as you describe but I still get curious results (at least, in my mind).

Here is my general coding approach:

plastimatch convert --input structure.dcm --output-ss-img image.nii --output-ss-list image.txt --referenced-ct /directory/to/dicomseries

From loading the reference DICOM series into slicer and inspecting the meta-data, I know the pixel spacing is 0.78125mm and 0.78125mm, and slice thickness is 1mm. However, the printed messages from plastimatch do not seem to align. See below.

image

I cut off the plastimatch convert call for data confidentiality reasons. But it is exactly as described above. Any idea why the spacing is in disagreement when converting from RTSS to NRRD or NII?

Furthermore, I also loaded the RTSS into Slicer and then exported a binary label map using the Segmentations module into NRRD. I then cleared the scene, loaded in the original DICOM series (not the RTSS) and loaded the NRRD mask in order to superimpose it on the CT, and when I enter the Editor module and select the DICOM series as the master volume and the NRRD mask as the merge volume, I get the following error:

image

Any thoughts on what's going on here?

For completeness, I am using Slicer 4.10.1, plastimatch 1.8.0, and I am on a Windows machine.

mattwarkentin commented 5 years ago

@fedorov Let me save some time by saying I think I was using the wrong output option with plastimatch. I changed to:

plastimatch convert --input structure.dcm --output-labelmap image.nii --referenced-ct /directory/to/dicomseries

However, the rest of the issues/questions still apply. The plastimatch log is essentially the same as above. Spacing still seems to change. Also, can't superimpose the plastimatch label map on the original DICOM series in Slicer; error below.

image

brianmanderson commented 4 years ago

A bit late to the game, but as one solution I converted the contour points by loading with pydicom, and convert them to sitk handles and binary masks

You can look at the code here https://github.com/brianmanderson/Dicom_RT_and_Images_to_Mask

or you can install it with pip install DicomRTTool from DicomRTTool import DicomReaderWriter Contour_Names = ['Liver'] # Or whatever your RT structure name is path = 'C:\users\brianmanderson\Patients\Patient_1\CT_1\' # Path contains dicom image and RT structure files Dicom_reader = DicomReaderWriter(get_images_mask=True, Contour_Names=Contour_Names)

Dicom_reader.Make_Contour_From_directory(path) image = DicomImage.ArrayDicom mask = DicomImage.mask

fedorov commented 4 years ago

Sorry, I completely missed the earlier communication.

@mattwarkentin were you able to figure it out? Is the dataset you are working with public? Sorry for the 1+ year delay in responding...

mattwarkentin commented 4 years ago

Honestly, I don't remember much about this issue except I believe it was eventually solved by using the solution described in SlicerRt/SlicerRt#105 (i.e. using BatchStructureSetConversion.py). Feel free to close this issue.

mattwarkentin commented 4 years ago

For posterity, here is why I was having the original issue with plastimatch (spoiler, I incorrectly assumed recursive searching for DICOM files): https://gitlab.com/plastimatch/plastimatch/-/issues/46.

fedorov commented 4 years ago

Thanks Matt! Good to know plastimatch is working, and that additional detail about the data layout.