def getTempDirectoryBase(self):
   tempDir = qt.QDir(slicer.app.temporaryPath)
   fileInfo = qt.QFileInfo(qt.QDir(tempDir), "SegmentMesher")
   dirPath = fileInfo.absoluteFilePath()
   qt.QDir().mkpath(dirPath)
   return dirPath
  def createMeshFromSegmentationCleaver(self, inputSegmentation, outputMeshNode, additionalParameters="--scale 0.2 --multiplier 2 --grading 5"):
  
    self.abortRequested = False
    tempDir = self.createTempDirectory()
    self.addLog('Mesh generation using Cleaver is started in working directory: '+tempDir)

    inputParamsCleaver = []
    
    # Write inputs
    qt.QDir().mkpath(tempDir)
    
    labelmapVolumeNode = slicer.mrmlScene.AddNewNodeByClass('vtkMRMLLabelMapVolumeNode')
    slicer.modules.segmentations.logic().ExportAllSegmentsToLabelmapNode(inputSegmentation, labelmapVolumeNode)
    inputLabelmapVolumeFilePath = os.path.join(tempDir, "inputLabelmap.nrrd")
    slicer.util.saveNode(labelmapVolumeNode, inputLabelmapVolumeFilePath, {"useCompression": False})
    inputParamsCleaver.extend(["--input_files", inputLabelmapVolumeFilePath])
    inputParamsCleaver.append("--segmentation")
    
    # Keep IJK to RAS matrix, we'll need it later
    unscaledIjkToRasMatrix = vtk.vtkMatrix4x4()
    labelmapVolumeNode.GetIJKToRASDirectionMatrix(unscaledIjkToRasMatrix)
    origin = labelmapVolumeNode.GetOrigin()
    for i in range(3):
      unscaledIjkToRasMatrix.SetElement(i,3, origin[i])

    # Keep color node, we'll need it later
    colorTableNode = labelmapVolumeNode.GetDisplayNode().GetColorNode()    
    # Background color is transparent by default which is not ideal for 3D display
    colorTableNode.SetColor(0,0.6,0.6,0.6,1.0)
    
    slicer.mrmlScene.RemoveNode(labelmapVolumeNode)
    slicer.mrmlScene.RemoveNode(colorTableNode)
    
    # Set up output format
    
    inputParamsCleaver.extend(["--output_path", tempDir+"/"])
    inputParamsCleaver.extend(["--output_format", "vtkUSG"]) # VTK unstructed grid
    inputParamsCleaver.append("--fix_tet_windup") # prevent inside-out tets
    inputParamsCleaver.append("--strip_exterior") # remove temporary elements that are added to make the volume cubic
    
    inputParamsCleaver.append("--verbose")
    
    # Quality
    inputParamsCleaver.extend(slicer.util.toVTKString(additionalParameters).split(' '))

    # Run Cleaver
    ep = self.startMesher(inputParamsCleaver, self.getCleaverPath())
    self.logProcessOutput(ep, self.cleaverFilename)

    # Read results
    if not self.abortRequested:
      outputVolumetricMeshPath = os.path.join(tempDir, "output.vtk")
      outputReader = vtk.vtkUnstructuredGridReader()
      outputReader.SetFileName(outputVolumetricMeshPath)
      outputReader.ReadAllScalarsOn()
      outputReader.ReadAllVectorsOn()
      outputReader.ReadAllNormalsOn()
      outputReader.ReadAllTensorsOn()
      outputReader.ReadAllColorScalarsOn()
      outputReader.ReadAllTCoordsOn()
      outputReader.ReadAllFieldsOn()
      outputReader.Update()
      
      # Cleaver returns the mesh in voxel coordinates, need to transform to RAS space
      transformer = vtk.vtkTransformFilter()
      transformer.SetInputData(outputReader.GetOutput())
      ijkToRasTransform = vtk.vtkTransform()
      ijkToRasTransform.SetMatrix(unscaledIjkToRasMatrix)
      transformer.SetTransform(ijkToRasTransform)
      
      outputMeshNode.SetUnstructuredGridConnection(transformer.GetOutputPort())
      outputMeshDisplayNode = outputMeshNode.GetDisplayNode()
      if not outputMeshDisplayNode:
        # Initial setup of display node
        outputMeshNode.CreateDefaultDisplayNodes()
        
        outputMeshDisplayNode = outputMeshNode.GetDisplayNode()
        outputMeshDisplayNode.SetEdgeVisibility(True)
        outputMeshDisplayNode.SetClipping(True)

        colorTableNode = slicer.mrmlScene.AddNode(colorTableNode)
        outputMeshDisplayNode.SetAndObserveColorNodeID(colorTableNode.GetID())
        
        outputMeshDisplayNode.ScalarVisibilityOn()
        outputMeshDisplayNode.SetActiveScalarName('labels')
        outputMeshDisplayNode.SetActiveAttributeLocation(vtk.vtkAssignAttribute.CELL_DATA);
        outputMeshDisplayNode.SetSliceIntersectionVisibility(True)
        outputMeshDisplayNode.SetSliceIntersectionOpacity(0.5)
      else:
        currentColorNode = outputMeshDisplayNode.GetColorNode()
        if currentColorNode.GetType() == currentColorNode.User and currentColorNode.IsA("vtkMRMLColorTableNode"):
          # current color table node can be overwritten
          currentColorNode.Copy(colorTableNode)
        else:
          colorTableNode = slicer.mrmlScene.AddNode(colorTableNode)
          outputMeshDisplayNode.SetAndObserveColorNodeID(colorTableNode.GetID())

    # Clean up
    if self.deleteTemporaryFiles:
      import shutil
      shutil.rmtree(tempDir)

    self.addLog("Model generation is completed")
