Andrej Studen před 8 roky
revize
595a76ec1a

+ 30 - 0
cardiacSPECT/CMakeLists.txt

@@ -0,0 +1,30 @@
+#-----------------------------------------------------------------------------
+set(MODULE_NAME cardiacSPECT)
+
+#-----------------------------------------------------------------------------
+set(MODULE_PYTHON_SCRIPTS
+  ${MODULE_NAME}.py
+  )
+
+set(MODULE_PYTHON_RESOURCES
+  Resources/Icons/${MODULE_NAME}.png
+  )
+
+#-----------------------------------------------------------------------------
+slicerMacroBuildScriptedModule(
+  NAME ${MODULE_NAME}
+  SCRIPTS ${MODULE_PYTHON_SCRIPTS}
+  RESOURCES ${MODULE_PYTHON_RESOURCES}
+  WITH_GENERIC_TESTS
+  )
+
+#-----------------------------------------------------------------------------
+if(BUILD_TESTING)
+
+  # Register the unittest subclass in the main script as a ctest.
+  # Note that the test will also be available at runtime.
+  slicer_add_python_unittest(SCRIPT ${MODULE_NAME}.py)
+
+  # Additional build-time testing
+  add_subdirectory(Testing)
+endif()

binární
cardiacSPECT/Resources/Icons/cardiacSPECT.png


+ 1 - 0
cardiacSPECT/Testing/CMakeLists.txt

@@ -0,0 +1 @@
+add_subdirectory(Python)

+ 2 - 0
cardiacSPECT/Testing/Python/CMakeLists.txt

@@ -0,0 +1,2 @@
+
+#slicer_add_python_unittest(SCRIPT ${MODULE_NAME}ModuleTest.py)

+ 227 - 0
cardiacSPECT/cardiacSPECT.py

