Use Open Curve Markup as input node in "Markups To Model" extension

Hi,

I was just working with the “Markups To Model” extension for the first time and I saw that it only accepts markup fiducial lists as an input. Is there a reason why it does not take markup curves (open or closed) as input? I know how to convert a markups open curve into a markup fiducial list programmatically (via arrayFromMarkupsCurvePoints()), but the name “Markups to Model” implies that this is already supported anyway, directly from the UI.

A bit of context why I’m doing this: I’m currently trying to find an easy way to accurately segment bone fractures, specifically skull fractures. The way I do it right now works, but is a bit cumbersome, with roughly the following steps:

  1. segment the skull via thresholding
  2. erode that by 1mm and export that as a model
  3. use “Probe Volume With Model” to get a textured mesh with ImageScalars (the fracture is nicely visible in these scalars)
  4. place an open-curve-markup with a few control points on the mesh surface
  5. constrain the curve path to lie on the skull surface model, and the path should follow a shortest-distance path on the model with an “Additive” cost function that is “distance + 10*ImageScalars” - Now, the open curve follows exactly the HU intensity dip along the fracture line.
  6. Here is where the above part is a bit of an inconvenience… I wanna import this path into the segmentation node… to do this I have to i) resample the path with a large number of fiducials, ii) convert the curve to a fiducials list, iii) use “Markups to Model” to convert this into a model, and iv) import the model into the segmentation.

Especially point 6 would get a bit simpler, if it was possible to directly convert the open curve into a model (without resampling and/or converting to a fiducial list).

Maybe this way to segment a fracture is way too complex. If anyone has an idea how to do it better, I’d be greatful for suggestions. I can also open a new topic for this… This topic is mainly for the “Markups to Model” feature request to accept “Open Curve Markups” as well.

Thanks you for the information, it is good to know that this processing flow works well.

You can resample a curve using Markups module (resample section).

I’m not sure what these steps are needed for. You can clip a surface along enclosing curve and planes using Dynamic modeler module:

Thanks Andras… the reason I need to do steps 6.i/ii/iii are because I want to convert the open curve itself into a model (a tube of certain diameter), then import that tube into the segment editor. I don’t see a quicker way how to accomplish that right now.
The Dynamic Modeler module can clip a surface along an open curve - but it cannot convert the curve itself into a tube model, right?

You can draw tubes directly in Segment Editor using “Draw tube” effect (provided by SegmentEditorExtraEffects extension). Right now you cannot enable constraining of the curve in this effect (or selection of a curve node), but this would not be hard to add. The effect is just a short Python script that you can easily modify, so it would be great if you could give it a go and make the changes that you need (e.g., add an input curve node selector). If you find that these modifications are useful then we could merge those back to the official version.

Hello mangotee,

I found this topic since I am having the same issue for a different application (for which I’ve just posted another topic: Create equidistant curves on a given surface).

For what I’ve read, it seems that you managed to convert an “Open Curve Markup” into a “Markup Fiducial List”, in order to use it in “Markups to Model”. Could you please share the code with me or give me some instructions about it? I would really appreciate it.

Thank you very much in advance,

Alessandro

Hi agv,
sure, no problem. Getting an array from the curve is easy, Slicer provides a function for this. Creating a fiducial markups node from this array needs to be coded. There may be a Slicer function for this too, but I’m not aware of any. I modified a function I created for this to fit your use case. Here’s the code, obviously you can modify the function to your needs (and obviously it’s not very clean, e.g. no asserts or anything) - hope it helps:

# Utility function to create a fiducial list from a Nx3 numpy array
def fiducialListFromArray(name_fidnode, arr, fidsPrefix=''):
    # Check if a fiducial markups node with the name "name_fidnode" already exists, if not, create it and add it to the scene
    try:
        n_fid = slicer.util.getNode(name_fidnode)
    except slicer.util.MRMLNodeNotFoundException:
        n_fid = slicer.vtkMRMLMarkupsFiducialNode()
        n_fid.SetName(name_fidnode)
        slicer.mrmlScene.AddNode(n_fid)
    nrfids = arr.shape[0]
    # if no fiducial names are given, 
    if fidsPrefix=='':
        sep = ''
    else:
        sep = '-'
    listFidNames = ['%s%s%d'%(fidsPrefix,sep,i+1) for i in range(nrfids)]
    for i in range(nrfids):
        n_fid.AddFiducial(arr[i,0], arr[i,1], arr[i,2], listFidNames[i])
    return n_fid

# n_curve is the node to the open curve markup
arr = arrayFromMarkupsControlPoints(n_curve)
# A node of name 'FiducialsFromCurve' is created if it doesn't exist already
# if it exists, the fiducials in arr are simply appended to the existing list
n_fid = fiducialListFromArray('FiducialsFromCurve', arr, fidsPrefix='F')
3 Likes

Hello again mangotee,

thank you very much for your explanation and the attached code! I tested it and it worked perfectly for what I was trying to do :slight_smile:

2 Likes

The method that converts numpy array to markups control points is called slicer.util.updateMarkupsControlPointsFromArray, so you should be able to convert curveNode to fiducialNode with something like this:

fiducialNode = slicer.mrmlScene.AddNewNodeByClass("vtkMRMLMarkupsFiducialNode")
updateMarkupsControlPointsFromArray(fiducial Node, arrayFromMarkupsControlPoints(curveNode))
1 Like

It works well and saves some lines of code, thank you Andras!