Interactively adding control point with a given label

not getting what I require. What is the problem in the code. doesnt load models one by one when ever the new event occurs (new event is the adding of a fiducial for a each model)

def onMarkupsAdded(self, caller, event):
        index=0
        print(index)
        index+=1
        modelFileExt = "ply"
        print(index)
        modelDir = "/media/useradmin/Disk2/Slicer-5.3.0-2023-01-21-linux-amd64/Case1"
        modelFiles = list(f for f in os.listdir(modelDir) if f.endswith("."+modelFileExt))
        print(modelFiles)
        placeModePersistence = 0
        slicer.modules.markups.logic().StartPlaceMode(placeModePersistence) 
        modelNode = slicer.util.loadModel(modelDir + "/" + modelFiles[index])
def process(self, inputDir, inputVolume, outputVolume):
        """
        Run the processing algorithm.
        Can be used without GUI widget.
        :param inputVolume: volume to be thresholded
        :param outputVolume: thresholding result
        :param imageThreshold: values above/below this threshold will be set to 0
        :param invert: if True then values above the threshold will be set to 0, otherwise values below are set to 0
        :param showResult: show output volume in slice viewers
        """
        global index
        print(inputDir)
        if not inputVolume or not outputVolume:
            raise ValueError("Input or output volume is invalid")

        import time
        startTime = time.time()
        logging.info('Processing started')
        logging.info('Search for .ply files')
        modelDir = inputDir
        modelFileExt = "ply"
        
        import math
        import os
        
        modelFiles = list(f for f in os.listdir(modelDir) if f.endswith("."+modelFileExt))
        print(modelFiles)
        
        #load models and show in 3D view
        #for modelIndex, modelFile in enumerate(modelFiles):
        index = 0
        #name = os.path.basename(modelFiles)
        modelNode = slicer.util.loadModel(modelDir + "/" + modelFiles[index])
        placeModePersistence = 1
        slicer.modules.markups.logic().StartPlaceMode(placeModePersistence)    
        markup_node = slicer.mrmlScene.AddNewNodeByClass("vtkMRMLMarkupsFiducialNode")
        
        markup_node.AddObserver(slicer.vtkMRMLMarkupsNode.PointAddedEvent, self.onMarkupsAdded)

My first thought was that you would load the next model immediately when you placed the point, but actually you only want to switch to the next model when you confirmed that the markup was placed at the correct position (you rotate the view around and adjust the point position as needed). So, instead of observing the point added event, I would recommend to switch to the next model using a keyboard shortcut.

In the function that is executed by thr keyboard shortcut, you can check the number of points in the markup point list and from that you would know how many models are annotated already and load the next one.

Any examples of keyboard shortcut.

regards,
Saima

There are several examples for keyboard shortcuts in the script repository.

in the keyboard shortcut, is there a possibility to pass arguments to the called function.

shortcut = qt.QShortcut(slicer.util.mainWindow())
shortcut.setKey(qt.QKeySequence(“s”))
shortcut.connect( “activated()”, self.switchNextModel, like here can we pass arguments)

Here are the steps needed int he module:

  1. search for the .ply files in the folder
  2. load the first model from the list
  3. rotate model and select the location
    turn the mouse pointer to fiducial placement mode (should I use keyboard shortcut here or what is best to use)
  4. add a point to the selected location
  5. save the location coordinated to the output .txt file
  6. clear the view window
  7. load the next model and repeat

when using keyboard shortcut can I save the point in the file can i pass the argument.

I think I would need to clear the view before loading another model and selecting the point in that model.

Hi Andras,
I am stuck at the following

I have the model loaded and the user can scroll to select the position to put the point.

adding markup and then adding the user defined number of control points for a given model.

how should i proceed with this scenario?

regards,
Saima

1 Like

It is unclear for me what is that you already have and what is that you have trouble implementing.

I am not understanding how the user will add the markup control points after locating the position on the model. I want to set the number of control points as well for a given model not just one control point for all models. For example in future user can adjust the number of control points to be added for a given model.

after adding the specified number of control points I will use keyboard shortcut to move to next model.

I am stuck with the adding of control points by the user. what is the best way to add the control points.
Should there be a button to select for adding control points or a keyboard shortcut to add control points.
and would be switching between node placement mode for control points.

Please let me know how to proceed.

Thank you

regards,
Saima

Hi Andras,
I have the following code but the keyboard shortcut not working. Could you please help.

modelNode = slicer.util.loadModel(modelDir + “/” + modelFiles[index])
node_id = slicer.modules.markups.logic().AddNewFiducialNode(‘modelFileName’)
self.markup_node = slicer.mrmlScene.GetNodeByID(node_id)
self.initialize_points()

import qt
shortcut1 = qt.QShortcut(qt.QKeySequence('p'), slicer.util.mainWindow())
shortcut1.connect("activated()", self.addNewNode)             