@@ -0,0 +1,227 @@
+import os
+import sys
+import unittest
+import vtk, qt, ctk, slicer
+from slicer.ScriptedLoadableModule import *
+import logging
+import vtkInterface as vi
+#
+# cardiacSPECT
+#
+
+class cardiacSPECT(ScriptedLoadableModule):
+  """Uses ScriptedLoadableModule base class, available at:
+  https://github.com/Slicer/Slicer/blob/master/Base/Python/slicer/ScriptedLoadableModule.py
+  """
+  def __init__(self, parent):
+    ScriptedLoadableModule.__init__(self, parent)
+    parent.title = "Cardiac SPECT"
+    parent.categories = ["Examples"]
+    parent.dependencies = []
+    parent.contributors = ["Andrej Studen (FMF/JSI)"] # replace with "Firstname Lastname (Org)"
+    parent.helpText = """
+    Load dynamic cardiac SPECT data to Slicer
+    """
+    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.helpText += self.getDefaultModuleDocumentationLink()
+    self.parent = parent
+
+#
+# cardiacSPECTWidget
+#
+
+class cardiacSPECTWidget(ScriptedLoadableModuleWidget):
+  """Uses ScriptedLoadableModuleWidget base class, available at:
+  https://github.com/Slicer/Slicer/blob/master/Base/Python/slicer/ScriptedLoadableModule.py
+  """
+
+  def setup(self):
+    ScriptedLoadableModuleWidget.setup(self)
+
+    # Instantiate and connect widgets ...
+    dataButton = ctk.ctkCollapsibleButton()
+    dataButton.text = "Data"
+    self.layout.addWidget(dataButton)
+
+    # Layout within the sample collapsible button
+    dataFormLayout = qt.QFormLayout(dataButton)
+
+    dataLoadButton = qt.QPushButton("Load")
+    dataLoadButton.toolTip="Load data from DICOM"
+    dataFormLayout.addWidget(dataLoadButton)
+    dataLoadButton.connect('clicked(bool)',self.onDataLoadButtonClicked)
+
+    # Set local var as instance attribute
+    self.dataLoadButton = dataLoadButton
+
+    # Add vertical spacer
+    self.layout.addStretch(1)
+
+    addFrameButton=qt.QPushButton("Add Frame")
+    addFrameButton.toolTip="Add frame to VTK"
+    dataFormLayout.addWidget(addFrameButton)
+    addFrameButton.connect('clicked(bool)',self.onAddFrameButtonClicked)
+
+    addCTButton=qt.QPushButton("Add CT")
+    addCTButton.toolTip="Add CT to VTK"
+    dataFormLayout.addWidget(addCTButton)
+    addCTButton.connect('clicked(bool)',self.onAddCTButtonClicked)
+
+    #
+    # Parameters Area
+    #
+
+    parametersCollapsibleButton = ctk.ctkCollapsibleButton()
+    parametersCollapsibleButton.text = "Parameters"
+    self.layout.addWidget(parametersCollapsibleButton)
+
+    # Layout within the dummy collapsible button
+    parametersFormLayout = qt.QFormLayout(parametersCollapsibleButton)
+
+    #
+    # check box to trigger taking screen shots for later use in tutorials
+    #
+    self.time_frame_select=qt.QLineEdit()
+    self.time_frame_select.setText("2")
+    self.time_frame_select.toolTip = "Select the time frame"
+    parametersFormLayout.addRow(self.time_frame_select)
+
+
+    #
+    # Apply Button
+    #
+    self.applyButton = qt.QPushButton("Apply")
+    self.applyButton.toolTip = "Run the algorithm."
+    self.applyButton.enabled = False
+    parametersFormLayout.addRow(self.applyButton)
+
+    # connections
+    self.applyButton.connect('clicked(bool)', self.onApplyButton)
+    #self.inputSelector.connect("currentNodeChanged(vtkMRMLNode*)", self.onSelect)
+    #self.outputSelector.connect("currentNodeChanged(vtkMRMLNode*)", self.onSelect)
+
+    # Add vertical spacer
+    self.layout.addStretch(1)
+
+    self.logic=cardiacSPECTLogic()
+
+  def cleanup(self):
+    pass
+
+  def onApplyButton(self):
+      pass
+    #logic = cardiacSPECTLogic()
+    #imageThreshold = self.imageThresholdSliderWidget.value
+
+  def onDataLoadButtonClicked(self):
+      self.logic.loadData()
+
+  def onAddFrameButtonClicked(self):
+      it=int(self.time_frame_select.text)
+      self.logic.addFrame(it)
+
+  def onAddCTButtonClicked(self):
+      self.logic.addCT()
+#
+#
+# cardiacSPECTLogic
+#
+
+class cardiacSPECTLogic(ScriptedLoadableModuleLogic):
+  """This class should implement all the actual
+  computation done by your module.  The interface
+  should be such that other python code can import
+  this class and make use of the functionality without
+  requiring an instance of the Widget.
+  Uses ScriptedLoadableModuleLogic base class, available at:
+  https://github.com/Slicer/Slicer/blob/master/Base/Python/slicer/ScriptedLoadableModule.py
+  """
+
+  def loadData(self):
+    startDir=os.environ['HOME']
+    inputDir=qt.QFileDialog.getExistingDirectory(None,
+        'Select DICOM directory',startDir)
+
+    #use another script from the same file
+    mypath=os.environ['HOME']+'/software/build/dynamicSPECT'
+    sys.path.append(mypath)
+    import parseDicom as pd
+
+    self.frame_data,self.frame_time=pd.read_dynamic_SPECT(inputDir)
+
+    self.ct_data,self.ct_center,self.ct_pixel_size=pd.read_CT(inputDir)
+
+    #additional message via qt
+    qt.QMessageBox.information(
+        slicer.util.mainWindow(),
+        'Slicer Python','Data loaded')
+
+
+  def addNode(self,nodeName,v):
+       #nodeName='testVolume'+str(it)
+       newNode=slicer.vtkMRMLScalarVolumeNode()
+       newNode.SetName(nodeName)
+       ijkToRAS = vtk.vtkMatrix4x4()
+       ijkToRAS.Identity()
+       newNode.SetIJKToRASMatrix(ijkToRAS)
+       newNode.SetAndObserveImageData(v)
+       slicer.mrmlScene.AddNode(newNode)
+       selectionNode = slicer.app.applicationLogic().GetSelectionNode()
+       selectionNode.SetReferenceActiveVolumeID(newNode.GetID())
+       slicer.app.applicationLogic().PropagateVolumeSelection(0)
+
+  def addFrame(self,it):
+       #convert data from numpy.array to vtkImageData
+       #use time point it
+       frame_data=self.frame_data[:,:,:,it];
+       nodeName='testVolume'+str(it)
+       self.addNode(nodeName,
+        vi.numpyToVTK3D(frame_data,[0,0,0],[1,1,1]))
+
+  def addCT(self):
+       nodeName='testCT'
+       self.addNode(nodeName,
+            vi.numpyToVTK3D(self.ct_data,
+                self.ct_center,self.ct_pixel_size))
+
+
+class cardiacSPECTTest(ScriptedLoadableModuleTest):
+  """
+  This is the test case for your scripted module.
+  Uses ScriptedLoadableModuleTest base class, available at:
+  https://github.com/Slicer/Slicer/blob/master/Base/Python/slicer/ScriptedLoadableModule.py
+  """
+
+  def setUp(self):
+    """ Do whatever is needed to reset the state - typically a scene clear will be enough.
+    """
+    slicer.mrmlScene.Clear(0)
+
+  def runTest(self):
+    """Run as few or as many tests as needed here.
+    """
+    self.setUp()
+    self.test_cardiacSPECT1()
+
+  def test_cardiacSPECT1(self):
+    """ Ideally you should have several levels of tests.  At the lowest level
+    tests should exercise the functionality of the logic with different inputs
+    (both valid and invalid).  At higher levels your tests should emulate the
+    way the user would interact with your code and confirm that it still works
+    the way you intended.
+    One of the most important features of the tests is that it should alert other
+    developers when their changes will have an impact on the behavior of your
+    module.  For example, if a developer removes a feature that you depend on,
+    your test should break so they know that the feature is needed.
+    """
+
+    self.delayDisplay("Starting the test")
+    #
+    # first, get some data
+    #
+
+    self.delayDisplay('Test passed!')

