NDI Polaris Lyra + 3D Slicer OpenIGTLink: How to Keep Registration Valid After Moving the Phantom?

Hello,

I have been experimenting with the NDI Polaris Lyra and Slicer OpenIGTLink. I have read all the documentation and, after numerous attempts, I think I have some parts working, but I would like to ask for help.

What I want to achieve

I have a phantom skull with an NDI passive marker tool attached to it. I also have a passive stylus tool. My goal is to be able to move the patient’s head (or phantom skull) while maintaining the calibration, so that I do not need to recalibrate every time the head moves. The tracking should remain consistent relative to the stylus tip.

My workflow

The NDI Polaris Lyra is connected to the network, and I use the Plus application to stream both the stylus and reference tool passive markers.

In 3D Slicer, I have the following transforms:

  • ToolToTracker

  • StylusToTracker

My first step is to calculate StylusToReference. I use the Transform Processor module to compute a full transform:

  • From: ToolToTracker

  • To: StylusToTracker

  • Output: StylusToReference

Once I have StylusToReference, I create a Skull_StylusTipToStylus transform and place a needle model under it.

For the fiducial registration workflow, I create the following transforms:

  • ReferenceToInitial

  • InitialToRAS

The final hierarchy is shown below:

Registration workflow

The next step is fiducial registration. I create a ReferencePoints point list that I populate using Skull_StylusTipToStylus, and an InitialPoints point list that I place on my skull STL model.

After registration, the tip of the virtual needle is very close to the corresponding point on my real-life printed phantom.

To improve accuracy, I then perform surface registration. I create an InitialSurfacePoints point list, collect approximately 40 points on the phantom surface, and register them to the skull model using the InitialToRAS transform.

After surface registration, the stylus appears even more accurate.

My question

When I move the patient’s head (or phantom skull), the tracking no longer follows correctly. Shouldn’t StylusToReference be continuously updated through ToolToTracker?

Perhaps I am misunderstanding how the transform hierarchy should work. Should I enable automatic fiducial registration instead of using manual registration?

Any guidance on whether my workflow is correct, or what I may be missing, would be greatly appreciated.

Thank you very much in advance.

Hi, it’s best to send StylusToReference directly from PLUS. You can add that transform to the OpenIGTLink part of the PLUS config file and PLUS will calculate it automatically if Reference and Stylus are available as tracked tools.

I’m not sure what you mean by ToolToTracker. Shouldn’t that be ReferenceToTracker? Anyway, if you call your reference “Tool” then StylusToTool should work just as well as StylusToReference.

Alternatively, you can invert and concatenate transforms in Slicer too, but that is less efficient, and a bit inconvenient.

I hope this helps.

Hi Tamás,

I tried it, but perhaps my configuration is not correct because no transformation appears in SlicerIGT.

Do you have any documentation or tutorial that I could use?

I am using the Tool as Reference. It is the same passive marker, but a bit longer.

My plus configuration:

<PlusConfiguration version="2.1">

  <DataCollection StartupDelaySec="1.0" >
  <DeviceSet 
      Name="PlusServer: NDI Polaris tracker with passive markers" 
      Description="Broadcasting tool tracking data through OpenIGTLink
For NDI Polaris passive marker starting kit: Tool (8700339), Stylus (8700340)" />
    <Device
      Id="TrackerDevice"
      Type="NDITracker"
      ToolReferenceFrame="Tracker" 
      NetworkHostname="192.168.1.115"
      NetworkPort="8765" >
      <DataSources>
  	<DataSource Type="Tool" Id="Tool" RomFile="NdiToolDefinitions/8700339.rom"  />
        <DataSource Type="Tool" Id="Stylus" RomFile="NdiToolDefinitions/8700340.rom"  />
      </DataSources>
      <OutputChannels>
        <OutputChannel Id="TrackerStream" >
          <DataSource Id="Tool"/>
          <DataSource Id="Stylus"/>
        </OutputChannel>
      </OutputChannels>
    </Device>
    <Device
      Id="CaptureDevice"
      Type="VirtualCapture"
      BaseFilename="RecordingTest.igs.nrrd"
      EnableFileCompression="TRUE"
      EnableCapturingOnStart="TRUE" >
      <InputChannels>
        <InputChannel Id="TrackerStream" />
      </InputChannels>
    </Device>    
  </DataCollection>

  <CoordinateDefinitions>
    <Transform From="StylusTip" To="Stylus"
      Matrix="
        1	0	0.000203823	0.0180449
        3.31529e-09	-1	-1.62655e-05	-0.00144002
        0.000203823	1.62655e-05	-1	-88.5321
        0	0	0	1"
       Error="0.554951" Date="012617_105449" />

    <Transform From="Tool" To="Reference"
    Matrix="
      1 0 0 0
      0 1 0 0
      0 0 1 0
      0 0 0 1" />
  </CoordinateDefinitions>

  <PlusOpenIGTLinkServer 
    MaxNumberOfIgtlMessagesToSend="1"
    MaxTimeSpentWithProcessingMs="50"
    ListeningPort="18944"
    SendValidTransformsOnly="true"
    OutputChannelId="TrackerStream" >
    <DefaultClientInfo>
      <MessageTypes>
        <Message Type="TRANSFORM" />
      </MessageTypes>
      <TransformNames>
        <Transform Name="ToolToReference" />
        <Transform Name="StylusToReference" />
      </TransformNames>
    </DefaultClientInfo>
  </PlusOpenIGTLinkServer>

</PlusConfiguration>

Hi Adam,

Your configuration looks good. Here are a few thoughts.

I wouldn’t keep StylusTipToStylus in the PLUS config file. It’s easier to keep it in Slicer and re-calibrate it from StylusToReference using pivot calibration when necessary.

This line doesn’t make sense in the IGTL server section:
Transform Name=“ToolToReference”
because Tool and Reference coordinate systems are the same.

StylusToReference should be enough for you, and that should be the position of the stylus relative to the reference. If you use that (looks correct in your message), then you should be able to move the patient/phantom and it should already remain accurate.

One sensitive point in your description is how you collect registration fiducial points. The goal of registration is to compute ReferenceToRAS. RAS points are just placed on the CT or STL model of the skull. But when recording matching points in the Reference coordinate system, you need to make sure that StylusTip is tranformed to Reference. In your screenshot, the StylutTipToStylus is transformed all the way to RAS, so if ReferenceToInitial or InitialToRAS is not identity, you should not use StylusTip to record new points. You can either duplicate StylusToReference to keep one copy for registration, or add a little python code to Slicer to make sure the registration points are always recorded in Reference.

So I think you are on the right track and it should work. I assume you have seen the SlicerIGT tutorials. I don’t know of newer documentation.

Let us know how it goes.

Tamas

Hi Tamás,

I quickly tried it and it is working! Thank you! I will keep experimenting with it.