Unchanged Markup Position and Sequences

Slicer Version: 5.1.0-2022-07-17
OS: Windows 10

Hi,
I made a sequence where I tracked 1 transformed markup list at 22 different positions (as 22 sequences). However, when I try copy and pasting each of the unique marker positions into a new markup list, it just pastes the original position of the markups rather than each tracked and transformed position. For example, when I try copy and pasting “bigHole”:
At 0.10s, the position of the points “bigHole” and “smallHole” in “trackLine” are this:
image
Then when I try copy and pasting the position of “bigHole” into the new markup list “tracked_BigHole”, it just gives the original untransformed and untracked position of “bigHole”:
image
image
The same thing happens when I try it at other time positions. For example, at 0.4s:
image
image

Also, the reason why I’m doing this manually is because when I took the tracked points, the sequence “wireToProbe” I created to record these markup positions didn’t take any of the positions for some reason. This may have been a mistake on my part, though:
image
image

Thanks,
Chris

Did you try hardening the markups?

I just tried that, and it does work the first time. But then when I try applying “trackLine” back to its original transform “PlatformToMarker2” so I can get the position of the next sequence, I think it applies “PlatformToMarker2” to the hardened transformed points rather than the original points. This causes my points to move into this strange position:
image

One other solution I’ve been working on is writing a small Slicer Module instead. So far, I can loop through each sequence and acquire the values of the transform “PlatformToMarker2”, and have the original untransformed points of “trackLine”. Right now, I’m trying to use a Slicer transform function which I thought would just do the same thing as dragging my Markup list onto a Slicer transform, but the new positions end up looking like this instead:
image

The code I’ve tried using looks like this:

wireSequenceNode = slicer.util.getNode('boxToProbe-boxToProbe-Seq')
        browserNode = slicer.modules.sequences.logic().GetFirstBrowserNodeForSequenceNode(wireSequenceNode)
        browserNode.SetSelectedItemNumber(0)

        originalPoints = slicer.util.getNode('trackLine')
        shNode = slicer.vtkMRMLSubjectHierarchyNode.GetSubjectHierarchyNode(slicer.mrmlScene)
        originalID = shNode.GetItemByDataNode(originalPoints)

        print('START\n')

        for count in range(wireSequenceNode.GetNumberOfDataNodes()):  # Loops 22 times
            browserNode.SetSelectedItemNumber(count)  # Moves to next sequence

            # Creates a copy of the original untransformed line points
            clonedItemID = slicer.modules.subjecthierarchy.logic().CloneSubjectHierarchyItem(shNode, originalID)
            clonedNode = shNode.GetItemDataNode(clonedItemID)

            # Gets the current sequence's unique 'PlatformToMarker2' transform values
            currentPTMNode = slicer.util.getNode('PlatformToMarker2')
            currentTrackNode = slicer.util.getNode('Marker2ToTracker')

            #copyArray = slicer.util.arrayFromMarkupsControlPoints(originalPoints)
            platformVTK = currentPTMNode.GetMatrixTransformFromParent()
            trackerVTK = currentTrackNode.GetMatrixTransformFromParent()

            print("ITERATION:", count+1, "\n")
            print("TRANSFORM PTM:", platformVTK, "\n")
            print("TRANSFORM TRACK:", trackerVTK, "\n")
            print("POINTS:", slicer.util.arrayFromMarkupsControlPoints(clonedNode), "\n")
            clonedNode.ApplyTransformMatrix(platformVTK)
            clonedNode.ApplyTransformMatrix(trackerVTK)
            
            print("PTS TRANSFORMED:", slicer.util.arrayFromMarkupsControlPoints(clonedNode), "\n")
            #copyPoints.SetAndObserveTransformNodeID(currentTransformNode.GetID())

            #print(slicer.util.arrayFromMarkupsControlPoints(copyPoints))
            #print(slicer.util.arrayFromTransformMatrix(currentTransformNode))

        print('FINISH\n')

I might also try finding some code to harden/unharden the markup list and then just reuse a copy of the original points at each new sequence.

Thanks,
Chris

Yes, you need a script for this, as there is no module offered for this yet.

Your script looks quite good and it could be actually quite easily generalized so that it works for any node and transform (for each time point in the sequence, clone the transformed node and apply and harden the transform). The script could then be added to a Python scripted module so that it can be used with a convenient GUI. Let us know if you would be interested to contribute this (maybe in the Sandbox extension).

So when I tried to clone and harden the “trackLine” markup list, it just gave me the original untransformed positions. Is it possible to copy the markup list with the points already transformed? I know I can manually harden the original point at the start (as I spoke about earlier), which I’ll use as a starting point. The code looks like this:

