SlicerRt / SlicerRT

Open-source toolkit for radiation therapy research, an extension of 3D Slicer. Features include DICOM-RT import/export, dose volume histogram, dose accumulation, external beam planning (TPS), structure comparison and morphology, isodose line/surface generation, etc.
https://slicerrt.org
127 stars 60 forks source link

RT Structure Set conversions to binary label maps - SlicerRT vs plastimatch #105

Closed mattwarkentin closed 3 years ago

mattwarkentin commented 5 years ago

Hi,

I am trying to batch convert many RT Structure Sets to binary label maps (NRRD format). Using the Slicer GUI and the SlicerRT extension, I seem to be able to load in the RTSS and then using the Segmentations module in Slicer I can export to a label map and also specify a reference volume (DICOM series) so that the label map has the same geometry as the original CT image.

After saving the mask, when I clear the scene in Slicer and then load the NRRD mask back in, I can verify in the Volumes module that the dimensions (512 512 275), spacing (0.78 0.78 1.00), and origin line up with the reference volume. This is great.

However, doing this interactively is not very efficient for how many conversions I need to perform. I tried to use plastimatch from the command-line so that I could eventually wrap it into a shell script for batch conversions. Here is how I have been using plastimatch...

plastimatch convert --input structure.dcm --output-labelmap mask.nrrd --referenced-ct /path/to/dicom/series

However, the resulting binary mask does NOT seem to have the geometry of the reference volume. Here is the plastimatch output to the shell...

Loading RDD Loading RDD complete Found RTSTUCT, UID=1.3.12.2.1107.5.8.15.130095.30000019050309482349500000100 Found DCM_ReferencedFrameOfReferenceSequence! Found DCM_RTReferencedStudySequence! Trying to load rt structure set. Adding structure (1), L1GTV1 Structure 1 has color 245\60\25 PIH is: Origin = -65.9414 121.3711 -528.8000 Size = 512 512 18 Spacing = 0.0401 0.0401 1.0000 Direction = 1.0000 0.0000 0.0000 0.0000 1.0000 0.0000 0.0000 0.0000 1.0000 Rt_study_warp: Convert ss_img to cxt. Rt_study_warp: Apply dicom_dir. Rt_study_warp: Set geometry from PIH. Rt_study_warp: Set rasterization geometry. rast_dim = 512 512 18 rast_offset = -65.9414 121.371 -528.8 rast_spacing = 0.0400543 0.0400543 1 Rt_study_warp: warp and save ss. Warp_and_save_ss: seg->rasterize Rasterizing... Converting... Finished rasterization. Warp_and_save_ss: save_ss_img save_ss_img: save_labelmap Trying to write image to mask.nrrd Finished!

As with before, when I load this NRRD mask (mask.nrrd) into Slicer, the Volumes module shows that the dimensions (512 512 18) and spacing (0.04 0.04 1.00) are not the same as the reference image. Any idea why there is this discrepancy between the Slicer conversion and the plastimatch conversion? Am I doing something wrong?

Thanks for your help.

cpinter commented 5 years ago

Have you tried this? https://github.com/SlicerRt/SlicerRT/tree/master/BatchProcessing We developed this just for this purpose.

mattwarkentin commented 5 years ago

@cpinter Thanks for sharing. I will give this a shot and report back.

Still interested in why plastimatch isn't doing what I was hoping. I was under the impression plastimatch was part of the SlicerRT extension, so I expected to achieve equivalent results.

cpinter commented 5 years ago

SlicerRT relies on Plastimatch but it is its own library: http://plastimatch.org/ https://gitlab.com/plastimatch/plastimatch/issues @gregsharp

mattwarkentin commented 5 years ago

@cpinter Thanks. I did post this issue over on gitlab, but it didn't appear to be too active, so I decided to cross-post it over here as I thought the issue overlapped both libraries.

I am going to try your batch conversion script shortly and I can report back with my experiences.

mattwarkentin commented 5 years ago

@cpinter The _readme.txt file indicates that the CT image needs to located in the input folder alongside the RTSS. What format can the reference volume be in? Will a directory containing a DICOM series be a valid reference volume?

cpinter commented 5 years ago

Yes, I believe that it actually expects DICOM

mattwarkentin commented 5 years ago

@cpinter So I am not sure if I have been able to get the batch conversion script working entirely. Slicer seems to error-out without any informative errors so it isn't obvious what the issue is, if there is one.

I have a directory called input which contains the RTSS file and also a directory which recursively leads to a DICOM series. I have another empty directory called output for returning the NRRD mask.

The code looks as follows:

C:/'Program Files'/'Slicer 4.10.1'/Slicer.exe --no-main-window --python-script path/to/BatchStructureSetConversion.py --input-folder /path/to/inputs --output-folder /path/to/outputs