Example #3
0
    def test_Part1DICOM(self,
                        enableScreenshotsFlag=0,
                        screenshotScaleFactor=1):
        """ Test the DICOM part of the test using the head atlas
    """

        self.enableScreenshots = enableScreenshotsFlag
        self.screenshotScaleFactor = screenshotScaleFactor

        import os
        self.delayDisplay("Starting the DICOM test")
        #
        # first, get the data - a zip file of dicom data
        #
        import urllib
        downloads = (('http://slicer.kitware.com/midas3/download?items=124183',
                      'dataset1_Thorax_Abdomen.zip'), )

        self.delayDisplay("Downloading")
        for url, name in downloads:
            filePath = slicer.app.temporaryPath + '/' + name
            if not os.path.exists(filePath) or os.stat(filePath).st_size == 0:
                self.delayDisplay('Requesting download %s from %s...\n' %
                                  (name, url))
                urllib.urlretrieve(url, filePath)
        self.delayDisplay('Finished with download\n')

        self.delayDisplay("Unzipping")
        dicomFilesDirectory = slicer.app.temporaryPath + '/dicomFiles'
        qt.QDir().mkpath(dicomFilesDirectory)
        slicer.app.applicationLogic().Unzip(filePath, dicomFilesDirectory)

        try:
            self.delayDisplay("Switching to temp database directory")
            originalDatabaseDirectory = DICOMUtils.openTemporaryDatabase(
                'tempDICOMDatabase')

            self.delayDisplay('Importing DICOM')
            mainWindow = slicer.util.mainWindow()
            mainWindow.moduleSelector().selectModule('DICOM')

            indexer = ctk.ctkDICOMIndexer()
            indexer.addDirectory(slicer.dicomDatabase, dicomFilesDirectory,
                                 None)
            indexer.waitForImportFinished()

            dicomWidget.detailsPopup.open()

            # load the data by series UID
            dicomWidget.detailsPopup.offerLoadables(
                '1.3.12.2.1107.5.1.4.50025.30000005060811542834300000776',
                'Series')
            dicomWidget.detailsPopup.examineForLoading()

            self.delayDisplay('Loading Selection')
            dicomWidget.detailsPopup.loadCheckedLoadables()

            self.takeScreenshot('LoadingADICOMVolume-Loaded',
                                'Loaded DICOM Volume', -1)

            layoutManager = slicer.app.layoutManager()
            redWidget = layoutManager.sliceWidget('Red')
            slicer.util.clickAndDrag(redWidget, start=(10, 10), end=(10, 40))
            slicer.util.clickAndDrag(redWidget, start=(10, 10), end=(40, 10))

            self.takeScreenshot('LoadingADICOMVolume-WL',
                                'Changed level and window', -1)

            redWidget.sliceController().setSliceLink(True)
            redWidget.sliceController().setSliceVisible(True)
            self.takeScreenshot('LoadingADICOMVolume-LinkView',
                                'Linked and visible', -1)

            slicer.util.clickAndDrag(redWidget,
                                     button='Right',
                                     start=(10, 10),
                                     end=(10, 40))
            self.takeScreenshot('LoadingADICOMVolume-Zoom', 'Zoom', -1)

            threeDView = layoutManager.threeDWidget(0).threeDView()
            slicer.util.clickAndDrag(threeDView)
            self.takeScreenshot('LoadingADICOMVolume-Rotate', 'Rotate', -1)

            threeDView.resetFocalPoint()
            self.takeScreenshot('LoadingADICOMVolume-Center',
                                'Center the view', -1)

            layoutManager.setLayout(slicer.vtkMRMLLayoutNode.
                                    SlicerLayoutConventionalWidescreenView)
            self.takeScreenshot('LoadingADICOMVolume-ConventionalWidescreen',
                                'Conventional Widescreen Layout', -1)

            slicer.util.mainWindow().moduleSelector().selectModule(
                'VolumeRendering')
            self.takeScreenshot('VolumeRendering-Module', 'Volume Rendering',
                                -1)

            volumeRenderingWidgetRep = slicer.modules.volumerendering.widgetRepresentation(
            )
            abdomenVolume = slicer.mrmlScene.GetFirstNodeByName(
                '6: CT_Thorax_Abdomen')
            volumeRenderingWidgetRep.setMRMLVolumeNode(abdomenVolume)
            self.takeScreenshot('VolumeRendering-SelectVolume',
                                'Select the volume 6: CT_Thorax_Abdomen', -1)

            presetsScene = slicer.modules.volumerendering.logic(
            ).GetPresetsScene()
            ctCardiac3 = presetsScene.GetFirstNodeByName('CT-Cardiac3')
            volumeRenderingWidgetRep.applyPreset(ctCardiac3)
            self.takeScreenshot('VolumeRendering-SelectPreset',
                                'Select the Preset CT-Cardiac-3')

            self.delayDisplay('Skipping: Select VTK CPU Ray Casting')

            volumeRenderingNode = slicer.mrmlScene.GetFirstNodeByName(
                'VolumeRendering')
            volumeRenderingNode.SetVisibility(1)
            self.takeScreenshot('VolumeRendering-ViewRendering',
                                'View Volume Rendering', -1)

            self.delayDisplay('Skipping Move the Shift slider')

            redWidget.sliceController().setSliceVisible(False)
            self.takeScreenshot('VolumeRendering-SlicesOff',
                                'Turn off visibility of slices in 3D', -1)

            threeDView = layoutManager.threeDWidget(0).threeDView()
            slicer.util.clickAndDrag(threeDView)
            self.takeScreenshot('VolumeRendering-RotateVolumeRendering',
                                'Rotate volume rendered image', -1)

            volumeRenderingNode.SetVisibility(0)
            self.takeScreenshot('VolumeRendering-TurnOffVolumeRendering',
                                'Turn off volume rendered image', -1)

            volumeRenderingNode.SetCroppingEnabled(1)
            annotationROI = slicer.mrmlScene.GetFirstNodeByName(
                'AnnotationROI')
            annotationROI.SetDisplayVisibility(1)
            self.takeScreenshot('VolumeRendering-DisplayROI',
                                'Enable cropping and display ROI', -1)

            redWidget.sliceController().setSliceVisible(True)
            self.takeScreenshot('VolumeRendering-SlicesOn',
                                'Turn on visibility of slices in 3D', -1)

            annotationROI.SetXYZ(-79.61, 154.16, -232.591)
            annotationROI.SetRadiusXYZ(43.4, 65.19, 70.5)
            self.takeScreenshot('VolumeRendering-SizedROI',
                                'Position the ROI over a kidney', -1)

            volumeRenderingNode.SetVisibility(1)
            self.takeScreenshot('VolumeRendering-ROIRendering',
                                'ROI volume rendered', -1)

            annotationROI.SetXYZ(15, 146, -186)
            annotationROI.SetRadiusXYZ(138, 57, 61)
            self.takeScreenshot('VolumeRendering-BothKidneys',
                                'Rendered both kidneys', -1)

            self.delayDisplay('Test passed!')
        except Exception, e:
            import traceback
            traceback.print_exc()
            self.delayDisplay('Test caused exception!\n' + str(e))
Example #4
0
    def export(self, exportables):
        for exportable in exportables:
            # Get volume node to export
            shNode = slicer.vtkMRMLSubjectHierarchyNode.GetSubjectHierarchyNode(
                slicer.mrmlScene)
            if shNode is None:
                error = "Invalid subject hierarchy"
                logging.error(error)
                return error
            volumeNode = shNode.GetItemDataNode(
                exportable.subjectHierarchyItemID)
            if volumeNode is None or not volumeNode.IsA(
                    'vtkMRMLScalarVolumeNode'):
                error = "Series '" + shNode.GetItemName(
                    exportable.subjectHierarchyItemID) + "' cannot be exported"
                logging.error(error)
                return error

            # Get output directory and create a subdirectory. This is necessary
            # to avoid overwriting the files in case of multiple exportables, as
            # naming of the DICOM files is static
            directoryName = 'ScalarVolume_' + str(
                exportable.subjectHierarchyItemID)
            directoryDir = qt.QDir(exportable.directory)
            directoryDir.mkpath(directoryName)
            directoryDir.cd(directoryName)
            directory = directoryDir.absolutePath()
            logging.info("Export scalar volume '" + volumeNode.GetName() +
                         "' to directory " + directory)

            # Get study and patient items
            studyItemID = shNode.GetItemParent(
                exportable.subjectHierarchyItemID)
            if not studyItemID:
                error = "Unable to get study for series '" + volumeNode.GetName(
                ) + "'"
                logging.error(error)
                return error
            patientItemID = shNode.GetItemParent(studyItemID)
            if not patientItemID:
                error = "Unable to get patient for series '" + volumeNode.GetName(
                ) + "'"
                logging.error(error)
                return error

            # Assemble tags dictionary for volume export
            tags = {}
            tags['Patient Name'] = exportable.tag(
                slicer.vtkMRMLSubjectHierarchyConstants.
                GetDICOMPatientNameTagName())
            tags['Patient ID'] = exportable.tag(
                slicer.vtkMRMLSubjectHierarchyConstants.
                GetDICOMPatientIDTagName())
            tags['Patient Birth Date'] = exportable.tag(
                slicer.vtkMRMLSubjectHierarchyConstants.
                GetDICOMPatientBirthDateTagName())
            tags['Patient Sex'] = exportable.tag(
                slicer.vtkMRMLSubjectHierarchyConstants.
                GetDICOMPatientSexTagName())
            tags['Patient Comments'] = exportable.tag(
                slicer.vtkMRMLSubjectHierarchyConstants.
                GetDICOMPatientCommentsTagName())
            tags['Study ID'] = exportable.tag(
                slicer.vtkMRMLSubjectHierarchyConstants.GetDICOMStudyIDTagName(
                ))
            tags['Study Date'] = exportable.tag(
                slicer.vtkMRMLSubjectHierarchyConstants.
                GetDICOMStudyDateTagName())
            tags['Study Time'] = exportable.tag(
                slicer.vtkMRMLSubjectHierarchyConstants.
                GetDICOMStudyTimeTagName())
            tags['Study Description'] = exportable.tag(
                slicer.vtkMRMLSubjectHierarchyConstants.
                GetDICOMStudyDescriptionTagName())
            tags['Modality'] = exportable.tag('Modality')
            tags['Manufacturer'] = exportable.tag('Manufacturer')
            tags['Model'] = exportable.tag('Model')
            tags['Series Description'] = exportable.tag('SeriesDescription')
            tags['Series Number'] = exportable.tag('SeriesNumber')
            tags['Series Date'] = exportable.tag('SeriesDate')
            tags['Series Time'] = exportable.tag('SeriesTime')
            tags['Content Date'] = exportable.tag('ContentDate')
            tags['Content Time'] = exportable.tag('ContentTime')

            tags['Study Instance UID'] = exportable.tag('StudyInstanceUID')
            tags['Series Instance UID'] = exportable.tag('SeriesInstanceUID')
            tags['Frame of Reference UID'] = exportable.tag(
                'FrameOfReferenceUID')

            # Generate any missing but required UIDs
            if not tags['Study Instance UID']:
                import pydicom as dicom
                tags['Study Instance UID'] = dicom.uid.generate_uid()
            if not tags['Series Instance UID']:
                import pydicom as dicom
                tags['Series Instance UID'] = dicom.uid.generate_uid()
            if not tags['Frame of Reference UID']:
                import pydicom as dicom
                tags['Frame of Reference UID'] = dicom.uid.generate_uid()

            # Use the default Study ID if none is specified
            if not tags['Study ID']:
                tags['Study ID'] = self.defaultStudyID

            # Validate tags
            if tags['Modality'] == "":
                error = "Empty modality for series '" + volumeNode.GetName(
                ) + "'"
                logging.error(error)
                return error

            seriesInstanceUID = tags['Series Instance UID']
            if seriesInstanceUID:
                # Make sure we don't use a series instance UID that already exists (it would mix in more slices into an existing series,
                # which is very unlikely that users would want).
                db = slicer.dicomDatabase
                studyInstanceUID = db.studyForSeries(seriesInstanceUID)
                if studyInstanceUID:
                    # This seriesInstanceUID is already found in the database
                    if len(seriesInstanceUID) > 25:
                        seriesInstanceUID = seriesInstanceUID[:20] + "..."
                    error = f"A series already exists in the database by SeriesInstanceUID {seriesInstanceUID}."
                    logging.error(error)
                    return error

            #TODO: more tag checks

            # Perform export
            exporter = DICOMExportScalarVolume(tags['Study ID'], volumeNode,
                                               tags, directory)
            if not exporter.export():
                return "Creating DICOM files from scalar volume failed. See the application log for details."

        # Success
        return ""