+ 228 - 0
cardiacSPECT/cardiacSPECT/cardiacSPECT.py

@@ -0,0 +1,228 @@
+import os
+import sys
+import unittest
+import vtk, qt, ctk, slicer
+from slicer.ScriptedLoadableModule import *
+import logging
+
+#
+# cardiacSPECT
+#
+
+class cardiacSPECT(ScriptedLoadableModule):
+  """Uses ScriptedLoadableModule base class, available at:
+  https://github.com/Slicer/Slicer/blob/master/Base/Python/slicer/ScriptedLoadableModule.py
+  """
+  def __init__(self, parent):
+    ScriptedLoadableModule.__init__(self, parent)
+    parent.title = "Cardiac SPECT"
+    parent.categories = ["Examples"]
+    parent.dependencies = []
+    parent.contributors = ["Andrej Studen (FMF/JSI)"] # replace with "Firstname Lastname (Org)"
+    parent.helpText = """
+    Load dynamic cardiac SPECT data to Slicer
+    """
+    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.helpText += self.getDefaultModuleDocumentationLink()
+    self.parent = parent
+
+#
+# cardiacSPECTWidget
+#
+
+class cardiacSPECTWidget(ScriptedLoadableModuleWidget):
+  """Uses ScriptedLoadableModuleWidget base class, available at:
+  https://github.com/Slicer/Slicer/blob/master/Base/Python/slicer/ScriptedLoadableModule.py
+  """
+  def __init__(self, parent):
+      ScriptedLoadableModuleWidget.__init__(self, parent)
+      self.logic=cardiacSPECTLogic()
+
+  def setup(self):
+    ScriptedLoadableModuleWidget.setup(self)
+
+    # Instantiate and connect widgets ...
+    dataButton = ctk.ctkCollapsibleButton()
+    dataButton.text = "Data"
+    self.layout.addWidget(dataButton)
+
+    # Layout within the sample collapsible button
+    dataFormLayout = qt.QFormLayout(dataButton)
+
+    dataLoadButton = qt.QPushButton("Load")
+    dataLoadButton.toolTip="Load data from DICOM"
+    dataFormLayout.addWidget(dataLoadButton)
+    dataLoadButton.connect('clicked(bool)',self.onDataLoadButtonClicked)
+
+    # Set local var as instance attribute
+    self.dataLoadButton = dataLoadButton
+
+    # Add vertical spacer
+    self.layout.addStretch(1)
+
+    dataImportButton=qt.QPushButton("Import")
+    dataImportButton.toolTip="Import time frame to VTK"
+    dataFormLayout.addWidget(dataImportButton)
+    dataImportButton.connect('clicked(bool)',self.onImportButtonClicked)
+
+    #
+    # Parameters Area
+    #
+
+    parametersCollapsibleButton = ctk.ctkCollapsibleButton()
+    parametersCollapsibleButton.text = "Parameters"
+    self.layout.addWidget(parametersCollapsibleButton)
+
+    # Layout within the dummy collapsible button
+    parametersFormLayout = qt.QFormLayout(parametersCollapsibleButton)
+
+    #
+    # check box to trigger taking screen shots for later use in tutorials
+    #
+    self.time_frame_select=qt.QLineEdit()
+    self.time_frame_select.setText("2")
+    self.time_frame_select.toolTip = "Select the time frame"
+    parametersFormLayout.addRow(self.time_frame_select)
+
+
+    #
+    # Apply Button
+    #
+    self.applyButton = qt.QPushButton("Apply")
+    self.applyButton.toolTip = "Run the algorithm."
+    self.applyButton.enabled = False
+    parametersFormLayout.addRow(self.applyButton)
+
+    # connections
+    self.applyButton.connect('clicked(bool)', self.onApplyButton)
+    #self.inputSelector.connect("currentNodeChanged(vtkMRMLNode*)", self.onSelect)
+    #self.outputSelector.connect("currentNodeChanged(vtkMRMLNode*)", self.onSelect)
+
+    # Add vertical spacer
+    self.layout.addStretch(1)
+
+
+  def cleanup(self):
+    pass
+
+  def onApplyButton(self):
+    #logic = cardiacSPECTLogic()
+    #imageThreshold = self.imageThresholdSliderWidget.value
+
+  def onDataLoadButtonClicked(self):
+      self.logic.load()
+
+  def onImportButtonClicked(self):
+      it=int(self.time_frame_select.text())
+      self.logic.setVolume(it)
+#
+# cardiacSPECTLogic
+#
+
+class cardiacSPECTLogic(ScriptedLoadableModuleLogic):
+  """This class should implement all the actual
+  computation done by your module.  The interface
+  should be such that other python code can import
+  this class and make use of the functionality without
+  requiring an instance of the Widget.
+  Uses ScriptedLoadableModuleLogic base class, available at:
+  https://github.com/Slicer/Slicer/blob/master/Base/Python/slicer/ScriptedLoadableModule.py
+  """
+
+  def load(self):
+    startDir=os.environ['HOME']
+    inputDir=qt.QFileDialog.getExistingDirectory(None,
+        'Select DICOM directory',startDir)
+
+    #use another script from the same file
+    mypath=os.environ['HOME']+'/software/build/dynamicSPECT'
+    sys.path.append(mypath)
+    import parseData as pd
+
+    self.frame_data,self.frame_time=pd.read_dynamic_SPECT(inputDir)
+
+    #additional message via qt
+    qt.QMessageBox.information(
+        slicer.util.mainWindow(),
+        'Slicer Python','Data loaded')
+
+  def setVolume(self,it):
+       #convert data from numpy.array to vtkImageData
+       #use time point it
+       frame_data=self.frame_data;
+
+       v=vtk.vtkImageData()
+       v.SetDimensions(
+            frame_data.shape[0],
+            frame_data.shape[1],
+            frame_data.shape[2])
+       v.SetOrigin(0.0*frame_data.shape[0],
+                0.0*frame_data.shape[1],
+                0.0*frame_data.shape[2])
+       sp = 1.0
+       v.SetSpacing(sp, sp, sp)
+       scalars = vtk.vtkShortArray()
+       for k in range(0,frame_data.shape[2]):
+           kOffset = k * frame_data.shape[1]*self.frame_data.shape[0]
+           for j in range(0,frame_data.shape[1]):
+               jOffset = j * frame_data.shape[0]
+               for i in range(0,frame_data.shape[0]):
+                   offset = i + jOffset + kOffset;
+                   scalars.InsertTuple1(offset,frame_data[i,j,k,it])
+       v.GetPointData().SetScalars(scalars);
+       self.v=v
+
+       nodeName='testVolume'+str(it)
+       newNode=slicer.vtkMRMLScalarVolumeNode()
+       newNode.SetName(nodeName)
+       ijkToRAS = vtk.vtkMatrix4x4()
+       ijkToRAS.Identity()
+       newNode.SetIJKToRASMatrix(ijkToRAS)
+       newNode.SetAndObserveImageData(v)
+       slicer.mrmlScene.AddNode(newNode)
+       selectionNode = slicer.app.applicationLogic().GetSelectionNode()
+       selectionNode.SetReferenceActiveVolumeID(newNode.GetID())
+       slicer.app.applicationLogic().PropagateVolumeSelection(0)
+
+
+
+class cardiacSPECTTest(ScriptedLoadableModuleTest):
+  """
+  This is the test case for your scripted module.
+  Uses ScriptedLoadableModuleTest base class, available at:
+  https://github.com/Slicer/Slicer/blob/master/Base/Python/slicer/ScriptedLoadableModule.py
+  """
+
+  def setUp(self):
+    """ Do whatever is needed to reset the state - typically a scene clear will be enough.
+    """
+    slicer.mrmlScene.Clear(0)
+
+  def runTest(self):
+    """Run as few or as many tests as needed here.
+    """
+    self.setUp()
+    self.test_cardiacSPECT1()
+
+  def test_cardiacSPECT1(self):
+    """ Ideally you should have several levels of tests.  At the lowest level
+    tests should exercise the functionality of the logic with different inputs
+    (both valid and invalid).  At higher levels your tests should emulate the
+    way the user would interact with your code and confirm that it still works
+    the way you intended.
+    One of the most important features of the tests is that it should alert other
+    developers when their changes will have an impact on the behavior of your
+    module.  For example, if a developer removes a feature that you depend on,
+    your test should break so they know that the feature is needed.
+    """
+
+    self.delayDisplay("Starting the test")
+    #
+    # first, get some data
+    #
+
+    self.delayDisplay('Test passed!')

