Cross Section Analysis Freezing

Hi all.

We automatizated the cross section analysis computation for our own centerlines.

The program runs like this:

def obtainDataFromGeometry(segmentationNode, segmentID, branchID, G, centerlinesDict, tableNode, 
                 fileToSaveResults, createFile):

    """
    Runs CrossSectionAnalysis and genrates a file with
    - Centerline ID
    - Centerline branch ID
    - Node ID
    - Distance
    - Diameter
    - Area
    - Curvature
    - RAS coordinates 
    
    :segmentationNode: segmentation node where the geometry is
    :segmentID: the segment name we want to operate with
    :branchID: indicates the coronary branch ('left' or 'right')
    :centerlinesDict: a dictionary with key "centerline name" and value list of 
            nodeIDs of the centerline
    :tableNode: 'vtkMRMLTableNode' to save auxiliary results
    :fileToSaveResults: path to file where to save results
    :createFile: if True, generates a new file to save resutls. If False, appends results to file
    
    :return: creates a file in fileToSaveResults with the metadata
    """
    
    logic = CrossSectionAnalysis.CrossSectionAnalysisLogic()
    
    segmentNodeID = segmentationNode.GetSegmentation().GetSegmentIdBySegmentName(segmentID)
    
    # Set lumen surface
    logic.setLumenSurface(segmentationNode, segmentNodeID)
    
    # Set table node
    logic.setOutputTableNode(tableNode)
    
    # To obtain RAS coordinates in different cols
    logic.coordinateSystemColumnSingle = False
    
    # Set centerline node
    centerlineNames = list(centerlinesDict.keys()) # utils.getCurveNames(centerlinesPrefix)
    
    if(createFile): # We create a new file
        with open(fileToSaveResults, "w") as file:
            file.write("CenterlineID,branchID,nodeID,Distance,Diameter(CE),Cross-sectionArea,Curvature,R,A,S\n")
    
    
    for centerlineID in centerlineNames:
        print("Extracting metadata info from " + str(centerlineID) + "...")
        
        # Find nodes 
        # numberOfValues = tableNode.GetTable().GetColumn(0).GetNumberOfValues()
        numberOfValues = len(centerlinesDict[centerlineID])
        
        # If the curve has less than 3 points, cross section analysis crashes
        doCrossSection = False
        if(numberOfValues > 2):
            doCrossSection = True
        
        if (doCrossSection):
            # avoids interpolating points
            slicer.util.getNode(centerlineID).SetNumberOfPointsPerInterpolatingSegment(1) 
            
            logic.setInputCenterlineNode(slicer.util.getNode(centerlineID))
            
            # Run CrossSectionAnalysis
            logic.run()

The issue we see few times is the following one:

  • If the scene is created and saved and we use this routine in a new Slicer window where we open the saved scene, the function sometimes freezes extracting radius and other parameters. The function blocks the interface:

  • This problem is not seemed yet when we run the the function in the same Slicer window, but we do not rule out the possibility of happen here too.

Thanks a lot for the help :slight_smile: .

Can you share an MRB file so as to reproduce and debug this over the weekend? Only reproducible execution pathways can be solved. What Slicer version are you using? Which OS?

Hi @chir.set, what is a MRB File? We usually work with mrml filesā€¦ We are using 3D Slicer 5.6.2 version, in Windows 11. I would try to send you the scene without the images and then I will try to reproduce the error. If the error occurs in the scene without images I can send you whatever you need.

Thanks :slight_smile:

You can select an MRB file in the ā€˜Save dialogā€™, itā€™s a ZIP archive that includes anything in your scene that can be saved.

If you mean the volume node, it can be omitted. The segmentation and the centerlines must be included however. Your code is already online.

Okay, how can I share you this file?

Pd: We tried using the function without saving and opening the scene and it also freezesā€¦

You can use any online file sharing services like Yandex disk, Google driveā€¦

Okay, I think this should work:

How is centerlinesDict structured? What is pointed by centerlinesDict[centerlineID]?

