Browse Source

Storing both original as well as resampled volumes in nrrd, moving vtkInterface to labkeySlicerPythonAPI/utils directory

Andrej Studen 5 years ago
parent
commit
36e957e24a
3 changed files with 67 additions and 159 deletions
  1. 66 47
      cardiacSPECT/cardiacSPECT.py
  2. 1 1
      cardiacSPECT/parseDicom.py
  3. 0 111
      cardiacSPECT/vtkInterface.py

+ 66 - 47
cardiacSPECT/cardiacSPECT.py

@@ -24,7 +24,7 @@ class cardiacSPECT(ScriptedLoadableModule):
   def __init__(self, parent):
     ScriptedLoadableModule.__init__(self, parent)
     parent.title = "Cardiac SPECT"
-    parent.categories = ["Examples"]
+    parent.categories = ["dynamicSPECT"]
     parent.dependencies = []
     parent.contributors = ["Andrej Studen (FMF/JSI)"] # replace with "Firstname Lastname (Org)"
     parent.helpText = """
@@ -94,6 +94,14 @@ class cardiacSPECTWidget(ScriptedLoadableModuleWidget):
     dataFormLayout.addRow("Segmentation",loadSegmentationButton)
     loadSegmentationButton.clicked.connect(self.onLoadSegmentationButtonClicked)
 
+    self.modelParameter=qt.QLineEdit('k1');
+    dataFormLayout.addRow('Model Parameter', self.modelParameter)
+    
+    loadModelButton = qt.QPushButton("Load")
+    loadModelButton.toolTip="Load model parameters from server"
+    dataFormLayout.addRow("Model",loadModelButton)
+    loadModelButton.clicked.connect(self.onLoadModelButtonClicked)
+
     saveVolumeButton = qt.QPushButton("Save")
     saveVolumeButton.toolTip="Save volume to NRRD"
     dataFormLayout.addRow("Volume",saveVolumeButton)
@@ -353,6 +361,9 @@ class cardiacSPECTWidget(ScriptedLoadableModuleWidget):
   def onLoadSegmentationButtonClicked(self):
       self.logic.loadSegmentation(self.patientId.text)
 
+  def onLoadModelButtonClicked(self):
+      self.logic.loadModelVolume(self.patientId.text,self.modelParameter.text)
+
   def onSaveVolumeButtonClicked(self):
       self.logic.storeVolumeNodes(self.patientId.text,
             self.time_frame_select.minimum,self.time_frame_select.maximum)
@@ -511,6 +522,11 @@ class cardiacSPECTLogic(ScriptedLoadableModuleLogic):
   def loadCTVolume(self,patientId):
       self.loadNode(patientId,patientId+'CT','VolumeFile')
 
+  def loadModelVolume(self,patientId,name):
+      node=self.loadNode(patientId,name,'VolumeFile')
+      if node:
+          node.SetName(patientId+'_'+name)
+
   def loadSegmentation(self,patientId):
       self.loadNode(patientId,'Heart','SegmentationFile')
 
@@ -518,8 +534,8 @@ class cardiacSPECTLogic(ScriptedLoadableModuleLogic):
       relativePath=self.coreRelativePath+'/'+patientId
       labkeyFile=relativePath+'/'+fName+suffix
       print ("Remote: {}").format(labkeyFile)
-      self.net.loadNode(labkeyFile,type,returnNode=True)
-
+      return self.net.loadNode(labkeyFile,type,returnNode=True)
+      
 
   def addNode(self,nodeName,v, lpsOrigin, pixel_size, lpsOrientation, dataType):
 
@@ -712,37 +728,37 @@ class cardiacSPECTLogic(ScriptedLoadableModuleLogic):
       return segNode.GetSegmentation().GetSegment(segNode.GetSegmentation().GetNthSegmentID(i)).GetName()
 
   def storeNodeRemote(self,relativePath,nodeName):
-      labkeyPath=self.pd.net.GetLabkeyPathFromRelativePath(relativePath)
-      print ("Remote: {}").format(labkeyPath)
-      #checks if exists
-      self.pd.net.mkdir(labkeyPath)
+    labkeyPath=self.pd.net.GetLabkeyPathFromRelativePath(relativePath)
+    print ("Remote: {}").format(labkeyPath)
+    #checks if exists
+    self.pd.net.mkdir(labkeyPath)
 
-      localPath=self.pd.net.GetLocalPathFromRelativePath(relativePath)
-      localPath.replace('/',os.path.sep)
+    localPath=self.pd.net.GetLocalPathFromRelativePath(relativePath)
+    localPath.replace('/',os.path.sep)
 
-      node=slicer.mrmlScene.GetFirstNodeByName(nodeName)
-      if node==None:
-          print("Node {} not found").format(nodeName)
-          return
+    node=slicer.mrmlScene.GetFirstNodeByName(nodeName)
+    if node==None:
+        print("Node {} not found").format(nodeName)
+        return
 
-      suffix=".nrrd"
-      if node.__class__.__name__=="vtkMRMLDoubleArrayNode":
-          suffix=".mcsv"
-      if (node.__class__.__name__=="vtkMRMLTransformNode" or
-        node.__class__.__name__=="vtkMRMLGridTransformNode"):
-          suffix=".h5"
+    suffix=".nrrd"
+    if node.__class__.__name__=="vtkMRMLDoubleArrayNode":
+        suffix=".mcsv"
+    if (node.__class__.__name__=="vtkMRMLTransformNode" or
+            node.__class__.__name__=="vtkMRMLGridTransformNode"):
+        suffix=".h5"
 
-      fileName=re.sub(r'_RS$',r'',nodeName)+suffix
+    #fileName=re.sub(r'_RS$',r'',nodeName)+suffix
 
-      if not os.path.isdir(localPath):
-         os.mkdir(localPath)
+    if not os.path.isdir(localPath):
+        os.mkdir(localPath)
 
-      file=os.path.join(localPath,fileName)
-      slicer.util.saveNode(node,file)
-      print("Stored to: {}").format(file)
-      f=open(file,"rb")
-      remoteFile=labkeyPath+'/'+fileName
-      self.pd.net.put(remoteFile,f.read())
+    file=os.path.join(localPath,fileName)
+    slicer.util.saveNode(node,file)
+    print("Stored to: {}").format(file)
+    f=open(file,"rb")
+    remoteFile=labkeyPath+'/'+fileName
+    self.pd.net.put(remoteFile,f.read())
 
 
 
@@ -750,31 +766,34 @@ class cardiacSPECTLogic(ScriptedLoadableModuleLogic):
   def storeVolumeNodes(self,patientId,n1,n2):
       #n1=self.time_frame.minimum;
       #n2=self.time_frame.maximum
-      relativePath=self.coreRelativePath+'/'+patientId
+    relativePath=self.coreRelativePath+'/'+patientId
 
-      print("Store CT")
-      nodeName=patientId+'CT'
+    print("Store CT")
+    nodeName=patientId+'CT'
 
-      #prefer resampled
-      testNode=slicer.util.getFirstNodeByName(nodeName+"_RS")
-      if testNode:
-          nodeName=nodeName+"_RS"
-      self.storeNodeRemote(relativePath,nodeName)
+    self.storeNodeRemote(relativePath,nodeName)
 
-      print("Storing NM from {} to {}").format(n1,n2)
-      n=n2-n1+1
-      for i in range(n):
-          it=i+n1
-          nodeName=patientId+'Volume'+str(it)
+    #prefer resampled
+    testNode=slicer.util.getFirstNodeByName(nodeName+"_RS")
+    if testNode:
+        nodeName=nodeName+"_RS"
+        self.storeNodeRemote(relativePath,nodeName)
+
+    print("Storing NM from {} to {}").format(n1,n2)
+    n=n2-n1+1
+    for i in range(n):
+        it=i+n1
+        nodeName=patientId+'Volume'+str(it)
 
-          #prefer resampled
-          testNode=slicer.util.getFirstNodeByName(nodeName+"_RS")
-          if testNode:
-              nodeName=nodeName+"_RS"
+        self.storeNodeRemote(relativePath,nodeName)
 
-          self.storeNodeRemote(relativePath,nodeName)
+        #add resampled
+        testNode=slicer.util.getFirstNodeByName(nodeName+"_RS")
+        if testNode:
+            nodeName=nodeName+"_RS"
+            self.storeNodeRemote(relativePath,nodeName)
 
-      self.storeDummyInputFunction(patientId)
+    self.storeDummyInputFunction(patientId)
 
   def storeSegmentation(self,patientId):
       relativePath=self.coreRelativePath+'/'+patientId

+ 1 - 1
cardiacSPECT/parseDicom.py

@@ -19,7 +19,7 @@ class parseDicom(ScriptedLoadableModule):
   def __init__(self, parent):
     ScriptedLoadableModule.__init__(self, parent)
     parent.title = "parseDicom"
-    parent.categories = ["Examples"]
+    parent.categories = ["dynamicSPECT"]
     parent.dependencies = []
     parent.contributors = ["Andrej Studen (FMF/JSI)"] # replace with "Firstname Lastname (Org)"
     parent.helpText = """