+ 179 - 0
parseDicom.py

@@ -0,0 +1,179 @@
+import os
+import sys
+import dicom
+import numpy as np
+import re
+#rom os import listdir
+#from os.path import isfile, join
+#onlyfiles = [f for f in listdir(mypath) if isfile(join(mypath, f))]
+#import Tkinter as tk
+#from Tkinter import filedialog
+
+#root = tk.Tk()
+#root.withdraw()
+#file_path = filedialog.askopenfilename()
+class parseDicom:
+  def __init__(self, parent):
+    parent.title = "parse dicom"
+    parent.categories = ["Examples"]
+    parent.dependencies = []
+    parent.contributors = ["Andrej Studen (FMF/JSI)"] # replace with "Firstname Lastname (Org)"
+    parent.helpText = """
+    Parse dynamic SPECT DICOM files
+    """
+    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 read_dynamic_SPECT(mypath):
+#mypath=os.environ['PWD']
+#list files
+    onlyfiles = [f for f in os.listdir(mypath)
+            if os.path.isfile(os.path.join(mypath, f))]
+
+    for f in onlyfiles:
+        print '{}:'.format(f)
+        try:
+            plan = dicom.read_file(os.path.join(mypath,f))
+        except:
+            print ("Not a dicom file")
+            continue
+        try:
+            nframe=plan[0x0019,0x10a5].value;
+        except:
+            print ("Tag not found;")
+            continue
+        if not (type(nframe) is list) :
+            continue
+
+    #this is the "master" file where data on other files can be had
+    #here we found out the duration of the frame and their distribution through
+    #phases and cycles
+
+        for i in range(1,len(nframe)):
+            nframe[i]+=nframe[i-1]
+
+        print(nframe)
+
+    #nframe now holds for index i total number of frames collected up
+    #to the end of each phase
+
+        frame_start=plan[0x0019,0x10a7].value
+        frame_stop=plan[0x0019,0x10a8].value
+        frame_duration=plan[0x0019,0x10a9].value
+
+    #print "rep [{}] start [{}] stop [{}] duration [{}]".format(
+    #len(rep),len(rep_start),len(rep_stop),len(rep_duration))
+
+#select AC reconstructed data
+    frame_time=[None]*nframe[-1]
+    frame_data=np.empty([1,1,1,nframe[-1]])
+    for f in onlyfiles:
+        try:
+            plan = dicom.read_file(os.path.join(mypath,f))
+        except:
+            print ("Not a dicom file")
+            continue
+
+        try:
+            pf=plan[0x0018,0x5020]
+        except:
+            print ("Tag not found")
+            continue
+        try:
+            phase=plan[0x0035,0x1005].value
+            cycle=plan[0x0035,0x1004].value
+        except:
+            print ("Phase/Cycle tag not found")
+            continue
+
+        #convert phase/cycle to frame index
+        off=0
+        if phase > 1:
+            off=nframe[phase-2]
+        ifi=off+cycle-1
+
+    #from values in the master file determine frame time
+    #(as the mid point between starting and ending the frame)
+        frame_time[ifi]=0.5*(frame_start[ifi]+frame_stop[ifi]); #in ms
+
+        print "({},{}) converted to {} at {} for {}".format(
+            phase,cycle,ifi,frame_time[ifi],frame_duration[ifi])
+
+    #play with pixel data
+        if frame_data.shape[0] == 1:
+            sh=plan.pixel_array.shape;
+            sh=list(sh)
+            sh.append(nframe[-1])
+            frame_data=np.empty(sh)
+            print "Setting frame_data to",sh
+
+        frame_data[:,:,:,ifi]=plan.pixel_array
+    return [frame_data,frame_time]
+
+def read_CT(mypath):
+    onlyfiles = [f for f in os.listdir(mypath)
+            if os.path.isfile(os.path.join(mypath, f))]
+
+    ct_data = []
+    ct_idx = []
+    ct_pixel_size = [0,0,0]
+    ct_center = [0,0,0]
+
+    for f in onlyfiles:
+        print '{}:'.format(f)
+        try:
+            plan = dicom.read_file(os.path.join(mypath,f))
+        except:
+            print ("Not a dicom file")
+            continue
+
+        if plan.Modality != 'CT':
+            print ('Not a CT file')
+            continue
+
+        if re.match("AC",plan.SeriesDescription) == None:
+            print (plan.SeriesDescription)
+            print ('Not a AC file')
+            continue
+        #a slice of pure CT
+        ct_data.append(plan.pixel_array)
+        ct_idx.append(plan.InstanceNumber)
+        #ct_center.append(plan.ImagePositionPatient)
+
+        pixel_size_read=[plan.PixelSpacing[0],plan.PixelSpacing[1],
+            plan.SliceThickness]
+
+
+        for i in range(0,3):
+            if ct_pixel_size[i] == 0:
+                ct_pixel_size[i] = float(pixel_size_read[i])
+            if abs(ct_pixel_size[i]-pixel_size_read[i]) > 1e-3:
+                print 'Pixel size mismatch {.2f}/{.2f}'.format(ct_pixel_size[i],
+                pixel_size_read[i])
+
+        for i in range(0,2):
+            if ct_center[i] == 0:
+                ct_center[i] = float(plan.ImagePositionPatient[i])
+            if abs(ct_center[i]-plan.ImagePositionPatient[i]) > 1e-3:
+                        print 'Image center mismatch {.2f}/{.2f}'.format(ct_center[i],
+                        plan.ImagePositionPatient[i])
+        #not average, but minimum (!)
+        if plan.ImagePositionPatient[2]<ct_center[2]:
+            ct_center[2]=plan.ImagePositionPatient[2]
+
+    nz=len(ct_idx)
+    #not average, again
+    #ct_center[2]/=nz
+    sh=ct_data[-1].shape
+    sh_list=list(sh)
+    sh_list.append(nz)
+    data_array=np.zeros(sh_list)
+
+    for k in range(0,nz):
+        data_array[:,:,ct_idx[k]-1]=ct_data[k]
+
+    return data_array,ct_center,ct_pixel_size

+ 37 - 0
vtkInterface.py

@@ -0,0 +1,37 @@
+import vtk, qt, ctk, slicer
+import numpy as np
+
+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 numpyToVTK3D(numpy_array, origin, spacing):
+    v=vtk.vtkImageData()
+    v.SetDimensions(
+            numpy_array.shape[0],
+            numpy_array.shape[1],
+            numpy_array.shape[2])
+    v.SetOrigin(origin[0], origin[1], origin[2])
+    v.SetSpacing(spacing[0], spacing[1], spacing[2])
+    scalars = vtk.vtkShortArray()
+    for k in range(0,numpy_array.shape[2]):
+       kOffset = k * numpy_array.shape[1]*numpy_array.shape[0]
+       for j in range(0,numpy_array.shape[1]):
+           jOffset = j * numpy_array.shape[0]
+           for i in range(0,numpy_array.shape[0]):
+               offset = i + jOffset + kOffset;
+               scalars.InsertTuple1(offset,numpy_array[i,j,k])
+    v.GetPointData().SetScalars(scalars)
+    return v