originListNode = slicer.mrmlScene.AddNewNodeByClass("vtkMRMLMarkupsFiducialNode", "originList")
        directionListNode = slicer.mrmlScene.AddNewNodeByClass("vtkMRMLMarkupsFiducialNode", "directionList")

        print('START\n')
        #wireSequenceNode.GetNumberOfDataNodes()
        for count in range(1):  # Loops 22 times
            # Moves to current sequence
            browserNode.SetSelectedItemNumber(count)
            # Get the sequence's current transformed node
            currentNode = slicer.util.getNode('trackLine')

            # Clone the sequence's current transformed node
            shNode = slicer.vtkMRMLSubjectHierarchyNode.GetSubjectHierarchyNode(slicer.mrmlScene)
            itemIDToClone = shNode.GetItemByDataNode(currentNode)
            clonedItemID = slicer.modules.subjecthierarchy.logic().CloneSubjectHierarchyItem(shNode, itemIDToClone)
            clonedNode = shNode.GetItemDataNode(clonedItemID)

            # Harden the clone and copy its points onto each markup list
            clonedNode.HardenTransform()
            print(slicer.util.arrayFromMarkupsControlPoints(clonedNode))

UPDATE:
Yes, when I just harden the currentNode, it does print the correct positions the first time. But after that, it just gives incorrect values again like I explained earlier. Perhaps I can undo a Harden but maintain the original position using some commands?

START

[[ -36.5066296  -175.44519425  162.36295889]
 [ -16.77793346 -159.36146699  -48.94167894]]
[[1143.41438904 -764.82269612  -14.8033893 ]
 [ 950.82061968 -674.30338919  -18.18966374]]
[[  217.3803255    -94.13977669 -1729.84833624]
 [  329.31301792  -126.13236205 -1551.67673178]]
FINISH


You can find examples for all commonly used operations, including how to harden a transform on a node, in the script repository.

Excellent, I got it working. I’m sure this could be generalized and put into one of your Extensions, as you mentioned. And yes, I would be interested in doing this. The code is here:

transformSequenceNode = slicer.util.getNode("PlatformToMarker2")
        originalListNode = slicer.util.getNode('trackLine')

        wireSequenceNode = slicer.util.getNode('boxToProbe-boxToProbe-Seq')
        browserNode = slicer.modules.sequences.logic().GetFirstBrowserNodeForSequenceNode(wireSequenceNode)
        browserNode.SetSelectedItemNumber(0)

        print('START\n')
        print("Unhardened Original Points:", slicer.util.arrayFromMarkupsControlPoints(originalListNode))

        #wireSequenceNode.GetNumberOfDataNodes()
        for index in range(3):
            # Moves sequence browser to the current sequence
            browserNode.SetSelectedItemNumber(index)

            # Obtain the current sequence's unique "PlatformToMarker2" transform values
            currentTransform = wireSequenceNode.GetNthDataNode(index).GetTransformToParent()

            # Clone the original markup list node
            shNode = slicer.vtkMRMLSubjectHierarchyNode.GetSubjectHierarchyNode(slicer.mrmlScene)
            itemIDToClone = shNode.GetItemByDataNode(originalListNode)
            clonedItemID = slicer.modules.subjecthierarchy.logic().CloneSubjectHierarchyItem(shNode, itemIDToClone)
            clonedNode = shNode.GetItemDataNode(clonedItemID)
            clonedNode.ApplyTransform(currentTransform)
            clonedNode.HardenTransform()
            print("Hardened Clone Point ", index, ":\n", slicer.util.arrayFromMarkupsControlPoints(clonedNode), "\n")

            # Removing the cloned node since it is no longer needed.
            slicer.mrmlScene.RemoveNode(clonedNode)

Output:

START

Unhardened Original Points: [[ -101.747   -27.208 -1134.565]
 [  111.037   -24.332 -1138.072]]
Hardened Clone Point  0 :
 [[ -36.5066296  -175.44519425  162.36295889]
 [ -16.77793346 -159.36146699  -48.94167894]] 

Hardened Clone Point  1 :
 [[  -7.30387223 -167.37110704  149.97626905]
 [ -25.2482563  -158.40310593  -61.90847158]] 

Hardened Clone Point  2 :
 [[  54.83111357 -251.74909748   85.94484956]
 [ -49.3164436  -131.60420018  -55.53333947]] 

FINISH

I just iterated 3 times for testing, but the line commented above the for loop allows you to loop through the entire sequence.