Linking lung nodules across CT scans using nodule centroid anatomical coordinates

This is a longer explanation of what Andras already said.

Download MRBrainTumor1 and 2 from sample data. Create a Fiducial (markup) on the edge of the left ventricle of the Tumor2. Note point is not on the edge of the ventricle in Tumor1 dataset.

image

Switch to General Registration(Brains) module. Set Tumor1 as fixed, Tumor2 as moving. Choose all registration options including bspline. Expand Expert Only Parameters and create an Output Transform. This helps you concatenate the linear and non-linear portions of the transformation into a single transform that you can apply to markups and volumes.

When you execute this flow, Tumor2 will be put under the Output Transform but not the point. If you add the F to the output transform you can now see it lining up on the edge of the ventricle for both Tumor1 and Tumor2.

I am sure you can do all this with Elastix as well, I am not very familiar with it.

1 Like

You can transform volumes, markup fiducial points, segmentations, models, etc. the same way, using Transforms module (see documentation and/or demo video at Documentation/Nightly/Modules/Transforms - Slicer Wiki) or using Data module (double-click on the transform column in the row of the node that you would like to transform).

Once you have the aligned segmentations, you can use “Segment editor” module’s “Logical operators” effect to compute intersection of each segment pairs and then use “Segment statistics” module to get the volumes of the intersecting regions. This is of course very tedious to do manually (using the GUI), so if you confirmed that everything works well then it is worth writing a short Python script to automate it. There are many code snippets in the script repository that you can combine/modify to get what you need.

1 Like

Thanks @muratmaga and @lassoan. This is very helpful.

I think I am very close to getting my test example to work using the GUI. I am stuck on getting the intersection of the transformed binary mask and the baseline/fixed mask. I used the Elastix extension to get the transformation of the follow-up scan to the baseline geometry. Then I applied the transformation to the binary mask in the Data module as you described. Upon visual inspection, I can now see the two masks are closely aligned.

Using the Segment Editor I then Create new Segmentation. Then using the Master volume: option I Add both of the masks. I then selected Logical operators and the intersect operation. In the upper box I selected one mask, and in the lower box (Intersect with segmet:) I selected the second mask and hit apply. However, nothing seems to happen, and Segment Statistics produces an empty table for the resulting segmentation. Am I using the logical operators wrong? It feels like it.

You need to harden the transform on the segmentation to apply the transform permanently (even if you copy/move segments to another segmentation).

When you apply intersection operation do you see that the non-overlapping part of the segment disappears?

So I was eventually able to get it working. I had to harden the transform, as you mentioned. The other issue was that I was loading in the binary label maps (.nrrd files) as volumes (and selecting the LabelMap option). Once I changed the Description to Segmentation, I was able to intersect the two nodules and use the Segment Statistics module to generate volume statistics.

I don’t think I’m entirely clear on whether I should load binary label maps as Volumes with the LabelMap or whether they should be loaded as Segmentations.

The overlap fraction (intersection volume / min(nodule1_volume, nodule2_volume) * 100) was rather low (~8%). This, of course, could be the result of two segmentation masks that do not overlap much, but could some of this be attributable to poor CT registration?

Awesome.

If you use Segment Editor module to process these images then it makes sense to load them as segmentation nodes directly.

You can fade between the registered CT volumes to verify how well they are aligned.

If you find that overlap volume is not a sensitive enough metric then you can also compute average distance from each segment. To do this, export one segment to a labelmap and use Simple Filters module to generate distance map image. Then use Seent statistics module with the distance map as input scalar volume to get average distance. You may also use Segment Comparison module to compute overlap and diatance between segments.

1 Like

So I think I was able to follow your instructions to compute the average distance metric.

  1. I imported one binary label map as a label map; and imported one binary label map as a segmentation node (I hardened the transform to this segmentation).

  2. I used the Simple Filters module and chose the filter ApproximateSignedDistanceMapImageFilter and selected the label map as the input volume. I created a new output volume for this distance map.

  3. I then used the Segment Statistics module with the segmentation node as the Segmentation, and the distance map as the Scalar volume. I was able to produce a table with several statistics including a Mean of 1.74.

I am not entirely sure on how to interpret this number in absolute terms, but I have to think smaller numbers indicate shorter distances, and vice versa.

  1. Lastly, I went back a re-imported the first binary label map as a segmentation node directly. I then used the Segment Comparison module to compare the nodes. I obtained some Hausdorff distance metrics (Max: 3.6, Min: 1.9, 95% 3.1) and Dice similarity metrics (e.g. Coef: 0.067, Ref center: (126,-38,-167), Compare center: (127,-35,-167), Ref volume: 0.06, Compare volume: 0.05).

I suppose I could use any number of metrics to make a determination on whether they are the “same” nodule.

Sorry for the long write-up. I am partly documenting my steps for my own sake, but also in case this is useful for anyone who stumbles upon this thread in the future.

2 Likes