A few of the Slicer progress bars pop up and disappear before Slicer produces this error:

error: [C:/Program Files/Slicer 4.10.1/bin/SlicerApp-real.exe] exit abnormally - Report the problem.

A .nrrd file is in the output folder and I can load it into Slicer GUI and it actually looks mostly correct. The Volumes module has the expected spacing, however, the dimensions of the mask [27 27 21] are not the same as the reference volume supplied [512 512 275]. Is this expected? Should the dimensions be the same as the reference DICOM series?

EDIT: Using Slicer, I was also able to properly superimpose the label map (.nrrd) output from the batch conversion python script onto the DICOM series. It looks fine.

cpinter commented 5 years ago

It is possible that the CT is not used as reference volume. Such an option wouldn't be hard to add. I'll look into it when I have time.

Can you check the log file for some clue about the crash? If you start Slicer and go to About / Report an error, then you can find the 10 last logs.

mattwarkentin commented 5 years ago

@cpinter Sorry for taking so long to return to this issue. Here is a print-out of the error log from Slicer:

image

Same as before, the label map has the expected spacing, but the dimensions are not the same as the input. I'm not sure if the dimensions should be the same or not. I guess it depends on if the script uses the CT as a reference volume in that way.

What should be contained in the input directory? Right now I have the RTSS file, and a directory containing the CT series. Seems like the CT series is being ignored.

cpinter commented 5 years ago

The screenshot shows a fresh Slicer log just after starting. I'd need the one from the time it crashed. If you start Slicer and go to About / Report an error, then you can find the 10 last logs. Thanks!

gregsharp commented 5 years ago

@cpinter Matt and I figured out the problem with plastimatch, which is that it does not search recursive subdirectories for the reference CT. But SlicerRT should not have that problem, since the CT is loaded by Slicer.

Regarding the labelmap size. isn't the issue that the created labelmap is minimally sized to contain the structure, and padding zeros are not included?

mattwarkentin commented 5 years ago

Regarding the labelmap size. isn't the issue that the created labelmap is minimally sized to contain the structure, and padding zeros are not included?

@gregsharp I agree, this seems like the most logical reason for the difference in dimensions. If it is expected that the RTSS is converted to a labelmap with its size as a minimal bounding box containing the ROI, then perhaps the script is working as anticipated.

Notes:

  • The CT (or other anatomical) volume of the study needs to be present in the input folder so that the converter can use it as a reference.

However, it is this note (shown above) that was contained in the _readme.txt file for the BatchStructureSetConversion.py that led me to believe the CT volume was used as a reference for the output geometry. Perhaps the CT image is used as a reference for a different reason.

cpinter commented 5 years ago

SlicerRT should not have that problem, since the CT is loaded by Slicer

Yes, but I believe the reference image is simply not considered in SlicerRT and 1x1x1mm^3 resolution is used. However I don't have time to work on this right now.

Regarding the labelmap size. isn't the issue that the created labelmap is minimally sized to contain the structure, and padding zeros are not included?

Same thing, would need to use the reference.

Sorry guys I don't even have time to check the code now. I have an emergency task for today and PhD defense on Monday. Please remind me about this a bit later.

mattwarkentin commented 5 years ago

@cpinter Do not worry about this issue. There is no rush. I am in the process of moving from Toronto to Boston, so I can't spend much time on this at the moment either. Lets come back to this at a later date.

We can circle back after your defense. Good luck.

mattwarkentin commented 5 years ago

Hi @cpinter. I hope your PhD defense went well, I'm sure it did. In case you do decide to circle back to this issue, here is a brief summary of our conversation:

So potential updates/features might be:

cpinter commented 5 years ago

It went fine, thanks for asking :)

Since we have the same option in Slicer, I'd lean towards the first option, i.e. adding an argument to use the found anatomical image as the reference for the nrrd conversion. Would it be satisfactory?

mattwarkentin commented 5 years ago

I think that sounds like a great idea. Though, I think combining both suggested features could be useful for those who might want to use a common reference CT for many RTSS conversions (probably less common), or simply in situations where the reference CT and the RTSS are located in separate directories (probably more common).

Selfishly, I prefer to avoid moving files into a common directory to use a script. But I haven't looked into the guts of the script, so I don't really have a sense for the amount of extra work this would take.

I'm imaging something like...

Slicer --no-main-window --python-script path/to/BatchStructureSetConversion.py \
     --input-folder /path/to/rtss \
     --ref-dicom-dir path/to/dicom \
     --output-folder /path/to/output \
     --force-geom-match
cpinter commented 5 years ago

I added these two arguments, see https://github.com/SlicerRt/SlicerRT/commit/2078c7a8b9ac342e888021a27f8125b91c15e0e7

The argument names are slightly different from what you suggested, please see readme file.

Let me know how it works for you.