+ 0 - 111
cardiacSPECT/vtkInterface.py

@@ -1,111 +0,0 @@
-import vtk, qt, ctk, slicer
-import numpy as np
-import SimpleITK as sitk
-
-#set of routines to transform images from one form to another, most notably
-#numpy to vtk to itk and all possible combinations inbetween. Keep track of
-#orientation, origin and spacing between transforms
-
-class vtkInterface:
-  def __init__(self, parent):
-    parent.title = "vtk Interface"
-    parent.categories = ["Examples"]
-    parent.dependencies = []
-    parent.contributors = ["Andrej Studen (FMF/JSI)"] # replace with "Firstname Lastname (Org)"
-    parent.helpText = """
-    Convert native numpy data structures to vtk
-    """
-    parent.acknowledgementText = """
-    This module was developed within the frame of the ARRS sponsored medical
-    physics research programe to investigate quantitative measurements of cardiac
-    function using sestamibi-like tracers
-    """ # replace with organization, grant and thanks.
-    self.parent = parent
-
-
-def numpyToVTK(numpy_array, shape, data_type=vtk.VTK_FLOAT):
-    v=vtk.vtkImageData()
-    v.GetPointData().SetScalars(
-        vtk.util.numpy_support.numpy_to_vtk(
-            np.ravel(numpy_array,order='F'),deep=True, array_type=data_type))
-    v.SetOrigin(0,0,0)
-    v.SetSpacing(1,1,1)
-    v.SetDimensions(shape)
-    return v
-
-
-def completeOrientation(orientation):
-    o=orientation
-    o.append(o[1]*o[5]-o[2]*o[4])#0,3
-    o.append(o[2]*o[3]-o[0]*o[5])#1,4
-    o.append(o[0]*o[4]-o[1]*o[3])#2,5
-    return o
-
-
-def ITK2VTK(img):
-    #convert itk to vtk format.
-    #get the array
-    data=sitk.GetArrayFromImage(img)
-    #reverse the shape (don't ask, look at vtk manual if really curios)
-    shape=list(reversed(data.shape))
-    return numpyToVTK(data.ravel(),shape)
-
-def VTK2ITK(v):
-    #convert vtk image to sitk image
-    #convert to numpy first and then go to sitk
-    scalars=v.GetPointData().GetScalars()
-    shape=v.GetDimensions()
-    data=vtk.util.numpy_support.vtk_to_numpy(scalars)
-    #now convert to sitk (notice the little reversal of the shape)
-    return sitk.GetImageFromArray(np.reshape(data,list(reversed(shape))))
-
-def ITKfromNode(nodeName):
-    #use node as data source and generate an itk image
-    node=slicer.mrmlScene.GetFirstNodeByName(nodeName)
-    if node==None:
-        print "Node {0} not available".format(nodeName)
-        return
-
-    img=VTK2ITK(node.GetImageData())
-
-    img.SetOrigin(node.GetOrigin())
-    img.SetSpacing(node.GetSpacing())
-    m=vtk.vtkMatrix4x4()
-    node.GetIJKToRASDirectionMatrix(m)
-    orientation=[0]*9
-    for i in range(0,3):
-        for j in range (0,3):
-            orientation[3*j+i]=m.GetElement(i,j)
-    img.SetDirection(orientation)
-    return img
-
-
-
-def ITKtoNode(img,nodeName):
-    #display itk image and assign it a volume node
-    #useful for displaying outcomes of itk calculations
-    node=slicer.mrmlScene.GetFirstNodeByName(nodeName)
-    if node==None:
-        node=slicer.vtkMRMLScalarVolumeNode()
-        node.SetName(nodeName)
-        slicer.mrmlScene.AddNode(node)
-
-    node.SetAndObserveImageData(ITK2VTK(img))
-
-    #hairy - keep orientation, spacing and origin from node and pass it to itk
-    #for future reference
-    spacing=img.GetSpacing()
-    orientation=img.GetDirection()
-    origin=img.GetOrigin()
-
-    #we should get orientation, spacing and origin from somewhere
-    ijkToRAS = vtk.vtkMatrix4x4()
-    #think how to do this with image orientation
-
-    for i in range(0,3):
-       for j in range(0,3):
-           ijkToRAS.SetElement(i,j,spacing[i]*orientation[3*j+i])
-
-       ijkToRAS.SetElement(i,3,origin[i])
-
-    node.SetIJKToRASMatrix(ijkToRAS)