The initialize_points function is below:
N_POINT = 3
def initialize_points(self):
slicer.modules.markups.logic().SetActiveListID(self.markup_node)
self.markup_node.SetControlPointLabelFormat(‘P%d’)
for i in range(1, self.N_POINT + 1):
self.markup_node.AddControlPoint([0, 0, 0])
self.markup_node.UnsetAllControlPoints()
self.markup_node.SetMaximumNumberOfControlPoints(self.N_POINT)

The addNewNode function is below: I want this function to do the following 1) convert mouse cursor to control point placement mode and then add no more than N_POINT.

def addNewNode(self):
slicer.modules.markups.logic().SetActiveListID(self.markup_node)
n_point = self.markup_node.GetNumberOfControlPoints()
placeModePersistence = 1
slicer.modules.markups.logic().StartPlaceMode(placeModePersistence)

    for i in range(n_point):
        self.markup_node.UnsetNthControlPointPosition(i)
        self.markup_node.SetControlPointPlacementStartIndex(i)
        self.markup_node.SetNthControlPointLabel(index, "p"+i)
        
    slicer.modules.markups.logic().StartPlaceMode(0)

This is too much code and to fragmented to copy-paste. If you send a link to your module source code on Github then I can have a look and give you some feedback.

Hi Andras,
I have added the code on github and added you in collaboration.
can you see the code?

Please let me know.
The switchnextmodel is not working with the shortcut key.
how can I get access to the df variable created. it is a list of models and I am keeping track using this list for the next model to . be uploaded.
How to lock the control points when the user move the model to locate the point

regards,
Saima

Hi Andras,
I have added the code on github and added you in collaboration.
can you see the code?

Please let me know.
The switchnextmodel is not working with the shortcut key.
how can I get access to the df variable created. it is a list of models and I am keeping track using this list for the next model to . be uploaded.
How to lock the control points when the user move the model to locate the point

regards,
Saima

Hi Andras,
I am putting the fiducials at each vertex for all the cells (triangles) in a surface model. I am using the following code. I need this for the next step where user needs to select a point in a surface model.

Problem: its very very time consuming to create a markup file for all the models (4000).

How can I improve the following code? or is there any way to highlight the vertices for all the cells in a model instead of looping through each cell and than creating a point at each vertex.

def process(self, dataDirectoryPath, showResult=True):
“”"
Run the processing algorithm.
Can be used without GUI widget.
:param dataDirectoryPath: path to the data directory containing .ply files

    """

    if not dataDirectoryPath:
        raise ValueError("no Data folder selected")

    import time
    startTime = time.time()
    logging.info('Processing started')
    logging.info('Search for .ply files')
    modelDir = dataDirectoryPath
    modelFileExt = "ply"
    modelFiles = list(f for f in os.listdir(modelDir) if f.endswith("."+modelFileExt))
    print(modelFiles)
    import pandas as pd
    
    #dataframe to store all the ply files name along with index
    df = pd.DataFrame(modelFiles, columns =['FileNames'])
    filepath = dataDirectoryPath+'/models_ids.csv'
    df.to_csv(filepath, index=True)
    
    idFile = 0
    for idFile in range(idFile,len(df)):
        print(df.iloc[idFile].FileNames)
        markupsNode = slicer.mrmlScene.AddNewNodeByClass("vtkMRMLMarkupsFiducialNode")
        markupsNode.CreateDefaultDisplayNodes()
        
        modelFileName =  df.iloc[idFile].FileNames
        modelNode = slicer.util.loadModel(dataDirectoryPath+ "/" + modelFileName)
        size = len(modelFileName)
        modelFileName = modelFileName[:size - 4]
        print(modelFileName)
        markupsNode.SetName(modelFileName)
        print(df.index.get_loc(idFile))
        
        meshModel = modelNode.GetMesh()
        points = meshModel.GetPoints()
        nPoints = points.GetNumberOfPoints()
        for i in range(nPoints):
            p = points.GetPoint(i)
            markupsNode.AddControlPoint(p[0],p[1],p[2])
            markupsNode.SetNthControlPointLocked(i, True)
            
            
        d = markupsNode.GetDisplayNode()
        d.PointLabelsVisibilityOff()
        #saving the markup point and cleaning the scene
        slicer.util.saveNode(markupsNode, os.path.join(dataDirectoryPath, modelFileName+".mrk.json"))
        slicer.mrmlScene.Clear(0)
   
    

    stopTime = time.time()
    logging.info(f'Processing completed in {stopTime-startTime:.2f} seconds')

regards,
saima

Your can draw a glyph, such as a small sphere at each point of a mesh using vtkGlyph3D filter.

In the next step after creating all these points, there is another program where the user select the point and the respective point coordinates are saved in a file.

can i still retrieve the point coordinates with the glyph?
Any example scripts?

regards,
saima safdar