cpinter commented 5 years ago

Also can you please briefly describe what do you use SlicerRT for, and which institution you are from? We just entered in the maintenance period of the CANARIE SlicerRT grant, which is just about helping new groups adopt the software, and for reporting purposes if would help a lot. Thanks :)

cpinter commented 5 years ago

@mattwarkentin Could you please aknowledge that you received notification about the improvement I implemented for you last week?

mattwarkentin commented 5 years ago

@cpinter Sorry for the slow response time. I have been travelling with very limited internet access. I confirm that I have received this notification. Thank you very much for this enhancement; it is very much appreciated. I will formally test it out when I am back in the office and will report back here with my experiences. Here is a write-up about my usage of the SlicerRT extension and also the BatchStructureSetConversion.py script that I hope is helpful for your grant reporting:

I am a PhD candidate in Molecular Epidemiology at University of Toronto and a Doctoral Fellow/Trainee at the Lunenfeld-Tanenbaum Research Institute at Mount Sinai Hospital. My PhD supervisor is a co-PI for a grant whereby the goal of one of the sub-projects is to integrate the lung screening data for several large international lung screening trials. In my role, I am working to develop the pipeline and tools needed for each site to independently extract and compile engineered features (i.e. radiomics) for all of the nodules identified in their lung screening CTs. This will be perhaps the largest and most diverse set of lung nodule data/features ever assembled, to date. Since we are collaborating with several international sites, some sites export their nodule contours from their CT vendor software as DICOM RTSS format. For our purposes, we convert the structures (contoured nodules) contained therein into binary label maps (NRRD format), so that we may then perform batch feature extraction. For the RTSS>NRRD conversion, I have tested your original script on a small subset of images/nodules, and will additionally test your newly updated script for this purpose.

I have also used the SlicerRT extension functionality many times from within the Slicer GUI to import/view RTSS files, and to convert RTSS to NRRD interactively (i.e. one at-a-time); but primarily I use the script/extension for en masse file conversions.

I hope this is helpful. Please let me know if I can provide any other useful information. Thanks again for all of your help.

cpinter commented 5 years ago

Thank you very much for taking the time to describe your work! Looking forward to hear about how the new options work for you.

liyongwu-github commented 3 years ago

Hi ,I had a experiment that I convert the RTstructure(2.dcm) into mask_ROIs(.nii.gz) respectively with the result as following figure on April 6 by using Platimatch convert commandline accidently,now I want to do it again,but I can't ,and I can't remember how I did ,I am being lost in it for some days,I ask for help on it. the name of files of all *.nii.gz is automatically finished

1

mattwarkentin commented 3 years ago

Is this a supposed issue with plastimatch or SlicerRT? If the former, you may wish to file an issue over at its repository here: https://gitlab.com/plastimatch/plastimatch/issues

From the plastimatch manual page (https://plastimatch.org/plastimatch.html), this may be helpful:

This next example shows how to convert a DICOM RT structure set file into an image using the –output-ss-img option. Because structures in DICOM RT are polylines, they are rasterized to create the image. The voxels of the output image are 32-bit integers, where the i^th bit of each integer has value one if the voxel lies with in the corresponding structure, and value zero if the voxel lies outside the structure. The structure names are stored in separate file using the –output-ss-list option.

plastimatch convert \
  --input structures.dcm \
  --output-ss-img outfile.nrrd \
  --output-ss-list outfile.txt
liyongwu-github commented 3 years ago

@mattwarkentin ,thanks,I filed an issue over at [https://gitlab.com/plastimatch/plastimatch/issues] as your suggest. I still is waiting for help

mattwarkentin commented 3 years ago

@liyongwu-github You're more likely to get a quick and helpful response if you are able to provide a minimal sample of data and code that reproduces your issue - especially code that can be copy and pasted. Generally, screenshots are of little help in most troubleshooting cases.

If you can't share the data for someone to try to reproduce your issue, then providing at least the code that produces the error is the next best thing.

liyongwu-github commented 3 years ago

@mattwarkentin,thanks,images is in a directory with 88 DICOM files now zipped into 3 files(*.zip, available for uploading) as attached and The RTstructrue file zipped as 2.zip is attached too . but I can't remember the code(a commandline in plastimatch) that I did,it a pity that only I can recall it with having 3 parameters in commandline.sorry img1.zip img2.zip img3.zip 2.zip

mattwarkentin commented 3 years ago

This command gets you most of the way there. This should serve as a good starting point.

plastimatch convert \
    --input 2.dcm \
    --output-prefix "masks" \
    --prefix-format "nii.gz" \
    --output-ss-img "image.nii.gz"

In general, you should try to include the code you have tried, even if it does not work. It is much easier to offer help with a starting point.

liyongwu-github commented 3 years ago

@mattwarkentin ,you are a kind person, thanks.