BUG: Fix RTSS contour-to-surface conversion with volume-based approach - #304
BUG: Fix RTSS contour-to-surface conversion with volume-based approach#304melandur wants to merge 1 commit into
Conversation
Add vtkPlanarContourToClosedSurfaceByRasterizationConversionRule as an alternative to the existing triangulation-based conversion. This rule rasterizes each contour into a 2D binary slice, builds a 3D volume with per-group nearest-neighbor fill, applies a Gaussian low-pass filter (additive-only to preserve small contours), and extracts a closed surface using marching cubes. The conversion cost is set to 1000 (higher than the existing rule's 700) so the triangulation approach remains the default. The rasterization approach can be useful for complex or highly variable contour data where direct triangulation produces artifacts. Co-authored-by: Cursor <cursoragent@cursor.com>
d1bb423 to
b1c4b8c
Compare
|
After a very quick review I have the following comments:
Thank you very much! |
|
Yes, please show screenshots of how it converts SlicerRT sample data sets and some other data sets. Highlight differences (good and bad) compared to the current method. Thank you! |
|
How do you decide which contours to connect? |
|
How do you handle internal holes? Do you support:
|
|
How do you decide which contours to connect? The rasterization does not explicitly connect contours between slices (like the standard), instead it uses a volume-based approach to determine connectivity:
Per-pixel refinement: Even within a connected pair, individual pixels that don't overlap with the adjacent group's fill only get 1-voxel extension. This prevents parts of a contour that are spatially isolated from stretching, even if other parts of the same slice are connected. The surface itself emerges implicitly from marching cubes on the resulting 3D binary volume — there's no explicit contour-to-contour matching or triangulation. How do you handle internal holes? Do you support: keyhole technique (classic) odd/even count of nested contours cw/ccw polygon winding? |
"A hole is created regardless of whether the inner contour is CW or CCW relative to the outer contour" means that it is actually not supported. CW/CCW is more robust to numerical errors than odd/even counting. Numerical errors are not theoretical but very frequently occur: vtkContourTriangulator does just odd/even counting and in all complex segmentations (for example, those that are created by thresholding a volume) there are dozens of errors in every segmentations. In practice, both techniques are used, but as far as I remember, the DICOM standard only allows odd/even counting. It is OK to have limitations, but they need to be clearly documented.
It means it does not follow the DICOM standard then. Again, it is OK to have limitations, but they need to be clearly documented. DICOM standard describes contour groups, so in theory you can encode in RTSTRUCT which contours needs to be connected to each other, but we have not implemented it because in the data sets that we got contour groups were not used. Do the data sets you have contain contour groups? What software created the contours that you are working with? It would be useful to document that for a certain software it is recommended to use the rasterization based conversion. Another general comment I have is to remove the closed surface generation step. The conversion you implemented converts from parallel contours to binary labelmap with reasonable sophistication. However the labelmap to closed surface conversion is very basic, has several issues (very inefficient, hardcoded Gaussian smoothing stdev, not optimal contouring filter), and if the segment is further edited then it is not nice that we need to convert back to binary labelmap again. It may be better to update your conversion rule to only include parallel contours to binary labelmap conversion and if you think your approach has some advantages compared to https://github.com/Slicer/Slicer/blob/main/Libs/vtkSegmentationCore/vtkBinaryLabelmapToClosedSurfaceConversionRule.cxx then add new alternative conversion rule for that, too. The only reason I can think of why a combine rule can be useful is if you have hundreds of overlapping structures then dropping the intermediate binary labelmap representation can save memory (are your data sets like that?); but for robust slice rendering you may need the binary labelmap representation anyway. As a very high-level comment, the effort we put into improving supporting parallel contours should be limited (focused on relieving specific pressure points instead of trying to implement general solutions), as segmentations today are generated by AI models and they are all labelmap based. Our efforts should be rather spent on improving support of DICOM SEG instead of the inherently limited, fragile, and complex DICOM RTSTRUCT. |
|
Thank you for the detailed and constructive feedback. The effort is appreciated.
You're right, I should not have framed the even-odd behavior as "fully supported."
Acknowledged. The rasterization approach does not use DICOM contour groups to determine inter-slice connectivity. Instead, it infers connectivity spatially via per-pixel XY overlap between adjacent Z-slices. This deviation from the standard needs to be documented
The contours are stored as flat sequences per ROI without any inter-contour connectivity information.
The contours were generated by PyRaDiSe, an open-source Python package for DICOM-RT-based auto-segmentation pipeline construction and DICOM-RT data conversion. The RTSS files are created from AI-generated segmentation masks. The generated RTSS files work correctly with treatment planning systems (TPS) and render properly in Weasis DICOM Viewer, which if I'm not mistaken, uses a pure slice-based 2D approach rather than 3D surface reconstruction.
That sound reasonable to me.
Fully agreed, I'm not a fan of RTSS as a format either. Unfortunately, RTSS remains widely used in clinical radiotherapy workflows, and clinicians do pull up SlicerRT to review structures. When the artifacts from the 3D visualization appears broken in the 2d view, it undermines confidence in the segmentation regardless of whether the underlying data is correct. This contribution is intended as a focused, narrow alternative for that current situation, not a general solution or replacement for the existing rule. |
|
I have not had time yet to fully process the details but only to see the screenshots. Please note, that these problems you see in the 2D views So other than these it is not obvious to me how the proposed algorithm is an improvement, as the closed surfaces in 3D generally look better to me (reality is never block-like, the interpolation that the current approach does makes the resulting closed surface more faithful to the clinical reality based on the limited planar contours representation of DICOM RTSS). |
|
Thanks a lot Michael for this PR!
I am very interested since it aligns very well with this question of mine https://discourse.slicer.org/t/vtkplanarcontourtobinarylabelmapconversionrule/47507/1 Reading through
plus
I can only share the observation that the frequency of appearance of these artefacts is indeed worrying when loading RTstructs: most people and radiotherapists only look at the 2D slices, not at the 3D view.
I guess there is a perspective conflict / compromise here. The current 3D viewing looks better in SlicerRT, whereas the 2D viewing looks better in this PR (SlicerRT has lots of artefacts). From my point of view, I prefer 2D without artefacts and pixelated blocky 3D surfaces rather than perfect 3D smoothy surfaces and artefact-full 2D contours. But I understand that depending on the intended audience one or the other might be beneficial. Maybe... a dropdown button in the DICOM import Dialog to use one or the other rule as default when importing could make everyone happy? |
|
I didn't get an answer about the artifacts, so they may or may not be the |





















Summary
vtkPlanarContourToClosedSurfaceConversionRule::Convertwith a nearest-neighbor approach combined with Gaussian low-pass filtering before marching cubes surface extractionmax) volume smoothing mergeChanges
Volume filling (Step 4)
Volume smoothing (Step 5)
max(smoothed, original)merge ensures the filter only adds material (fills concavities) and never erodes small contoursSurface extraction (Step 6)
Test plan
Made with Cursor