def addStopWatch(self): from libs.StopWatch import StopWatch self.stopWatch = StopWatch()
class ConoProbeConnectorLogic(ScriptedLoadableModuleLogic): def __init__(self): # Member variables self.outputLabels = None self.recordedDataBuffer = [] self.record = False self.reset = False self.outputObserverTag = -1 self.rigidBodyToTrackerTransformNode = None self.measurementToMeasurerTransformNode = None self.parametersToMeasurerTransformNode = None self.plus = None self.m = vtk.vtkMatrix4x4() self.direction = -1 self.ras = [0, 0, 0, 1] self.d = 0.0 self.snr = 0 self.total = 0 self.LABEL_UPDATE_RATE = 10 self.labelUpdateCount = 0 self.snrThreshold = 40 self.distanceMaximumValue = 1000.0 self.distanceMinimumValue = 0.0 self.lensMaxDistance = 0.0 self.lensMinDistance = 0.0 self.normalizingConstant = 0.0 self.min = -1000.0 self.addColours = True import Viewpoint # Viewpoint self.viewpointLogic = Viewpoint.ViewpointLogic() self.stopWatch = None # StopWatch # Create style sheets self.errorStyleSheet = "QLabel { color : #FF0000; \ font: bold 14px}" self.defaultStyleSheet = "QLabel { color : #000000; \ font: bold 14px}" # Create rainbow colour table self.colorTable=slicer.vtkMRMLColorTableNode() self.colorTable.SetTypeToRainbow () # Add MeasurementPoint self.measurementPointMarkupsFiducialNode = slicer.util.getNode('MeasurementPoint') if not self.measurementPointMarkupsFiducialNode: self.measurementPointMarkupsFiducialNode = slicer.vtkMRMLMarkupsFiducialNode() self.measurementPointMarkupsFiducialNode.SetName('MeasurementPoint') self.measurementPointMarkupsFiducialNode.AddFiducial(0, 0, 0) self.measurementPointMarkupsFiducialNode.SetNthFiducialLabel(0, '') slicer.mrmlScene.AddNode(self.measurementPointMarkupsFiducialNode) self.measurementPointMarkupsFiducialNode.GetDisplayNode().SetGlyphScale(2.0) self.measurementPointMarkupsFiducialNode.GetDisplayNode().SetGlyphType(13) # Sphere3D self.measurementPointMarkupsFiducialNode.GetDisplayNode().SetSelectedColor(1, 0, 0) # Add RecordedModel self.recordedModelNode = slicer.util.getNode('RecordedModel') if not self.recordedModelNode: recordedPoints = vtk.vtkPoints() recordedVertices = vtk.vtkCellArray() recordedPolyData = vtk.vtkPolyData() recordedPolyData.SetPoints(recordedPoints) recordedPolyData.SetVerts(recordedVertices) self.recordedModelNode = self.addModelToScene(recordedPolyData, "RecordedModel") self.recordedModelNode.GetModelDisplayNode().SetPointSize(3) # Set up coloured scalars colorArray = vtk.vtkDoubleArray() colorArray.SetNumberOfComponents(4) colorArray.SetName('Colors') self.recordedModelNode.GetPolyData().GetPointData().SetScalars(colorArray) # Create share directory self.pathToCreatedSaveDir = self.createShareDirectory() # Post-Processing default (for undo) self.recordedDataBufferDefault = [] def __del__(self): self.removeUpdateObserver() self.viewpointLogic.stopViewpoint() def initColouring(self, direction, min, max): if max > min: newMin = 0 newMax = 255 self.min = min self.direction = direction self.normalizingConstant = (newMax - newMin) / (max - min) return True else: logging.warning('Not possible to enable Intuitive Colouring: max <= min') return False def addStopWatch(self): from libs.StopWatch import StopWatch self.stopWatch = StopWatch() def startViewpoint(self): self.viewpointLogic.startViewpoint() def stopViewpoint(self): self.viewpointLogic.stopViewpoint() def initViewpoint(self): if self.rigidBodyToTrackerTransformNode: # ConoProbeModelToMeasurement conoProbeModelToMeasurement = slicer.util.getNode('ConoProbeModelToMeasurement') if not conoProbeModelToMeasurement: conoProbeModelToMeasurement=slicer.vtkMRMLLinearTransformNode() conoProbeModelToMeasurement.SetName("ConoProbeModelToMeasurement") m = vtk.vtkMatrix4x4() # Large lens # m.SetElement( 0, 0, -0.92 ) # Row 1 # m.SetElement( 0, 1, -0.16 ) # m.SetElement( 0, 2, 0.37 ) # m.SetElement( 0, 3, 63.53 ) # m.SetElement( 1, 0, -0.38 ) # Row 2 # m.SetElement( 1, 1, 0.06 ) # m.SetElement( 1, 2, -0.92 ) # m.SetElement( 1, 3, 81.99 ) # m.SetElement( 2, 0, 0.13 ) # Row 3 # m.SetElement( 2, 1, -0.98 ) # m.SetElement( 2, 2, -0.12 ) # m.SetElement( 2, 3, -40.15 ) # Small lens m.SetElement( 0, 0, -0.92 ) # Row 1 m.SetElement( 0, 1, -0.16 ) m.SetElement( 0, 2, 0.37 ) m.SetElement( 0, 3, 63.53 ) m.SetElement( 1, 0, -0.38 ) # Row 2 m.SetElement( 1, 1, 0.06 ) m.SetElement( 1, 2, -0.92 ) m.SetElement( 1, 3, 81.99 ) m.SetElement( 2, 0, 0.13 ) # Row 3 m.SetElement( 2, 1, -0.98 ) m.SetElement( 2, 2, -0.12 ) m.SetElement( 2, 3, 129.85 ) conoProbeModelToMeasurement.SetMatrixTransformToParent(m) slicer.mrmlScene.AddNode(conoProbeModelToMeasurement) conoProbeModelToMeasurement.SetAndObserveTransformNodeID(self.rigidBodyToTrackerTransformNode.GetID()) # ViewPointToMeasurement viewPointToMeasurement = slicer.util.getNode('ViewPointToMeasurement') if not viewPointToMeasurement: viewPointToMeasurement=slicer.vtkMRMLLinearTransformNode() viewPointToMeasurement.SetName("ViewPointToMeasurement") m = vtk.vtkMatrix4x4() # Large lens # m.SetElement( 0, 0, -1 ) # Row 1 # m.SetElement( 0, 1, 0 ) # m.SetElement( 0, 2, 0 ) # m.SetElement( 0, 3, 53.00 ) # m.SetElement( 1, 0, 0 ) # Row 2 # m.SetElement( 1, 1, -1 ) # m.SetElement( 1, 2, 0 ) # m.SetElement( 1, 3, 88.00 ) # m.SetElement( 2, 0, 0 ) # Row 3 # m.SetElement( 2, 1, 0 ) # m.SetElement( 2, 2, 1 ) # m.SetElement( 2, 3, -106 ) # Small lens m.SetElement( 0, 0, -1 ) # Row 1 m.SetElement( 0, 1, 0 ) m.SetElement( 0, 2, 0 ) m.SetElement( 0, 3, 53.00 ) m.SetElement( 1, 0, 0 ) # Row 2 m.SetElement( 1, 1, -1 ) m.SetElement( 1, 2, 0 ) m.SetElement( 1, 3, 88.00 ) m.SetElement( 2, 0, 0 ) # Row 3 m.SetElement( 2, 1, 0 ) m.SetElement( 2, 2, 1 ) m.SetElement( 2, 3, 54 ) viewPointToMeasurement.SetMatrixTransformToParent(m) slicer.mrmlScene.AddNode(viewPointToMeasurement) viewPointToMeasurement.SetAndObserveTransformNodeID(self.rigidBodyToTrackerTransformNode.GetID()) # ConoProbeModel conoProbeModel = slicer.util.getNode('ConoProbeModel') if not conoProbeModel: moduleDirectoryPath = slicer.modules.conoprobeconnector.path.replace('ConoProbeConnector.py', '') slicer.util.loadModel(qt.QDir.toNativeSeparators(moduleDirectoryPath + '../../Data/Models/ConoProbeModel.stl')) conoProbeModel=slicer.util.getNode(pattern="ConoProbeModel") conoProbeModel.SetName("ConoProbeModel") conoProbeModel.GetDisplayNode().SetOpacity(0.7) conoProbeModel.SetAndObserveTransformNodeID(conoProbeModelToMeasurement.GetID()) # Camera camera = slicer.util.getNode('Camera') if not camera: camera=slicer.vtkMRMLCameraNode() camera.SetName("Camera") slicer.mrmlScene.AddNode(camera) threeDView = slicer.util.getNode("view1") camera.SetActiveTag(threeDView.GetID()) # Viewpoint self.viewpointLogic.setCameraNode(camera) self.viewpointLogic.setTransformNode(viewPointToMeasurement) self.viewpointLogic.setModelPOVOnNode(conoProbeModel) self.viewpointLogic.SetCameraXPosMm(53) self.viewpointLogic.SetCameraYPosMm(72) self.viewpointLogic.SetCameraZPosMm(119) self.viewpointLogic.startViewpoint() def getLensMinMax(self): minMax = [0, 0] self.parametersToMeasurerTransformNode.GetMatrixTransformToParent(self.m) minMax[0] = self.m.GetElement(2, 0) minMax[1] = self.m.GetElement(2, 1) return minMax def saveRecordedDataToDefault(self): self.recordedDataBufferDefault = self.recordedDataBuffer def performPostProcessing(self, snrThreshold, distanceMinimumValue, distanceMaximumValue): # Create new vtkPolyData newPoints = vtk.vtkPoints() newVertices = vtk.vtkCellArray() newPolyData = vtk.vtkPolyData() newPolyData.SetPoints(newPoints) newPolyData.SetVerts(newVertices) colorArray = vtk.vtkDoubleArray() colorArray.SetNumberOfComponents(4) colorArray.SetName('Colors') newPolyData.GetPointData().SetScalars(colorArray) # Filter accordingly to the input parameters recordedDataBufferFiltered = [] for idx in range(len(self.recordedDataBuffer)): d = self.recordedDataBuffer[idx][3] snr = self.recordedDataBuffer[idx][4] if (snr > snrThreshold and d < distanceMaximumValue and d > distanceMinimumValue): recordedDataBufferFiltered.append(self.recordedDataBuffer[idx]) self.addPointToPolyData(newPolyData, self.recordedDataBuffer[idx][0:3]) # Update recorded model and buffer self.recordedModelNode.GetPolyData().DeepCopy(newPolyData) self.recordedModelNode.GetPolyData().Modified() self.recordedDataBuffer = recordedDataBufferFiltered def showColouring(self): self.recordedModelNode.GetModelDisplayNode().SetActiveScalarName('Colors') self.recordedModelNode.GetModelDisplayNode().SetScalarVisibility(True) def undoPostProcessing(self): # Create new vtkPolyData newPoints = vtk.vtkPoints() newVertices = vtk.vtkCellArray() newPolyData = vtk.vtkPolyData() newPolyData.SetPoints(newPoints) newPolyData.SetVerts(newVertices) colorArray = vtk.vtkDoubleArray() colorArray.SetNumberOfComponents(4) colorArray.SetName('Colors') newPolyData.GetPointData().SetScalars(colorArray) # Filter accordingly to the input parameters recordedDataBufferFiltered = [] for idx in range(len(self.recordedDataBufferDefault)): self.addPointToPolyData(newPolyData, self.recordedDataBufferDefault[idx][0:3]) # Update recorded model and buffer self.recordedModelNode.GetPolyData().DeepCopy(newPolyData) self.recordedModelNode.GetPolyData().Modified() self.recordedDataBuffer = self.recordedDataBufferDefault def setOutPutLabels(self, labels): self.outputLabels = labels def saveData(self): dateAndTime = time.strftime("_%Y-%m-%d_%H-%M-%S") path = self.pathToCreatedSaveDir + '/Points' + dateAndTime # Save model to .vtk slicer.modules.models.logic().SaveModel(path + '.vtk', self.recordedModelNode) # Save data to .csv self.writeRecordedDataBufferToFile(path + '.csv') saveDir = self.pathToCreatedSaveDir.split('SlicerModules')[1] # Just to remove unnecessary information self.outputLabels[3].setText('Data saved to: ' + saveDir) def writeRecordedDataBufferToFile(self, path): with open(path, 'wb') as csvfile: writer = csv.writer(csvfile, delimiter=",") writer.writerow(['x_ConoProbe', 'y_ConoProbe', 'z_ConoProbe', 'd', 'snr', 'total', 'x_RigidBody', 'y_RigidBody', 'z_RigidBody', 'Rxx', 'Rxy', 'Rxz', 'Ryx', 'Ryy', 'Ryz', 'Rzx', 'Rzy', 'Rzz']) for idx in range(len(self.recordedDataBuffer)): writer.writerow(self.recordedDataBuffer[idx]) def getModelXYZ(self): xyz = [] for idx in range(self.recordedModelNode.GetPolyData().GetPoints().GetNumberOfPoints()): point = [0, 0, 0] self.recordedModelNode.GetPolyData().GetPoint(idx, point) xyz.append(point) return xyz def createShareDirectory(self): date = time.strftime("%Y-%m-%d") shareDirPath = slicer.modules.conoprobeconnector.path.replace("ConoProbeConnector.py","") + 'Output/' + date if not os.path.exists(shareDirPath): os.makedirs(shareDirPath) return shareDirPath def addModelToScene(self, polyData, name): scene = slicer.mrmlScene node = slicer.vtkMRMLModelNode() node.SetScene(scene) node.SetName(name) node.SetAndObservePolyData(polyData) modelDisplay = slicer.vtkMRMLModelDisplayNode() modelDisplay.SetColor(0, 1, 0) modelDisplay.SetScene(scene) scene.AddNode(modelDisplay) node.SetAndObserveDisplayNodeID(modelDisplay.GetID()) scene.AddNode(node) return node def clearPointsInRecordedModel(self): self.recordedDataBuffer = [] newPoints = vtk.vtkPoints() newVertices = vtk.vtkCellArray() newPolyData = vtk.vtkPolyData() newPolyData.SetPoints(newPoints) newPolyData.SetVerts(newVertices) colorArray = vtk.vtkDoubleArray() colorArray.SetNumberOfComponents(4) colorArray.SetName('Colors') newPolyData.GetPointData().SetScalars(colorArray) self.recordedModelNode.GetPolyData().DeepCopy(newPolyData) self.recordedModelNode.GetPolyData().Modified() def setupPlus(self): connectorNode = self.createPlusConnector() connectorNode.Start() self.plus = Plus(connectorNode.GetID(), self.pathToCreatedSaveDir) def plusStartRecording(self): dateAndTime = time.strftime("_%Y-%m-%d_%H-%M-%S") self.plus.startStopRecording(True, 'PlusRecording' + dateAndTime + '.mha') def plusStopRecording(self): self.plus.startStopRecording(False) def createTransformTree(self): # Get transform nodes from scene if not self.rigidBodyToTrackerTransformNode: self.rigidBodyToTrackerTransformNode = slicer.util.getNode('RigidBodyToTracker') if not self.measurementToMeasurerTransformNode: self.measurementToMeasurerTransformNode = slicer.util.getNode('MeasurementToMeasure') if not self.parametersToMeasurerTransformNode: self.parametersToMeasurerTransformNode = slicer.util.getNode('ParametersToMeasurer') if not self.measurementToMeasurerTransformNode or not self.parametersToMeasurerTransformNode: logging.error('Missing transform! Is Plus properly configured?') self.outputLabels[3].setText('Missing transform! Is PlusServer running and properly configured?') self.outputLabels[3].setStyleSheet(self.errorStyleSheet) return False if not self.rigidBodyToTrackerTransformNode: logging.warning('No tracking transform present, only raw measurement.') self.outputLabels[3].setText(' ') self.outputLabels[3].setStyleSheet(self.defaultStyleSheet) if self.measurementToMeasurerTransformNode: self.measurementPointMarkupsFiducialNode.SetAndObserveTransformNodeID(self.measurementToMeasurerTransformNode.GetID()) if self.rigidBodyToTrackerTransformNode: self.measurementToMeasurerTransformNode.SetAndObserveTransformNodeID(self.rigidBodyToTrackerTransformNode.GetID()) else: self.outputLabels[3].setText('No tracking transform present, only raw measurement.') return True # From: SlicerIGT/GuideletLib # Takes for granted IP:port is localhost:18944 def createPlusConnector(self): connectorNode = slicer.util.getNode('PlusConnector') if not connectorNode: connectorNode = slicer.vtkMRMLIGTLConnectorNode() slicer.mrmlScene.AddNode(connectorNode) connectorNode.SetName('PlusConnector') hostNamePort = "localhost:18944" [hostName, port] = hostNamePort.split(':') connectorNode.SetTypeClient(hostName, int(port)) logging.debug("PlusConnector created") return connectorNode def addUpdateObserver(self): if self.outputObserverTag == -1: self.outputObserverTag = self.rigidBodyToTrackerTransformNode.AddObserver('ModifiedEvent', self.updateSceneCallback) logging.info('addOutputObserver') def removeUpdateObserver(self): if self.outputObserverTag != -1: self.parametersToMeasurerTransformNode.RemoveObserver(self.outputObserverTag) self.outputObserverTag = -1 logging.info('removeOutputObserver') def updateSceneCallback(self, modifiedNode, event=None): # This lowers the update rate of the labels to only every self.LABEL_UPDATE_RATE measurement self.labelUpdateCount = self.labelUpdateCount + 1 updateLabels = not self.labelUpdateCount % self.LABEL_UPDATE_RATE if self.reset: self.clearPointsInRecordedModel() self.reset = False if updateLabels or self.record: # Get ConoProbe parameters self.parametersToMeasurerTransformNode.GetMatrixTransformToParent(self.m) self.d = self.m.GetElement(0, 0) self.snr = self.m.GetElement(0, 1) self.total = self.m.GetElement(0, 2) self.power = self.m.GetElement(1, 1) self.frequency = self.m.GetElement(1, 0) self.lensMaxDistance = self.m.GetElement(2, 0) self.lensMinDistance = self.m.GetElement(2, 1) # Get measurement coordinate self.measurementPointMarkupsFiducialNode.GetNthFiducialWorldCoordinates(0, self.ras) if updateLabels: # Update output labels self.updateOutputLabels() self.labelUpdateCount = 0 if (self.record and self.snr > self.snrThreshold and self.d < self.distanceMaximumValue and self.d > self.distanceMinimumValue): self.acquireSingleMeasurement() def acquireSingleMeasurement(self): # Only for printing tracker position to file xRigidBodyToTracker = 0 yRigidBodyToTracker = 0 zRigidBodyToTracker = 0 Rxx = 0 Rxy = 0 Rxz = 0 Ryx = 0 Ryy = 0 Ryz = 0 Rzx = 0 Rzy = 0 Rzz = 0 if self.rigidBodyToTrackerTransformNode: self.rigidBodyToTrackerTransformNode.GetMatrixTransformToParent(self.m) xRigidBodyToTracker = self.m.GetElement(0, 3) yRigidBodyToTracker = self.m.GetElement(1, 3) zRigidBodyToTracker = self.m.GetElement(2, 3) Rxx = self.m.GetElement(0, 0) Rxy = self.m.GetElement(0, 1) Rxz = self.m.GetElement(0, 2) Ryx = self.m.GetElement(1, 0) Ryy = self.m.GetElement(1, 1) Ryz = self.m.GetElement(1, 2) Rzx = self.m.GetElement(2, 0) Rzy = self.m.GetElement(2, 1) Rzz = self.m.GetElement(2, 2) self.recordedDataBuffer.append([self.ras[0], self.ras[1], self.ras[2], self.d, self.snr, self.total, xRigidBodyToTracker, yRigidBodyToTracker, zRigidBodyToTracker, Rxx, Rxy, Rxz, Ryx, Ryy, Ryz, Rzx, Rzy, Rzz]) self.addPointToPolyData(self.recordedModelNode.GetPolyData(), self.ras) def updateOutputLabels(self): self.outputLabels[0].setText('%.1f' % self.d) self.outputLabels[1].setText('%.0f' % self.snr) self.outputLabels[2].setText('%.0f' % self.total) self.outputLabels[4].setText('[' + '%.1f' % self.ras[0] + ', ' + '%.1f' % self.ras[1] + ', '+ '%.1f' % self.ras[2] + ']') self.outputLabels[5].setText(str(self.recordedModelNode.GetPolyData().GetPoints().GetNumberOfPoints())) self.outputLabels[6].setText('%.2f' % self.stopWatch.getElapsedTime()) self.outputLabels[7].setText('%.0f' % self.power) self.outputLabels[8].setText('%.0f' % self.frequency) self.outputLabels[10].setText('%.1f' % self.lensMaxDistance) self.outputLabels[9].setText('%.1f' % self.lensMinDistance) def addPointToPolyData(self, polyData, ras): pid = vtk.vtkIdList() pid.SetNumberOfIds(1); temp = polyData.GetPoints().InsertNextPoint(ras[0], ras[1], ras[2]) pid.SetId(0, temp) polyData.GetVerts().InsertNextCell(pid) if self.direction > -1: color = [0, 0, 0, 0] tableValue = 1 + (ras[self.direction] - self.min) * self.normalizingConstant if tableValue > 255: tableValue = 255 elif tableValue < 0: tableValue = 0 self.colorTable.GetColor(int(tableValue), color) # 0 -> 255 self.recordedModelNode.GetPolyData().GetPointData().GetScalars('Colors').InsertTuple(self.recordedModelNode.GetPolyData().GetPoints().GetNumberOfPoints() - 1, color) if self.addColours: self.showColouring() self.addColours = False polyData.Modified() def openProbeDialog(self): self.plus.showProbeDialog() def setLayout(self): lm=slicer.app.layoutManager() lm.setLayout(4) # One 3D-view self.resetLayoutFocalPoint(0) self.zoomInThreeDView(0, 8) self.setAxisAndBoxVisibility('View1', False) def setAxisAndBoxVisibility(self, viewName, visible): view = slicer.mrmlScene.GetFirstNodeByName(viewName) view.SetAxisLabelsVisible(visible) view.SetBoxVisible(visible) def resetLayoutFocalPoint(self, viewIdx): threeDWidget = slicer.app.layoutManager().threeDWidget(viewIdx) threeDView = threeDWidget.threeDView() threeDView.resetFocalPoint() def zoomInThreeDView(self, viewIdx, zoomFactor): threeDWidget = slicer.app.layoutManager().threeDWidget(viewIdx) threeDView = threeDWidget.threeDView() for zoom in range(zoomFactor): threeDView.zoomIn() # Available ctkAxes: # - ctk.ctkAxesWidget.Right # - ctk.ctkAxesWidget.Left # - ctk.ctkAxesWidget.Inferior # - ctk.ctkAxesWidget.Superior # - ctk.ctkAxesWidget.Posterior # - ctk.ctkAxesWidget.Anterior def setFocalPointByAxis(self, viewIdx, ctkAxis): view=slicer.app.layoutManager().threeDWidget(viewIdx).threeDView() view.lookFromViewAxis(ctkAxis)