centerlinesDict is a dictionary which contains the centerline names as keys, and the list of nodes as values for each centerline.

numberOfValues = len(centerlinesDict[centerlineID]) is the number of nodes of the selected centerline.

This code just works: first time, repeat run in same scene, in recreated scenes, after closing and restarting Slicer, i.e, always.

i

import CrossSectionAnalysis
logic = CrossSectionAnalysis.CrossSectionAnalysisLogic()

segmentation = getNode("Segmentacion")
segmentId = segmentation.GetSegmentation().GetSegmentIdBySegmentName("Paciente")
centerlines = slicer.util.getNodesByClass("vtkMRMLMarkupsCurveNode")

logic.setLumenSurface(segmentation, segmentId)
logic.coordinateSystemColumnSingle = False
# table = slicer.mrmlScene.AddNewNodeByClass("vtkMRMLTableNode")
table = getNode("Table_4")
logic.setOutputTableNode(table)

for centerline in centerlines:
    centerline.SetNumberOfPointsPerInterpolatingSegment(1)
    logic.setInputCenterlineNode(centerline)
    logic.run()

Looking at your code, there seem to be confusion between ā€˜number of pointsā€™ and ā€˜number of centerlinesā€™:

numberOfValues = len(centerlinesDict[centerlineID])
...
# If the curve has less than 3 points, cross section analysis crashes
...
    if(numberOfValues > 2):

because you precise:

centerlinesDict is a dictionary which contains the centerline names as keys, and the list of nodes as values for each centerline

This is confusing, it suggests that one key references a collection of centerline curves. Is it a simple

dict[ā€œcenterlineNameā€] = centerlineNode

mapping?

Regardless, if

slicer.util.getNode(centerlineID)

returns a curve node, it should not hang.

You seem to be getting the curves using a custom function in your project:

# utils.getCurveNames(centerlinesPrefix)

Check whether all nodes in centerlinesDict for a given key is a curve; for example:

print(centerlineID, slicer.util.getNode(centerlineID).GetClassName())

It does not hang on my system (Linux) in any circumstances. In general, itā€™s user code, or some particulars of your system.

2 Likes

Hi @chir.set we have been testing the functions (your version and also our version). The behaviour seemed was:

Ā· Both functions works when we execute it alone in Pythonā€™s terminal in Slicer.

Ā· None of them works when we execute it in the whole routine. This routine contains the following steps:

With a button driven interface we generate the centerlines, smooth them and let the user modify it (with the same name per each centerline node). Then the centerlines are removed and re-generated using the modified points. Once this process is done, we call the function you provide and Slicer hangs.

This routine sometimes works and sometimes hangs depending on the geometry and the centerlines. We also see that the message ā€˜Waiting for background jobsā€¦ā€™ is related with taking the geometry.

Do you spot any possible mistake here?

Thanks :slight_smile:

Thatā€™s hard to say without seeing the full routine that you canā€™t obviously disclose (no problem with this).

Since your code works in the Python console too, and not in your routine, the problem remains in the way your code is using CrossSectionAnalysis.

You are repurposing the centerline curves, namely smoothing the curve and recreating points. The initial points are associated with scalar arrays coming right from VMTK and ā€˜Extract centerlineā€™. If you do not end up with the same number of points and/or if their coordinates change (they will change), the scalar arrays will be out-of-sync with the curve. Your curves miss the important ā€˜Radiusā€™ array for example. They are being used as arbitrary curves, which is perfectly legitimate too.

Have the curves in your sample file online been repurposed already?

The perpetual ā€˜Waiting for background jobsā€¦ā€™ message indicates that execution instance is still in the C++ pathway, probably in an infinite loop (and probably with a high CPU load). It contains 2 ā€˜forā€™ loops that you could debug so as to understand further. Please note that the scalar arrays of the centerline curves are not used by this C++ code.