Example #5
0
    def registerVolumes(self,
                        fixedVolumeNode,
                        movingVolumeNode,
                        parameterFilenames=None,
                        outputVolumeNode=None,
                        outputTransformNode=None,
                        fixedVolumeMaskNode=None,
                        movingVolumeMaskNode=None):

        self.abortRequested = False
        tempDir = self.createTempDirectory()
        self.addLog('Volume registration is started in working directory: ' +
                    tempDir)

        # Write inputs
        inputDir = os.path.join(tempDir, 'input')
        qt.QDir().mkpath(inputDir)

        inputParamsElastix = []

        # Add input volumes
        inputVolumes = []
        inputVolumes.append([fixedVolumeNode, 'fixed.mha', '-f'])
        inputVolumes.append([movingVolumeNode, 'moving.mha', '-m'])
        inputVolumes.append([fixedVolumeMaskNode, 'fixedMask.mha', '-fMask'])
        inputVolumes.append([movingVolumeMaskNode, 'movingMask.mha', '-mMask'])
        for [volumeNode, filename, paramName] in inputVolumes:
            if not volumeNode:
                continue
            filePath = os.path.join(inputDir, filename)
            slicer.util.saveNode(volumeNode, filePath,
                                 {"useCompression": False})
            inputParamsElastix.append(paramName)
            inputParamsElastix.append(filePath)

        # Specify output location
        resultTransformDir = os.path.join(tempDir, 'result-transform')
        qt.QDir().mkpath(resultTransformDir)
        inputParamsElastix += ['-out', resultTransformDir]

        # Specify parameter files
        if parameterFilenames == None:
            parameterFilenames = self.getRegistrationPresets(
            )[0][RegistrationPresets_ParameterFilenames]
        for parameterFilename in parameterFilenames:
            inputParamsElastix.append('-p')
            parameterFilePath = os.path.abspath(
                os.path.join(self.registrationParameterFilesDir,
                             parameterFilename))
            inputParamsElastix.append(parameterFilePath)

        # Run registration
        ep = self.startElastix(inputParamsElastix)
        self.logProcessOutput(ep)

        # Resample
        if not self.abortRequested:
            resultResampleDir = os.path.join(tempDir, 'result-resample')
            qt.QDir().mkpath(resultResampleDir)
            inputParamsTransformix = [
                '-in',
                os.path.join(inputDir, 'moving.mha'), '-out', resultResampleDir
            ]
            if outputTransformNode:
                inputParamsTransformix += ['-def', 'all']
            if outputVolumeNode:
                inputParamsTransformix += [
                    '-tp', resultTransformDir + '/TransformParameters.' +
                    str(len(parameterFilenames) - 1) + '.txt'
                ]
            tp = self.startTransformix(inputParamsTransformix)
            self.logProcessOutput(tp)

        # Write results
        if not self.abortRequested:

            if outputVolumeNode:
                outputVolumePath = os.path.join(resultResampleDir,
                                                "result.mhd")
                [success, loadedOutputVolumeNode
                 ] = slicer.util.loadVolume(outputVolumePath, returnNode=True)
                if success:
                    outputVolumeNode.SetAndObserveImageData(
                        loadedOutputVolumeNode.GetImageData())
                    ijkToRas = vtk.vtkMatrix4x4()
                    loadedOutputVolumeNode.GetIJKToRASMatrix(ijkToRas)
                    outputVolumeNode.SetIJKToRASMatrix(ijkToRas)
                    slicer.mrmlScene.RemoveNode(loadedOutputVolumeNode)

            if outputTransformNode:
                outputTransformPath = os.path.join(resultResampleDir,
                                                   "deformationField.mhd")
                [success, loadedOutputTransformNode
                 ] = slicer.util.loadTransform(outputTransformPath,
                                               returnNode=True)
                if success:
                    if loadedOutputTransformNode.GetReadAsTransformToParent():
                        outputTransformNode.SetAndObserveTransformToParent(
                            loadedOutputTransformNode.GetTransformToParent())
                    else:
                        outputTransformNode.SetAndObserveTransformFromParent(
                            loadedOutputTransformNode.GetTransformFromParent())
                    slicer.mrmlScene.RemoveNode(loadedOutputTransformNode)

        # Clean up
        if self.deleteTemporaryFiles:
            import shutil
            shutil.rmtree(tempDir)

        self.addLog("Registration is completed")
Example #6
0
    def export(self, exportables):
        for exportable in exportables:
            # Get volume node to export
            shNode = slicer.vtkMRMLSubjectHierarchyNode.GetSubjectHierarchyNode(
                slicer.mrmlScene)
            if shNode is None:
                error = "Invalid subject hierarchy"
                logging.error(error)
                return error
            volumeNode = shNode.GetItemDataNode(
                exportable.subjectHierarchyItemID)
            if volumeNode is None or not volumeNode.IsA(
                    'vtkMRMLScalarVolumeNode'):
                error = "Series '" + shNode.GetItemName(
                    exportable.subjectHierarchyItemID) + "' cannot be exported"
                logging.error(error)
                return error

            # Get output directory and create a subdirectory. This is necessary
            # to avoid overwriting the files in case of multiple exportables, as
            # naming of the DICOM files is static
            directoryName = 'ScalarVolume_' + str(
                exportable.subjectHierarchyItemID)
            directoryDir = qt.QDir(exportable.directory)
            directoryDir.mkdir(directoryName)
            directoryDir.cd(directoryName)
            directory = directoryDir.absolutePath()
            logging.info("Export scalar volume '" + volumeNode.GetName() +
                         "' to directory " + directory)

            # Get study and patient items
            studyItemID = shNode.GetItemParent(
                exportable.subjectHierarchyItemID)
            if not studyItemID:
                error = "Unable to get study for series '" + volumeNode.GetName(
                ) + "'"
                logging.error(error)
                return error
            patientItemID = shNode.GetItemParent(studyItemID)
            if not patientItemID:
                error = "Unable to get patient for series '" + volumeNode.GetName(
                ) + "'"
                logging.error(error)
                return error

            # Assemble tags dictionary for volume export
            tags = {}
            tags['Patient Name'] = exportable.tag(
                slicer.vtkMRMLSubjectHierarchyConstants.
                GetDICOMPatientNameTagName())
            tags['Patient ID'] = exportable.tag(
                slicer.vtkMRMLSubjectHierarchyConstants.
                GetDICOMPatientIDTagName())
            tags['Patient Birth Date'] = exportable.tag(
                slicer.vtkMRMLSubjectHierarchyConstants.
                GetDICOMPatientBirthDateTagName())
            tags['Patient Sex'] = exportable.tag(
                slicer.vtkMRMLSubjectHierarchyConstants.
                GetDICOMPatientSexTagName())
            tags['Patient Comments'] = exportable.tag(
                slicer.vtkMRMLSubjectHierarchyConstants.
                GetDICOMPatientCommentsTagName())
            tags['Study ID'] = self.defaultStudyID
            tags['Study Date'] = exportable.tag(
                slicer.vtkMRMLSubjectHierarchyConstants.
                GetDICOMStudyDateTagName())
            tags['Study Time'] = exportable.tag(
                slicer.vtkMRMLSubjectHierarchyConstants.
                GetDICOMStudyTimeTagName())
            tags['Study Description'] = exportable.tag(
                slicer.vtkMRMLSubjectHierarchyConstants.
                GetDICOMStudyDescriptionTagName())
            tags['Modality'] = exportable.tag('Modality')
            tags['Manufacturer'] = exportable.tag('Manufacturer')
            tags['Model'] = exportable.tag('Model')
            tags['Series Description'] = exportable.tag('SeriesDescription')
            tags['Series Number'] = exportable.tag('SeriesNumber')
            tags['Series Date'] = exportable.tag('SeriesDate')
            tags['Series Time'] = exportable.tag('SeriesTime')
            tags['Content Date'] = exportable.tag('ContentDate')
            tags['Content Time'] = exportable.tag('ContentTime')

            tags['Study Instance UID'] = exportable.tag('StudyInstanceUID')
            tags['Series Instance UID'] = exportable.tag('SeriesInstanceUID')
            tags['Frame of Reference Instance UID'] = exportable.tag(
                'FrameOfReferenceInstanceUID')

            # Validate tags
            if tags['Modality'] == "":
                error = "Empty modality for series '" + volumeNode.GetName(
                ) + "'"
                logging.error(error)
                return error
            #TODO: more tag checks

            # Perform export
            exporter = DICOMExportScalarVolume(tags['Study ID'], volumeNode,
                                               tags, directory)
            exporter.export()

        # Success
        return ""
Example #7
0
    def loadModules(self, path, depth=1):
        # Get list of modules in specified path
        modules = ModuleInfo.findModules(path, depth)

        # Determine which modules in above are not already loaded
        factory = slicer.app.moduleManager().factoryManager()
        loadedModules = factory.instantiatedModuleNames()

        candidates = [m for m in modules if m.key not in loadedModules]

        # Prompt to load additional module(s)
        if len(candidates):
            dlg = LoadModulesDialog(self.parent.window())
            dlg.setModules(candidates)

            if dlg.exec_() == qt.QDialog.Accepted:
                modulesToLoad = dlg.selectedModules

                # Add module(s) to permanent search paths, if requested
                if dlg.addToSearchPaths:
                    settings = slicer.app.revisionUserSettings()
                    rawSearchPaths = list(
                        _settingsList(settings, "Modules/AdditionalPaths"))
                    searchPaths = [qt.QDir(path) for path in rawSearchPaths]
                    modified = False

                    for module in modulesToLoad:
                        rawPath = os.path.dirname(module.path)
                        path = qt.QDir(rawPath)
                        if not path in searchPaths:
                            searchPaths.append(path)
                            rawSearchPaths.append(rawPath)
                            modified = True

                    if modified:
                        settings.setValue("Modules/AdditionalPaths",
                                          rawSearchPaths)

                # Enable developer mode (shows Reload&Test section, etc.), if requested
                if dlg.enableDeveloperMode:
                    qt.QSettings().setValue('Developer/DeveloperMode', 'true')

                # Register requested module(s)
                failed = []

                for module in modulesToLoad:
                    factory.registerModule(qt.QFileInfo(module.path))
                    if not factory.isRegistered(module.key):
                        failed.append(module)

                if len(failed):

                    if len(failed) > 1:
                        text = "The following modules could not be registered:"
                    else:
                        text = "The '%s' module could not be registered:" % failed[
                            0].key

                    failedFormat = "<ul><li>%(key)s<br/>(%(path)s)</li></ul>"
                    detailedInformation = "".join(
                        [failedFormat % m.__dict__ for m in failed])

                    slicer.util.errorDisplay(
                        text,
                        parent=self.parent.window(),
                        windowTitle="Module loading failed",
                        standardButtons=qt.QMessageBox.Close,
                        informativeText=detailedInformation)

                    return

                # Instantiate and load requested module(s)
                if not factory.loadModules(
                    [module.key for module in modulesToLoad]):
                    text = ("The module factory manager reported an error. "
                            "One or more of the requested module(s) and/or "
                            "dependencies thereof may not have been loaded.")
                    slicer.util.errorDisplay(
                        text,
                        parent=self.parent.window(),
                        windowTitle="Error loading module(s)",
                        standardButtons=qt.QMessageBox.Close)
Example #8
0
  def downloadFromSource(self,source,attemptCount=0):
    """Given an instance of SampleDataSource, downloads the associated data and
    load them into Slicer if it applies.

    The function always returns a list.

    Based on the fileType(s), nodeName(s) and loadFile(s) associated with
    the source, different values may be appended to the returned list:

      - if nodeName is specified, appends loaded nodes but if ``loadFile`` is False appends downloaded filepath
      - if fileType is ``SceneFile``, appends downloaded filepath
      - if fileType is ``ZipFile``, appends directory of extracted archive but if ``loadFile`` is False appends downloaded filepath

    If no ``nodeNames`` and no ``fileTypes`` are specified or if ``loadFiles`` are all False,
    returns the list of all downloaded filepaths.
    """
    nodes = []
    filePaths = []

    for uri,fileName,nodeName,loadFile,loadFileType in zip(source.uris,source.fileNames,source.nodeNames,source.loadFiles,source.loadFileType):

      current_source = SampleDataSource(uris=uri, fileNames=fileName, nodeNames=nodeName, loadFiles=loadFile, loadFileType=loadFileType, loadFileProperties=source.loadFileProperties)
      filePath = self.downloadFileIntoCache(uri, fileName)
      filePaths.append(filePath)

      if loadFileType == 'ZipFile':
        if loadFile == False:
          nodes.append(filePath)
          continue
        outputDir = slicer.mrmlScene.GetCacheManager().GetRemoteCacheDirectory() + "/" + os.path.splitext(os.path.basename(filePath))[0]
        qt.QDir().mkpath(outputDir)
        success = slicer.util.extractArchive(filePath, outputDir)
        if not success and attemptCount < 5:
          attemptCount += 1
          self.logMessage('<b>Load failed! Trying to download again (%d of 5 attempts)...</b>' % (attemptCount), logging.ERROR)
          file = qt.QFile(filePath)
          if not file.remove():
            self.logMessage('<b>Load failed! Unable to delete and try again loading %s!</b>' % filePath, logging.ERROR)
            nodes.append(None)
            break
          outputDir = self.downloadFromSource(current_source,attemptCount)[0]
        nodes.append(outputDir)

      elif loadFileType == 'SceneFile':
        if not loadFile:
          nodes.append(filePath)
          continue
        success = self.loadScene(filePath, source.loadFileProperties)
        if not success and attemptCount < 5:
          attemptCount += 1
          self.logMessage('<b>Load failed! Trying to download again (%d of 5 attempts)...</b>' % (attemptCount), logging.ERROR)
          file = qt.QFile(filePath)
          if not file.remove():
            self.logMessage('<b>Load failed! Unable to delete and try again loading %s!</b>' % filePath, logging.ERROR)
            nodes.append(None)
            break
          filePath = self.downloadFromSource(current_source,attemptCount)[0]
        nodes.append(filePath)

      elif nodeName:
        if loadFile == False:
          nodes.append(filePath)
          continue
        loadedNode = self.loadNode(filePath, nodeName, loadFileType, source.loadFileProperties)
        if loadedNode is None and attemptCount < 5:
          attemptCount += 1
          self.logMessage('<b>Load failed! Trying to download again (%d of 5 attempts)...</b>' % (attemptCount), logging.ERROR)
          file = qt.QFile(filePath)
          if not file.remove():
            self.logMessage('<b>Load failed! Unable to delete and try again loading %s!</b>' % filePath, logging.ERROR)
            loadedNode.append(None)
            break
          loadedNode = self.downloadFromSource(current_source,attemptCount)[0]
        nodes.append(loadedNode)

    if nodes:
      return nodes
    else:
      return filePaths
Example #9
0
    def test_Part1DICOM(self):
        """ Test the DICOM part of the test using the head atlas
    """

        import os
        self.delayDisplay("Starting the DICOM test")
        #
        # first, get the data - a zip file of dicom data
        #
        import urllib
        downloads = (('http://slicer.kitware.com/midas3/download?items=18822',
                      'Dcmtk-db.zip'), )

        self.delayDisplay("Downloading")
        for url, name in downloads:
            filePath = slicer.app.temporaryPath + '/' + name
            if not os.path.exists(filePath) or os.stat(filePath).st_size == 0:
                self.delayDisplay('Requesting download %s from %s...\n' %
                                  (name, url))
                urllib.urlretrieve(url, filePath)
        self.delayDisplay('Finished with download\n')

        self.delayDisplay("Unzipping")
        dicomFilesDirectory = slicer.app.temporaryPath + '/dicomFiles'
        qt.QDir().mkpath(dicomFilesDirectory)
        slicer.app.applicationLogic().Unzip(filePath, dicomFilesDirectory)

        try:
            self.delayDisplay("Switching to temp database directory")
            originalDatabaseDirectory = DICOMUtils.openTemporaryDatabase(
                'tempDICOMDatbase')

            self.delayDisplay('Start Local DICOM Q/R SCP')
            import subprocess
            import os
            configFilePath = dicomFilesDirectory + '/Dcmtk-db/dcmqrscp.cfg'
            processCurrentPath = dicomFilesDirectory + '/Dcmtk-db/'

            dcmqrscpExeOptions = (
                '/bin',
                '/../CTK-build/CMakeExternals/Install/bin',
                '/../DCMTK-install/bin',
                '/../DCMTK-build/bin',
            )

            dcmqrscpExePath = None
            dcmqrscpExeName = '/dcmqrscp'
            if slicer.app.os == 'win':
                dcmqrscpExeName = dcmqrscpExeName + '.exe'
            for path in dcmqrscpExeOptions:
                testPath = slicer.app.slicerHome + path + dcmqrscpExeName
                if os.path.exists(testPath):
                    dcmqrscpExePath = testPath
                    break
            if not dcmqrscpExePath:
                raise (UserWarning("Could not find dcmqrscp executable"))

            args = (dcmqrscpExePath, '-c', configFilePath)
            popen = subprocess.Popen(args,
                                     stdout=subprocess.PIPE,
                                     cwd=processCurrentPath)

            self.delayDisplay('Retrieve DICOM')
            mainWindow = slicer.util.mainWindow()
            mainWindow.moduleSelector().selectModule('DICOM')
            dicomRetrieve = ctk.ctkDICOMRetrieve()
            dicomRetrieve.setKeepAssociationOpen(True)
            dicomRetrieve.setDatabase(slicer.dicomDatabase)
            dicomRetrieve.setCallingAETitle('SlicerAE')
            dicomRetrieve.setCalledAETitle('DCMTK')
            dicomRetrieve.setPort(12345)
            dicomRetrieve.setHost('localhost')
            dicomRetrieve.getStudy(
                '1.2.124.113932.1.170.223.162.178.20050502.160340.12640015')
            popen.kill()
            dicomWidget.detailsPopup.open()
            # click on the first row of the tree
            index = dicomWidget.tree.indexAt(qt.QPoint(0, 0))
            dicomWidget.onTreeClicked(index)

            self.delayDisplay('Loading Selection')
            dicomWidget.detailsPopup.loadCheckedLoadables()

            self.delayDisplay('Change Level')
            layoutManager = slicer.app.layoutManager()
            redWidget = layoutManager.sliceWidget('Red')
            slicer.util.clickAndDrag(redWidget, start=(10, 10), end=(10, 40))

            self.delayDisplay('Change Window')
            slicer.util.clickAndDrag(redWidget, start=(10, 10), end=(40, 10))

            self.delayDisplay('Change Layout')
            layoutManager = slicer.app.layoutManager()
            layoutManager.setLayout(
                slicer.vtkMRMLLayoutNode.SlicerLayoutOneUpRedSliceView)

            self.delayDisplay('Zoom')
            slicer.util.clickAndDrag(redWidget,
                                     button='Right',
                                     start=(10, 10),
                                     end=(10, 40))

            self.delayDisplay('Pan')
            slicer.util.clickAndDrag(redWidget,
                                     button='Middle',
                                     start=(10, 10),
                                     end=(40, 40))

            self.delayDisplay('Center')
            redWidget.sliceController().fitSliceToBackground()

            self.delayDisplay('Lightbox')
            redWidget.sliceController().setLightboxTo6x6()

            self.delayDisplay('Conventional Layout')
            layoutManager.setLayout(
                slicer.vtkMRMLLayoutNode.SlicerLayoutConventionalView)

            self.delayDisplay('No Lightbox')
            redWidget.sliceController().setLightboxTo1x1()

            self.delayDisplay('Four Up Layout')
            layoutManager.setLayout(
                slicer.vtkMRMLLayoutNode.SlicerLayoutFourUpView)

            self.delayDisplay('Shift Mouse')
            slicer.util.clickAndDrag(redWidget,
                                     button='None',
                                     start=(100, 100),
                                     end=(140, 140),
                                     modifiers=['Shift'])

            self.delayDisplay('Conventional, Link, Slice Model')
            layoutManager.setLayout(
                slicer.vtkMRMLLayoutNode.SlicerLayoutConventionalView)
            redWidget.sliceController().setSliceLink(True)
            redWidget.sliceController().setSliceVisible(True)

            self.delayDisplay('Rotate')
            threeDView = layoutManager.threeDWidget(0).threeDView()
            slicer.util.clickAndDrag(threeDView)

            self.delayDisplay('Zoom')
            threeDView = layoutManager.threeDWidget(0).threeDView()
            slicer.util.clickAndDrag(threeDView, button='Right')

            self.delayDisplay('Test passed!')
        except Exception, e:
            import traceback
            traceback.print_exc()
            self.delayDisplay('Test caused exception!\n' + str(e))
Example #10
0
    def test_Part3PETCT(self,
                        enableScreenshotsFlag=0,
                        screenshotScaleFactor=1):
        """ Test using the PETCT module
    """

        self.enableScreenshots = enableScreenshotsFlag
        self.screenshotScaleFactor = screenshotScaleFactor

        self.delayDisplay("Starting the test")

        #
        # first, get some data
        #
        import urllib
        downloads = (('http://slicer.kitware.com/midas3/download?items=124185',
                      'dataset3_PETCT.zip'), )

        self.delayDisplay("Downloading")

        for url, name in downloads:
            filePath = slicer.app.temporaryPath + '/' + name
            if not os.path.exists(filePath) or os.stat(filePath).st_size == 0:
                print('Requesting download %s from %s...\n' % (name, url))
                urllib.urlretrieve(url, filePath)
        self.delayDisplay('Finished with download\n')

        self.delayDisplay("Unzipping to  %s" % (slicer.app.temporaryPath))
        zipFilePath = slicer.app.temporaryPath + '/' + 'dataset3_PETCT.zip'
        extractPath = slicer.app.temporaryPath + '/' + 'dataset3_PETCT'
        qt.QDir().mkpath(extractPath)
        self.delayDisplay("Using extract path  %s" % (extractPath))
        applicationLogic = slicer.app.applicationLogic()
        applicationLogic.Unzip(zipFilePath, extractPath)

        self.delayDisplay("Loading PET_CT_pre-treatment.mrb")
        preTreatmentPath = extractPath + '/PET_CT_pre-treatment.mrb'
        slicer.util.loadScene(preTreatmentPath)
        self.takeScreenshot('PETCT-LoadedPre', 'Loaded pre-treatement scene',
                            -1)

        try:
            logic = RSNAQuantTutorialLogic()
            mainWindow = slicer.util.mainWindow()
            layoutManager = slicer.app.layoutManager()
            threeDView = layoutManager.threeDWidget(0).threeDView()
            redWidget = layoutManager.sliceWidget('Red')
            redController = redWidget.sliceController()
            greenWidget = layoutManager.sliceWidget('Green')
            greenController = greenWidget.sliceController()
            yellowWidget = layoutManager.sliceWidget('Yellow')
            yellowController = yellowWidget.sliceController()
            viewNode = threeDView.mrmlViewNode()
            cameras = slicer.util.getNodes('vtkMRMLCameraNode*')
            for cameraNode in cameras.values():
                if cameraNode.GetActiveTag() == viewNode.GetID():
                    break

            threeDView.resetFocalPoint()
            slicer.util.clickAndDrag(threeDView, button='Right')
            redWidget.sliceController().setSliceVisible(True)
            yellowWidget.sliceController().setSliceVisible(True)
            self.takeScreenshot('PETCT-ConfigureView', 'Configure View', -1)

            mainWindow.moduleSelector().selectModule('Volumes')
            compositNode = redWidget.mrmlSliceCompositeNode()
            compositNode.SetForegroundOpacity(0.2)
            self.takeScreenshot('PETCT-ShowVolumes',
                                'Show Volumes with lesion', -1)

            compositNode.SetForegroundOpacity(0.5)
            self.takeScreenshot('PETCT-CTOpacity', 'CT1 volume opacity to 0.5',
                                -1)

            yellowWidget.sliceController().setSliceVisible(False)
            greenWidget.sliceController().setSliceVisible(True)
            self.takeScreenshot('PETCT-ShowSlices',
                                'Show axial and sagittal slices', -1)

            self.delayDisplay('SUV Computation')
            if not hasattr(slicer.modules,
                           'petstandarduptakevaluecomputation'):
                self.delayDisplay(
                    "PET SUV Computation not available, skipping the test.")
                return

            slicer.util.selectModule('PETStandardUptakeValueComputation')

            parameters = {
                "PETDICOMPath": extractPath + '/' + 'PET1',
                "PETVolume": slicer.util.getNode('PET1'),
                "VOIVolume": slicer.util.getNode('PET1-label'),
            }

            suvComputation = slicer.modules.petstandarduptakevaluecomputation
            self.CLINode1 = None
            self.CLINode1 = slicer.cli.run(suvComputation,
                                           self.CLINode1,
                                           parameters,
                                           delete_temporary_files=False)
            waitCount = 0
            while self.CLINode1.GetStatusString(
            ) != 'Completed' and waitCount < 100:
                self.delayDisplay("Running SUV Computation... %d" % waitCount)
                waitCount += 1

            # close the scene
            slicer.mrmlScene.Clear(0)

            self.delayDisplay("Loading PET_CT_post-treatment.mrb")
            postTreatmentPath = extractPath + '/PET_CT_post-treatment.mrb'
            slicer.util.loadScene(postTreatmentPath)
            self.takeScreenshot('PETCT-LoadedPost',
                                'Loaded post-treatement scene', -1)

            compositNode.SetForegroundOpacity(0.5)
            self.takeScreenshot('PETCT-CT2Opacity',
                                'CT2 volume opacity to 0.5', -1)

            redController.setSliceOffsetValue(-165.01)
            self.takeScreenshot('PETCT-LarynxUptake',
                                'Mild uptake in the larynx and pharynx', -1)

            redController.setSliceOffsetValue(-106.15)
            self.takeScreenshot('PETCT-TumorUptake',
                                'No uptake in the area of the primary tumor',
                                -1)

            self.delayDisplay('Test passed!')
        except Exception, e:
            import traceback
            traceback.print_exc()
            self.delayDisplay('Test caused exception!\n' + str(e))
Example #11
0
    def onStartStopDicomPeer(self, flag):
        if flag:
            import os
            self.startStopDicomPeerButton.setEnabled(False)
            dicomFilesDirectory = slicer.app.temporaryPath
            configFilePath = dicomFilesDirectory + '/Dcmtk-db/dcmqrscp.cfg'
            processCurrentPath = dicomFilesDirectory + '/Dcmtk-db/'

            if slicer.util.confirmYesNoDisplay(
                    'Do you want to choose local DCMTK database folder?'):
                print 'Yes'
                dicomFilesDirectory = qt.QFileDialog.getExistingDirectory(
                    None, 'Select DCMTK database folder')
                configFilePath = dicomFilesDirectory + '/dcmqrscp.cfg'
                processCurrentPath = dicomFilesDirectory
            else:
                downloads = (
                    ('http://slicer.kitware.com/midas3/download?items=18822',
                     'Dcmtk-db.zip'), )
                print 'Downloading'

                import urllib
                for url, name in downloads:
                    filePath = slicer.app.temporaryPath + '/' + name
                    if not os.path.exists(filePath) or os.stat(
                            filePath).st_size == 0:
                        print 'Requesting download %s from %s...\n' % (name,
                                                                       url)
                        urllib.urlretrieve(url, filePath)
                print 'Finished with download'

                print 'Unzipping'
                qt.QDir().mkpath(dicomFilesDirectory)
                slicer.app.applicationLogic().Unzip(filePath,
                                                    dicomFilesDirectory)

            import subprocess
            dcmqrscpExeOptions = (
                '/bin',
                '/../CTK-build/CMakeExternals/Install/bin',
                '/../DCMTK-install/bin',
                '/../DCMTK-build/bin',
            )

            dcmqrscpExePath = None
            dcmqrscpExeName = '/dcmqrscp'
            if slicer.app.os == 'win':
                dcmqrscpExeName = dcmqrscpExeName + '.exe'
            for path in dcmqrscpExeOptions:
                testPath = slicer.app.slicerHome + path + dcmqrscpExeName
                if os.path.exists(testPath):
                    dcmqrscpExePath = testPath
                    break
            if not dcmqrscpExePath:
                raise (UserWarning("Could not find dcmqrscp executable"))

            args = (dcmqrscpExePath, '-c', configFilePath)
            print 'Start DICOM peer'
            self.popen = subprocess.Popen(args,
                                          stdout=subprocess.PIPE,
                                          cwd=processCurrentPath)
            self.startStopDicomPeerButton.setEnabled(True)
        else:
            print 'Stop DICOM peer'
            self.popen.kill()
Example #12
0
    def test_AlternateReaders(self):
        """ Test the DICOM loading of sample testing data
    """
        testPass = True
        import os, json
        self.delayDisplay("Starting the DICOM test", 100)

        referenceData = json.JSONDecoder().decode('''[
      { "url": "http://slicer.kitware.com/midas3/download/item/292839/Mouse-MR-example-where-GDCM_fails.zip",
        "fileName": "Mouse-MR-example-where-GDCM_fails.zip",
        "name": "Mouse-MR-example-where-GDCM_fails",
        "seriesUID": "1.3.6.1.4.1.9590.100.1.2.366426457713813178933224342280246227461",
        "expectedFailures": ["GDCM", "Archetype"],
        "voxelValueQuantity": "(110852, DCM, \\"MR signal intensity\\")",
        "voxelValueUnits": "(1, UCUM, \\"no units\\")"
      },
      { "url": "http://slicer.kitware.com/midas3/download/item/294857/deidentifiedMRHead-dcm-one-series.zip",
        "fileName": "deidentifiedMRHead-dcm-one-series.zip",
        "name": "deidentifiedMRHead-dcm-one-series",
        "seriesUID": "1.3.6.1.4.1.5962.99.1.3814087073.479799962.1489872804257.270.0",
        "expectedFailures": [],
        "voxelValueQuantity": "(110852, DCM, \\"MR signal intensity\\")",
        "voxelValueUnits": "(1, UCUM, \\"no units\\")"
      }
    ]''')

        # another dataset that could be added in the future - currently fails for all readers
        # due to invalid format - see https://issues.slicer.org/view.php?id=3569
        #{ "url": "http://slicer.kitware.com/midas3/download/item/293587/RIDER_bug.zip",
        #"fileName": "RIDER_bug.zip",
        #"name": "RIDER_bug",
        #"seriesUID": "1.3.6.1.4.1.9328.50.7.261772317324041365541450388603508531852",
        #"expectedFailures": []
        #}

        loadingResult = {}
        #
        # first, get the data - a zip file of dicom data
        #
        self.delayDisplay("Downloading", 100)
        for dataset in referenceData:
            try:
                filePath = slicer.app.temporaryPath + '/' + dataset['fileName']
                if not os.path.exists(filePath) or os.stat(
                        filePath).st_size == 0:
                    self.delayDisplay(
                        'Requesting download %s from %s...\n' %
                        (dataset['fileName'], dataset['url']), 100)
                    urllib.urlretrieve(dataset['url'], filePath)
                self.delayDisplay('Finished with download\n', 100)

                self.delayDisplay("Unzipping", 100)
                dicomFilesDirectory = slicer.app.temporaryPath + dataset['name']
                qt.QDir().mkpath(dicomFilesDirectory)
                slicer.app.applicationLogic().Unzip(filePath,
                                                    dicomFilesDirectory)

                #
                # insert the data into th database
                #
                self.delayDisplay("Switching to temp database directory", 100)
                originalDatabaseDirectory = DICOMUtils.openTemporaryDatabase(
                    'tempDICOMDatabase')

                self.delayDisplay('Importing DICOM', 100)
                mainWindow = slicer.util.mainWindow()
                mainWindow.moduleSelector().selectModule('DICOM')

                indexer = ctk.ctkDICOMIndexer()
                indexer.addDirectory(slicer.dicomDatabase, dicomFilesDirectory,
                                     None)
                indexer.waitForImportFinished()

                #
                # select the series
                #
                detailsPopup = slicer.modules.DICOMWidget.detailsPopup
                detailsPopup.open()
                # load the data by series UID
                detailsPopup.offerLoadables(dataset['seriesUID'], 'Series')
                detailsPopup.examineForLoading()
                loadable = detailsPopup.getAllSelectedLoadables().keys()[0]

                #
                # try loading using each of the selected readers, fail
                # on enexpected load issue
                #
                scalarVolumePlugin = slicer.modules.dicomPlugins[
                    'DICOMScalarVolumePlugin']()
                readerApproaches = scalarVolumePlugin.readerApproaches()
                basename = loadable.name
                volumesByApproach = {}
                for readerApproach in readerApproaches:
                    self.delayDisplay(
                        'Loading Selection with approach: %s' % readerApproach,
                        100)
                    loadable.name = basename + "-" + readerApproach
                    volumeNode = scalarVolumePlugin.load(
                        loadable, readerApproach)
                    if not volumeNode and readerApproach not in dataset[
                            'expectedFailures']:
                        raise Exception(
                            "Expected to be able to read with %s, but couldn't"
                            % readerApproach)
                    if volumeNode and readerApproach in dataset[
                            'expectedFailures']:
                        raise Exception(
                            "Expected to NOT be able to read with %s, but could!"
                            % readerApproach)
                    if volumeNode:
                        volumesByApproach[readerApproach] = volumeNode

                        self.delayDisplay('Test quantity and unit')
                        if 'voxelValueQuantity' in dataset.keys():
                            self.assertEqual(
                                volumeNode.GetVoxelValueQuantity(
                                ).GetAsPrintableString(),
                                dataset['voxelValueQuantity'])
                        if 'voxelValueUnits' in dataset.keys():
                            self.assertEqual(
                                volumeNode.GetVoxelValueUnits(
                                ).GetAsPrintableString(),
                                dataset['voxelValueUnits'])

                #
                # for each approach that loaded as expected, compare the volumes
                # to ensure they match in terms of pixel data and metadata
                #
                failedComparisons = {}
                approachesThatLoaded = volumesByApproach.keys()
                print('approachesThatLoaded %s' % approachesThatLoaded)
                for approachIndex in range(len(approachesThatLoaded)):
                    firstApproach = approachesThatLoaded[approachIndex]
                    firstVolume = volumesByApproach[firstApproach]
                    for secondApproachIndex in range(
                            approachIndex + 1, len(approachesThatLoaded)):
                        secondApproach = approachesThatLoaded[
                            secondApproachIndex]
                        secondVolume = volumesByApproach[secondApproach]
                        print('comparing  %s,%s' %
                              (firstApproach, secondApproach))
                        comparison = slicer.modules.dicomPlugins[
                            'DICOMScalarVolumePlugin'].compareVolumeNodes(
                                firstVolume, secondVolume)
                        if comparison != "":
                            print('failed: %s', comparison)
                            failedComparisons[firstApproach,
                                              secondApproach] = comparison

                if len(failedComparisons.keys()) > 0:
                    raise Exception("Loaded volumes don't match: %s" %
                                    failedComparisons)

                self.delayDisplay('%s Test passed!' % dataset['name'], 200)

            except Exception, e:
                import traceback
                traceback.print_exc()
                self.delayDisplay(
                    '%s Test caused exception!\n' % dataset['name'] + str(e),
                    2000)
                testPass = False
Example #13
0
    def test_MissingSlices(self):
        """ Test behavior of the readers when slices are missing

    To edit and run this test from the python console, paste this below:

reloadScriptedModule('DICOMReaders'); import DICOMReaders; tester = DICOMReaders.DICOMReadersTest(); tester.setUp(); tester.test_MissingSlices()

    """
        testPass = True
        import os, json
        self.delayDisplay("Starting the DICOM test", 100)

        datasetURL = "http://slicer.kitware.com/midas3/download/item/294857/deidentifiedMRHead-dcm-one-series.zip"
        fileName = "deidentifiedMRHead-dcm-one-series.zip"
        filePath = os.path.join(slicer.app.temporaryPath, fileName)
        seriesUID = "1.3.6.1.4.1.5962.99.1.3814087073.479799962.1489872804257.270.0"
        seriesRASBounds = [
            -87.29489517211913, 81.70450973510744, -121.57139587402344,
            134.42860412597656, -138.71430206298828, 117.28569793701172
        ]
        seriesDirectory = "Series 004 [MR - SAG RF FAST VOL FLIP 20]"
        lastSliceCorners = [[[81.05451202, 133.92860413, 116.78569794],
                             [81.05451202, -122.07139587, 116.78569794]],
                            [[81.05451202, 133.92860413, -139.21429443],
                             [81.05451202, -122.07139587, -139.21429443]]]
        filesToRemove = [
            "1.3.6.1.4.1.5962.99.1.3814087073.479799962.1489872804257.361.0.dcm",
            "1.3.6.1.4.1.5962.99.1.3814087073.479799962.1489872804257.362.0.dcm",
            "1.3.6.1.4.1.5962.99.1.3814087073.479799962.1489872804257.363.0.dcm",
            "1.3.6.1.4.1.5962.99.1.3814087073.479799962.1489872804257.364.0.dcm",
            "1.3.6.1.4.1.5962.99.1.3814087073.479799962.1489872804257.365.0.dcm",
            "1.3.6.1.4.1.5962.99.1.3814087073.479799962.1489872804257.366.0.dcm",
            "1.3.6.1.4.1.5962.99.1.3814087073.479799962.1489872804257.367.0.dcm",
            "1.3.6.1.4.1.5962.99.1.3814087073.479799962.1489872804257.368.0.dcm",
            "1.3.6.1.4.1.5962.99.1.3814087073.479799962.1489872804257.369.0.dcm",
            "1.3.6.1.4.1.5962.99.1.3814087073.479799962.1489872804257.370.0.dcm",
            "1.3.6.1.4.1.5962.99.1.3814087073.479799962.1489872804257.371.0.dcm",
            "1.3.6.1.4.1.5962.99.1.3814087073.479799962.1489872804257.372.0.dcm",
            "1.3.6.1.4.1.5962.99.1.3814087073.479799962.1489872804257.373.0.dcm",
            "1.3.6.1.4.1.5962.99.1.3814087073.479799962.1489872804257.374.0.dcm",
            "1.3.6.1.4.1.5962.99.1.3814087073.479799962.1489872804257.375.0.dcm",
            "1.3.6.1.4.1.5962.99.1.3814087073.479799962.1489872804257.376.0.dcm",
        ]

        try:
            if not os.path.exists(filePath) or os.stat(filePath).st_size == 0:
                self.delayDisplay(
                    'Requesting download %s from %s...\n' %
                    (fileName, datasetURL), 100)
                urllib.urlretrieve(datasetURL, filePath)
            self.delayDisplay('Finished with download\n', 100)

            self.delayDisplay("Unzipping", 100)
            dicomFilesDirectory = slicer.app.temporaryPath + 'MRhead'
            qt.QDir().mkpath(dicomFilesDirectory)
            slicer.app.applicationLogic().Unzip(filePath, dicomFilesDirectory)

            print('Removing %d files from the middle of the series' %
                  len(filesToRemove))
            for file in filesToRemove:
                filePath = os.path.join(dicomFilesDirectory, seriesDirectory,
                                        file)
                os.remove(filePath)

            #
            # insert the data into the database
            #
            self.delayDisplay("Switching to temp database directory", 100)
            originalDatabaseDirectory = DICOMUtils.openTemporaryDatabase(
                'tempDICOMDatabase')

            self.delayDisplay('Importing DICOM', 100)
            mainWindow = slicer.util.mainWindow()
            mainWindow.moduleSelector().selectModule('DICOM')

            indexer = ctk.ctkDICOMIndexer()
            indexer.addDirectory(slicer.dicomDatabase, dicomFilesDirectory,
                                 None)
            indexer.waitForImportFinished()

            #
            # select the series
            #
            detailsPopup = slicer.modules.DICOMWidget.detailsPopup
            detailsPopup.open()
            # load the data by series UID
            detailsPopup.offerLoadables(seriesUID, 'Series')
            detailsPopup.examineForLoading()
            loadable = detailsPopup.getAllSelectedLoadables().keys()[0]

            if len(loadable.warning) == 0:
                raise Exception(
                    "Expected warning about geometry issues due to missing slices!"
                )

            #
            # load and correct for acquisition then check the geometry
            #
            scalarVolumePlugin = slicer.modules.dicomPlugins[
                'DICOMScalarVolumePlugin']()
            volumeNode = scalarVolumePlugin.load(loadable)

            if not numpy.allclose(
                    scalarVolumePlugin.acquisitionModeling.fixedCorners[-1],
                    lastSliceCorners):
                raise Exception(
                    "Acquisition transform didn't fix slice corners!")

            self.delayDisplay('test_MissingSlices passed!', 200)

        except Exception, e:
            import traceback
            traceback.print_exc()
            self.delayDisplay(
                'Missing Slices Test caused exception!\n' + str(e), 2000)
            testPass = False
Example #14
0
    def test_SEGExporterSelfTest1(self):
        """ Test DICOM import, segmentation, export
    """
        self.messageDelay = 50

        import os
        self.delayDisplay("Starting the DICOM SEG Export test")

        #
        # first, get the data - a zip file of dicom data
        #
        import urllib
        downloads = ((
            'http://slicer.kitware.com/midas3/download/item/220834/PieperMRHead.zip',
            'PieperMRHead.zip'), )
        self.delayDisplay("Downloading")
        for url, name in downloads:
            filePath = slicer.app.temporaryPath + '/' + name
            if not os.path.exists(filePath) or os.stat(filePath).st_size == 0:
                self.delayDisplay('Requesting download %s from %s...\n' %
                                  (name, url))
                urllib.urlretrieve(url, filePath)
        self.delayDisplay('Finished with download\n')

        self.delayDisplay("Unzipping")
        dicomFilesDirectory = slicer.app.temporaryPath + '/dicomFiles'
        qt.QDir().mkpath(dicomFilesDirectory)
        slicer.app.applicationLogic().Unzip(filePath, dicomFilesDirectory)

        try:
            self.delayDisplay("Switching to temp database directory")
            tempDatabaseDirectory = slicer.app.temporaryPath + '/tempDICOMDatabase'
            import shutil
            shutil.rmtree(tempDatabaseDirectory)
            qt.QDir().mkpath(tempDatabaseDirectory)
            if slicer.dicomDatabase:
                originalDatabaseDirectory = os.path.split(
                    slicer.dicomDatabase.databaseFilename)[0]
            else:
                originalDatabaseDirectory = None
                settings = qt.QSettings()
                settings.setValue('DatabaseDirectory', tempDatabaseDirectory)
            dicomWidget = slicer.modules.dicom.widgetRepresentation().self()
            dicomWidget.onDatabaseDirectoryChanged(tempDatabaseDirectory)

            self.delayDisplay('Importing DICOM')
            mainWindow = slicer.util.mainWindow()
            mainWindow.moduleSelector().selectModule('DICOM')

            indexer = ctk.ctkDICOMIndexer()
            indexer.addDirectory(slicer.dicomDatabase, dicomFilesDirectory,
                                 None)
            indexer.waitForImportFinished()

            dicomWidget.detailsPopup.open()

            # load the data by series UID
            mrHeadSeriesUID = "2.16.840.1.113662.4.4168496325.1025306170.548651188813145058"
            dicomWidget.detailsPopup.offerLoadables(mrHeadSeriesUID, 'Series')
            dicomWidget.detailsPopup.examineForLoading()
            self.delayDisplay('Loading Selection')
            dicomWidget.detailsPopup.loadCheckedLoadables()

            #
            # create a label map and set it for editing
            #
            masterNode = slicer.util.getNode('2: SAG*')
            volumesLogic = slicer.modules.volumes.logic()
            mergeNode = volumesLogic.CreateAndAddLabelVolume(
                slicer.mrmlScene, masterNode,
                masterNode.GetName() + '-label')
            mergeNode.GetDisplayNode().SetAndObserveColorNodeID(
                'vtkMRMLColorTableNodeFileGenericAnatomyColors.txt')
            selectionNode = slicer.app.applicationLogic().GetSelectionNode()
            selectionNode.SetReferenceActiveVolumeID(masterNode.GetID())
            selectionNode.SetReferenceActiveLabelVolumeID(mergeNode.GetID())
            slicer.app.applicationLogic().PropagateVolumeSelection(0)

            #
            # go to the editor and do some drawing
            #
            slicer.util.selectModule('Editor')

            import EditorLib
            from EditorLib.EditUtil import EditUtil
            parameterNode = EditUtil.getParameterNode()
            parameterNode.SetParameter("LabelEffect,paintThreshold", "1")
            parameterNode.SetParameter("LabelEffect,paintThresholdMin", "70.0")
            parameterNode.SetParameter("LabelEffect,paintThresholdMax",
                                       "279.75")
            parameterNode.SetParameter("PaintEffect,radius", "40")
            parameterNode.SetParameter("PaintEffect,sphere", "1")

            self.delayDisplay("Paint some things")
            parameterNode = EditUtil.getParameterNode()
            lm = slicer.app.layoutManager()
            paintEffect = EditorLib.PaintEffectOptions()
            paintEffect.setMRMLDefaults()
            paintEffect.__del__()
            sliceWidget = lm.sliceWidget('Red')
            paintTool = EditorLib.PaintEffectTool(sliceWidget)
            EditUtil.setLabel(1)
            paintTool.paintAddPoint(100, 100)
            paintTool.paintApply()
            EditUtil.setLabel(2)
            paintTool.paintAddPoint(200, 200)
            paintTool.paintApply()
            paintTool.cleanup()
            paintTool = None

            # save these to compare with the one we read back
            originalSegmentationArray = slicer.util.array(mergeNode.GetID())
            originalSegmentationNodeCopy = slicer.vtkMRMLLabelMapVolumeNode()
            originalSegmentationNodeCopy.CopyOrientation(mergeNode)

            # export the volumes into a SEG
            tempSEGDirectory = slicer.app.temporaryPath + '/tempDICOMSEG'
            qt.QDir().mkpath(tempSEGDirectory)
            segFilePath = os.path.join(tempSEGDirectory, "test.SEG.dcm")

            self.delayDisplay('spliting...', 200)
            EditUtil.splitPerStructureVolumes(masterNode, mergeNode)

            self.delayDisplay('exporting...', 200)
            EditUtil.exportAsDICOMSEG(masterNode)

            # close scene re-load the input data and SEG
            slicer.mrmlScene.Clear(0)
            indexer.addDirectory(slicer.dicomDatabase, tempSEGDirectory, None)
            indexer.waitForImportFinished()

            mrHeadStudyUID = "2.16.840.1.113662.4.4168496325.1025305873.7118351817185979330"
            dicomWidget.detailsPopup.offerLoadables(mrHeadStudyUID, 'Study')
            dicomWidget.detailsPopup.examineForLoading()
            self.delayDisplay('Loading Selection')
            dicomWidget.detailsPopup.loadCheckedLoadables()

            # confirm that segmentations are correctly reloaded
            headLabelName = '2: SAG/RF-FAST/VOL/FLIP 30-label'
            reloadedLabel = slicer.util.getNode(headLabelName)
            reloadedSegmentationArray = slicer.util.array(
                reloadedLabel.GetID())

            import numpy
            self.assertTrue(
                numpy.alltrue(
                    originalSegmentationArray == reloadedSegmentationArray))
            geometryWarnings = volumesLogic.CompareVolumeGeometry(
                mergeNode, reloadedLabel)
            print(geometryWarnings)
            self.assertTrue(geometryWarnings == '')

            # re-export

            # close scene re-load the input data and SEG

            # confirm that segmentations are available again as per-structure volumes

            self.delayDisplay('Test passed!')
        except Exception, e:
            import traceback
            traceback.print_exc()
            self.delayDisplay('Test caused exception!\n' + str(e))
Example #15
0
    def export(self, exportables):
        for exportable in exportables:
            # Get node to export
            node = slicer.mrmlScene.GetNodeByID(exportable.nodeID)
            if node.GetAssociatedNode() is None or not node.GetAssociatedNode(
            ).IsA('vtkMRMLScalarVolumeNode'):
                error = "Series '" + node.GetNameWithoutPostfix(
                ) + "' cannot be exported!"
                logging.error(error)
                return error

            # Get output directory and create a subdirectory. This is necessary
            # to avoid overwriting the files in case of multiple exportables, as
            # naming of the DICOM files is static
            directoryDir = qt.QDir(exportable.directory)
            directoryDir.mkdir(exportable.nodeID)
            directoryDir.cd(exportable.nodeID)
            directory = directoryDir.absolutePath()
            logging.info("Export scalar volume '" +
                         node.GetAssociatedNode().GetName() +
                         "' to directory " + directory)

            # Get study and patient nodes
            studyNode = node.GetParentNode()
            if studyNode is None:
                error = "Unable to get study node for series '" + node.GetAssociatedNode(
                ).GetName() + "'"
                logging.error(error)
                return error
            patientNode = studyNode.GetParentNode()
            if patientNode is None:
                error = "Unable to get patient node for series '" + node.GetAssociatedNode(
                ).GetName() + "'"
                logging.error(error)
                return error

            # Assemble tags dictionary for volume export
            from vtkSlicerSubjectHierarchyModuleMRMLPython import vtkMRMLSubjectHierarchyConstants
            tags = {}
            tags['Patient Name'] = exportable.tag(
                vtkMRMLSubjectHierarchyConstants.GetDICOMPatientNameTagName())
            tags['Patient ID'] = exportable.tag(
                vtkMRMLSubjectHierarchyConstants.GetDICOMPatientIDTagName())
            tags['Patient Comments'] = exportable.tag(
                vtkMRMLSubjectHierarchyConstants.
                GetDICOMPatientCommentsTagName())
            tags['Study ID'] = self.defaultStudyID
            tags['Study Date'] = exportable.tag(
                vtkMRMLSubjectHierarchyConstants.GetDICOMStudyDateTagName())
            tags['Study Description'] = exportable.tag(
                vtkMRMLSubjectHierarchyConstants.
                GetDICOMStudyDescriptionTagName())
            tags['Modality'] = exportable.tag('Modality')
            tags['Manufacturer'] = exportable.tag('Manufacturer')
            tags['Model'] = exportable.tag('Model')
            tags['Series Description'] = exportable.tag('SeriesDescription')
            tags['Series Number'] = exportable.tag('SeriesNumber')

            # Validate tags
            if tags['Modality'] == "":
                error = "Empty modality for series '" + node.GetAssociatedNode(
                ).GetName() + "'"
                logging.error(error)
                return error
            #TODO: more tag checks

            # Perform export
            exporter = DICOMExportScalarVolume(tags['Study ID'],
                                               node.GetAssociatedNode(), tags,
                                               directory)
            exporter.export()

        # Success
        return ""