Slicer Version: 5-0.2
I am trying to develop a pipeline to measure the eye surface area of butterflies from the entire head segment. Segmenting only the eye surface is extremely difficult, so I am using the following steps. I was hoping I could get some feedback on the pipeline which I am going to adopt for my upcoming project.
- Imported tiff stacks and segmented the whole out and convert his segment to model (data module → right-click on the segment and export the visible segment to the model)
- Create a close curve from the ‘Markups module’ and place the points to demarcate the outline of the eye. I chose the following options in the curve settings tab (curve type= Spline, constrain to model= model I just created). Also, not sure why the curve in 2D slices looks out of place and if that is a problem.
- Chose a curve cut from the ‘Dynamic Modeller’ module. I will then find the surface area of this curve in the ‘Models’ module.
Do these steps make sense? There are a large number of scans that I need to process. Any input will be extremely helpful. Thank you very much in advance.
You workflow looks OK, but you can make it a bit simpler and more efficient.
- You don’t need to use “Markups to model” module. Instead, you can place a closed curve on the surface using Markups module.
- You can reduce the number of points that you need to add (from 30-40 to 5-15) if you constrain the curve to the model surface, using Markups module → Curve settings → Constrain to model.
- You can further reduce the number of points that you need to add (from 5-15 to 6-8) and make the placement more reproducible by snapping the curve to high-curvature area. You can do that by computing curvature on the model by copy-pasting the code snippet below into the Python console, and then in Markups module → Curve settings → Curve type, choose “Shortest distance on surface” and as cost function select “Inverse squared”. You can add a small constant to the active scalar to make the line a bit smoother (e.g.,
activeScalar + 0.02).
inputModeNode = getNode('Segment_1')
- If you need to process hundreds of data sets then you can further reduce the processing time by writing a few code snippets for automating trivial steps (sementation by thresholding, setting up the curve node, cutting the model using Dynamic modeler, computing surface area, add result to a csv file, save the scene). You can assign shortcuts to run these code snippets or add a module that has a simple GUI to select input and output data sets and parameters. To get started with Python scripting in Slicer you can check out the “SlicerProgramming” PerkLab bootcamp tutorial.
Thank you very much for your quick reply and input, this is extremely helpful. I have two quick questions:
(1) I ran the code you provided, and what I got is this new model (image below). I am a bit confused at this step. So should I first run the code and then place the curve on this model choosing the curve settings you mentioned? Or should I place the curve first? Also, when I place the curve and choose these settings (curve type → shortest distance on surface, constrain to model → this new model I just got after running the code, cost function → inverse squared), the outline of the curve spill over the demarcated boundary. Not sure why happens.
(2) Will the measurements differ if I use curve type → spline & curve type → shortest distance on the surface? However, I must admit that I am trying to understand what these different curve types actually mean and when one should use a particular curve type.
Thank you very much again.
This is normal, it is because the path search between control points is too much controlled by the (always noisy) curvature computation result. Changing
activeScalar + 0.02 (or another small constant) will dampen the effect of the curvature. You can also add more control points if the path found between the points is not exactly where you want it (you can add more points on the curve by Ctrl + Left-click).
The length is always the length of the curve that you see. If the curve looks good then the measurement is good. I noticed that you placed the control points far away from the edge of the eye, which is not good. You still need to place the control points accurately, by making the path search between control points prefer high-curvature areas we just reduce the number of control points that are needed to guide the curve.
Now it’s perfectly clear! I just ran the entire workflow again and just adding +0.02 to activeScalar makes the curve much smoother. I believe after processing scans, I’ll get a good idea of how many control points are adequate.
If I may just ask another question related to this topic (or I can create a new topic), how can I export the curve cut (which I obtained from Dynamic Modeller) as a high-resolution label? Now I simply created the label from the curve cut by right-clicking on this model (i.e. curve cut) and clicking on covert to segmentation label. However, it is clearly visible that the edges of the curve cut look quite jagged/pixelated. So I was wondering if there is a good way to export it as a high-resolution label (we may possibly use these labels as a training dataset for giving a shot at deep learning).
Thanks very much for your input! This has been really helpful.
in Data module right click on the cut model and choose convert to Segmentation. Then after the conversion completed you can right click on the segmentation object and choose to export as a label map.
Thanks for the reply! So I believe this is the only way to export the ‘cut model’ as a label map. The only slight issue is that when this label map is superimposed on the original scan, the edges look a bit jagged (example below). Can the edges of the label map be smoothened out perhaps by adding (resampling) more control points? I am trying to reduce this noise as much as possible.
you can try that. Also check the geometry of the labelmap. If it is not very high, you can try to use the segmentation module and control the resolution of the created segmentation during the model to segmentation conversion (as I recall).
I’ve been following your Japanese translation updates. Thanks for your fantastic work.
Thanks a lot for your
Surface mesh segmentation (what I described, using Dynamic modeler) and image segmentation are two entirely different problems. If you want to label the CT image then specifying the outer surface of the eye is mostly useless.
If you want to do image segmentation then I would recommend to use Segment Editor to generate ground truth and MONAILabel to train a network.
If you want to segment surface meshes the you can use the workflow discussed above and experiment with networks specifically designed for mesh processing.
Thanks very much for the inputs (and you and your colleagues for developing SlicerMorph)! I shall give this a try.
Thank you very much for your quick replies and input! I now have a good workflow to get the measurements out. This forum is just fantastic!