I just noticed that your curves in the sample online file are of ā€˜Linearā€™ type. The curve is processed by vtkParallelTransportFrame in the C++ pathway to generate tangents along a spline, which is the default type for a markups curve. I donā€™t know how much this processing may be related with the hangs you are observing. Try with curves of ā€˜Splineā€™ type.

Hi @chir.set , by parts:

Ā· We didnā€™t notice high CPU usage (by the way is very low)
Ā· Indeed, our centerlines in the online example were modified and processed.
Ā· We donā€™t use extract centerlines, so we create in each iteration the centerlines from arrays.
Ā· Our centerlines are Linear type, yes, and we use the command: curve_node.SetCurveTypeToLinear(). We donā€™t know how to create centerlines with ā€˜Splineā€™, cause is not an option in SetCurveType functions.

We thank a lot your help.

We were exploring and reading the problematic function vtkCrossSectionCompute::UpdateTable in vtkCrossSectionCompute.cxx and the only possibility we manage now is that the thread is never free to be selected, so the module waits indef for it or problems in synchronization between the main thread and the workers threads.

This could be weird because the only execution the big routine do different with execute directly the function in the terminal is keep in the slicer interface a qt dockInterfaceWidget (no subprocess are running during CSAnalysis) and the usage of CPU is quite low (less than 10% before start the CSAnalysis). The memory remains low too, around the 50% of the total memory available is used.

At this point we are concern with the problem and donā€™t know what to do.

I donā€™t really understand what it means. Joinable threads are spawn, the main thread will wait for their completion. Each thread will stop when thereā€™s nothing more to process. The main thread cannot ā€˜selectā€™ a running thread to release/free it AFAIU.

Me too, me tooā€¦ All I can say is that itā€™s an out-of-intent/design usage. If you can find a solution that can satisfy your specific requirements without breaking anything, it can be further considered.

1 Like

We saw a completely arbitrary behavior in the well procedure or not in this function:

If we make some minimal changes in the geometry, like minimal smoothings or cut a little bit ends of some vessels, it looks to work again sometimesā€¦ A completely no senseā€¦

Thank for your help in this process.

PD: could you tell us how to make our centerlines ā€˜Splineā€™?

Since your centerlines are created outside of Slicer, I canā€™t really say how to draw them as spline.

In Slicer, the spline is the default curve type. As you know the coordinates of your centerline points, you just have to add points to a newly created curve:

mycurve = slicer.mrmlScene.AddNewNodeByClass("vtkMRMLMarkupsCurveNode")
mycurve.AddControlPointWorld(0.0, 0.0, 0.0)
1 Like

Hi, today I want to try again solve this topic.

I discovered something interesting: When CSA canā€™t intersect with the segment the module falls in a continuous ā€˜Waiting for background jobsā€™ and crash Slicer.

To avoid this I guess that check all the points are inside the segment before execute the CSA would be a solution, but the module shouldnā€™t crash for it also.

If anyone has more ideas to avoid crashes or some Slicer/VMTK developer reads this, it would be great if they share any opinions or solutions.

Thanks a lot :smiley:

Hi @chir.set , thanks for the quick answer here .

The version is 3D Slicer 5.6.2 running on Window 11

The logs displayed in the slicers prompt I canā€™t share it because CSA blocks the interface.

The toy scene to show the questions and the problematic behaviour (for sure produced by a problematic input) is here: Scene.

The ā€˜badā€™ segment causes ā€˜Waiting for background jobsā€™ message, meanwhile the ā€˜goodā€™ segment donā€™t. The ā€˜goodā€™ segment takes sections in the lower zone instead.

In my complete project I use segments extracted from a bigger segment and their corresponding centerlines, but if some trash appears in the segmentation the centerlines fails and can produce curves as shown in the mrb file.

Now I see clear that one possibility to freeze the module comes from the fact that I gave some bad inputs as could be in the example sceneā€¦

Are other problematic inputs (as could be give a segment that not intersects with the centerline curve) documented?

Thanks again, we think we are close to the optimal solution :slight_smile: