def tearDown(self):
   """ Close temporary DICOM database and remove temporary data
   """ 
   import shutil
   if self.originalDicomDatabase:
     DICOMUtils.closeTemporaryDatabase(self.originalDicomDatabase, True)
     shutil.rmtree(self.tempDicomDatabase) # closeTemporaryDatabase cleanup doesn't work. We need to do it manually
     self.originalDicomDatabase = None      
  def section_LoadDicomData(self):
    try:
      # Download and unzip test CT DICOM data
      import urllib
      downloads = (
          ('http://slicer.kitware.com/midas3/download/item/137843/TestDicomCT.zip', self.dicomZipFilePath),
          )

      downloaded = 0
      for url,filePath in downloads:
        if not os.path.exists(filePath) or os.stat(filePath).st_size == 0:
          if downloaded == 0:
            self.delayDisplay('Downloading input data to folder\n' + self.dicomZipFilePath + '.\n\n  It may take a few minutes...',self.delayMs)
          print('Requesting download from %s...' % (url))
          urllib.urlretrieve(url, filePath)
          downloaded += 1
        else:
          self.delayDisplay('Input data has been found in folder ' + self.dicomZipFilePath, self.delayMs)
      if downloaded > 0:
        self.delayDisplay('Downloading input data finished',self.delayMs)

      numOfFilesInDicomDataDir = len([name for name in os.listdir(self.dicomDataDir) if os.path.isfile(self.dicomDataDir + '/' + name)])
      if (numOfFilesInDicomDataDir != self.expectedNumOfFilesInDicomDataDir):
        slicer.app.applicationLogic().Unzip(self.dicomZipFilePath, self.dicomDataDir)
        self.delayDisplay("Unzipping done",self.delayMs)

      numOfFilesInDicomDataDirTest = len([name for name in os.listdir(self.dicomDataDir) if os.path.isfile(self.dicomDataDir + '/' + name)])
      self.assertEqual( numOfFilesInDicomDataDirTest, self.expectedNumOfFilesInDicomDataDir )

      # Open test database and empty it
      with DICOMUtils.TemporaryDICOMDatabase(self.dicomDatabaseDir, True) as db:
        self.assertTrue( db.isOpen )
        self.assertEqual( slicer.dicomDatabase, db)

        # Import test data in database
        indexer = ctk.ctkDICOMIndexer()
        self.assertIsNotNone( indexer )

        indexer.addDirectory( slicer.dicomDatabase, self.dicomDataDir )

        self.assertEqual( len(slicer.dicomDatabase.patients()), 1 )
        self.assertIsNotNone( slicer.dicomDatabase.patients()[0] )

        # Load test data
        numOfScalarVolumeNodesBeforeLoad = len( slicer.util.getNodes('vtkMRMLScalarVolumeNode*') )
        self.assertEqual( len( slicer.util.getNodes('vtkMRMLSubjectHierarchyNode*') ), 1 )

        patient = slicer.dicomDatabase.patients()[0]
        DICOMUtils.loadPatientByUID(patient)

        self.assertEqual( len( slicer.util.getNodes('vtkMRMLScalarVolumeNode*') ),  numOfScalarVolumeNodesBeforeLoad + 1 )
        self.assertEqual( len( slicer.util.getNodes('vtkMRMLSubjectHierarchyNode*') ), 1 )

    except Exception, e:
      import traceback
      traceback.print_exc()
      self.delayDisplay('Test caused exception!\n' + str(e),self.delayMs*2)
 def setUp(self):
   """ Do whatever is needed to reset the state - typically a scene clear will be enough.
   """
   self.UID = '1.3.6.1.4.1.14519.5.2.1.2744.7002.886851941687931416391879144903'
   self.PatientName = 'QIN-HEADNECK-01-0139'
   self.tempDicomDatabase = os.path.join(slicer.app.temporaryPath,'PETTest')
   slicer.mrmlScene.Clear(0)
   self.originalDicomDatabase = DICOMUtils.openTemporaryDatabase(self.tempDicomDatabase)
Example #4
0
    def section_LoadDicomDataWitchBatchProcessing(self):
        try:
            # Open Data module so that a subject hierarchy scene model is active
            # (which caused problems with batch processing)
            slicer.util.selectModule('Data')

            # Open test database and empty it
            with DICOMUtils.TemporaryDICOMDatabase(
                    self.dicomDatabaseDir) as db:
                self.assertTrue(db.isOpen)
                self.assertEqual(slicer.dicomDatabase, db)

                slicer.mrmlScene.StartState(
                    slicer.vtkMRMLScene.BatchProcessState)

                # Download, unzip, import, and load data. Verify loaded nodes.
                loadedNodes = {'vtkMRMLScalarVolumeNode': 1}
                with DICOMUtils.LoadDICOMFilesToDatabase( \
                    self.dicomZipFileUrl, self.dicomZipFilePath, \
                    self.dicomDataDir, self.expectedNumOfFilesInDicomDataDir, \
                    {}, loadedNodes, checksum=self.dicomZipChecksum) as success:
                    self.assertTrue(success)

                slicer.mrmlScene.EndState(
                    slicer.vtkMRMLScene.BatchProcessState)

            self.assertEqual(
                len(slicer.util.getNodes('vtkMRMLSubjectHierarchyNode*')), 1)

            shNode = slicer.mrmlScene.GetSubjectHierarchyNode()
            self.assertIsNotNone(shNode)
            loadedDicomVolumeItemID = shNode.GetItemByName(
                self.loadedDicomVolumeName)
            loadedDicomStudyItemID = shNode.GetItemByName(
                self.loadedDicomStudyName)
            self.assertEqual(shNode.GetItemParent(loadedDicomVolumeItemID),
                             loadedDicomStudyItemID)

        except Exception as e:
            import traceback
            traceback.print_exc()
            self.delayDisplay('Test caused exception!\n' + str(e),
                              self.delayMs * 2)
 def _loadWithPlugin(self, UID, pluginName):
   dicomWidget = slicer.modules.dicom.widgetRepresentation().self()
   dicomPluginCheckbox =  dicomWidget.detailsPopup.pluginSelector.checkBoxByPlugin
   dicomPluginStates = {(key,value.checked) for key,value in dicomPluginCheckbox.iteritems()}
   for cb in dicomPluginCheckbox.itervalues():
     cb.checked=False
   dicomPluginCheckbox[pluginName].checked = True
   success=DICOMUtils.loadSeriesByUID([UID])    
   for key,value in dicomPluginStates:
     dicomPluginCheckbox[key].checked=value
 def test_PETDicomExtensionSelfTest_Main(self):
   """ test PET SUV Plugin and DICOM RWVM creation 
   """ 
   self.delayDisplay('Checking for PET DICOM plugins')
   dicomWidget = slicer.modules.dicom.widgetRepresentation().self()
   dicomPluginCheckbox =  dicomWidget.detailsPopup.pluginSelector.checkBoxByPlugin
   self.assertIn('DICOMPETSUVPlugin', dicomPluginCheckbox)
   self.assertIn('DICOMRWVMPlugin', dicomPluginCheckbox)    
   
   self.delayDisplay('Adding PET DICOM dataset to DICOM database (including download if necessary)')
   self._downloadTestData()
   
   self.delayDisplay('Loading data with DICOMPETSUVPlugin')
   self._loadWithPlugin(self.UID, 'DICOMPETSUVPlugin')
   imageNode = slicer.mrmlScene.GetFirstNodeByClass('vtkMRMLScalarVolumeNode')
   self.assertIsNotNone(imageNode)
   
   self.delayDisplay('Testing properties of loaded SUV normalized data')
   self._testDataProperties(imageNode)
   
   self.delayDisplay('Testing DICOM database for created RWVM file')
   patientUID = DICOMUtils.getDatabasePatientUIDByPatientName(self.PatientName)
   studies = slicer.dicomDatabase.studiesForPatient(patientUID)
   series = slicer.dicomDatabase.seriesForStudy(studies[0])
   RWVMSeries = None
   for serie in series:
     description = slicer.dicomDatabase.descriptionForSeries(serie)
     if description=='PET SUV Factors':
       RWVMSeries = serie
   self.assertIsNotNone(RWVMSeries)
   files = slicer.dicomDatabase.filesForSeries(RWVMSeries)
   self.assertTrue(len(files)>0)
   RWVMFile = files[0]
   print(RWVMFile)
   
   self.delayDisplay('Testing RealWorldValueSlope stored in RWVM file')
   rwvm=dicom.read_file(RWVMFile)
   self.assertIn('ReferencedImageRealWorldValueMappingSequence',  rwvm)
   rirwvms = rwvm.ReferencedImageRealWorldValueMappingSequence[0]
   self.assertIn('RealWorldValueMappingSequence', rirwvms)
   rwvms = rirwvms.RealWorldValueMappingSequence[0]
   self.assertIn('RealWorldValueSlope', rwvms)
   slope = rwvms.RealWorldValueSlope
   self.assertTrue(abs(slope-0.000401664)<0.00001)
   
   self.delayDisplay('Loading data with DICOMRWVMPlugin')
   slicer.mrmlScene.Clear(0)
   self._loadWithPlugin(RWVMSeries, 'DICOMRWVMPlugin')
   imageNode = slicer.mrmlScene.GetFirstNodeByClass('vtkMRMLScalarVolumeNode')
   self.assertIsNotNone(imageNode)
   
   self.delayDisplay('Testing properties of loaded SUV normalized data')
   self._testDataProperties(imageNode)
       
   self.delayDisplay('Test passed!')
  def _test_PETLiverUptakeMeasurementQR2_WithQRExtension(self):
    """ verify SR measurements assuming QuantiativeReporting Extension is installed and enabled
    """
    from DICOMLib import DICOMUtils
    self.delayDisplay('Loading DICOM SEG and SR')

    # note: introduces dependency on QuantiativeReporting and DICOMPETExtension

    # with QuantiativeReporting installed, this should load the DICOM SR and SEG together with the PET scan
    # disable all dicom plugins except those necessary
    dicomWidget = slicer.modules.dicom.widgetRepresentation().self()
    dicomPluginCheckbox =  dicomWidget.detailsPopup.pluginSelector.checkBoxByPlugin
    dicomPluginStates = {(key,value.checked) for key,value in dicomPluginCheckbox.iteritems()}
    for cb in dicomPluginCheckbox.itervalues():
      cb.checked=False
    dicomPluginCheckbox['DICOMRWVMPlugin'].checked = True
    dicomPluginCheckbox['DICOMPETSUVPlugin'].checked = True
    dicomPluginCheckbox['DICOMSegmentationPlugin'].checked = True
    dicomPluginCheckbox['DICOMTID1500Plugin'].checked = True
    DICOMUtils.loadPatientByName(self.patienName)
    for key,value in dicomPluginStates:
      dicomPluginCheckbox[key].checked=value

    # with QuantiativeReporting installed, this should load the DICOM SR and SEG together with the PET scan
    DICOMUtils.loadPatientByName(self.patienName)
    tableNode = slicer.mrmlScene.GetFirstNodeByClass("vtkMRMLTableNode")
    self.assertIsNotNone(tableNode, "Loading SR into mrmlScene failed. No vtkMRMLTableNodes were found within the scene.")
    self.assertEqual(tableNode.GetNumberOfRows(),1)
    self.assertTrue(tableNode.GetNumberOfColumns()>3)
    self.assertEqual(tableNode.GetColumnName(0),'Segment')
    self.assertEqual(tableNode.GetColumnName(1),'Mean [{SUVbw}g/ml]')
    self.assertEqual(tableNode.GetColumnName(2),'Standard Deviation [{SUVbw}g/ml]')
    self.assertEqual(tableNode.GetColumnName(3),'Median [{SUVbw}g/ml]')
    self.assertEqual(tableNode.GetCellText(0,0),'Liver reference region')
    self.assertTrue(abs(float(tableNode.GetCellText(0,1))-2.36253)<0.01)
    self.assertTrue(abs(float(tableNode.GetCellText(0,2))-0.402997)<0.01)
    self.assertTrue(abs(float(tableNode.GetCellText(0,3))-2.335)<0.01)
    self.delayDisplay('Test passed!')

    for key,value in dicomPluginStates:
      dicomPluginCheckbox[key].checked=value
  def section_LoadDicomData(self):
    try:
      # Open test database and empty it
      with DICOMUtils.TemporaryDICOMDatabase(self.dicomDatabaseDir) as db:
        self.assertTrue( db.isOpen )
        self.assertEqual( slicer.dicomDatabase, db)

        # Download, unzip, import, and load data. Verify loaded nodes.
        loadedNodes = {'vtkMRMLScalarVolumeNode':1}
        with DICOMUtils.LoadDICOMFilesToDatabase( \
            self.dicomZipFileUrl, self.dicomZipFilePath, \
            self.dicomDataDir, self.expectedNumOfFilesInDicomDataDir, \
            {}, loadedNodes, checksum=self.dicomZipChecksum) as success:
          self.assertTrue(success)

      self.assertEqual( len( slicer.util.getNodes('vtkMRMLSubjectHierarchyNode*') ), 1 )

    except Exception as e:
      import traceback
      traceback.print_exc()
      self.delayDisplay('Test caused exception!\n' + str(e),self.delayMs*2)
Example #9
0
    def accessDICOMwebStudy(self, request, requestBody):
        """
        Access DICOMweb server to download requested study, add it to
        Slicer's dicom database, and load it into the scene.
        :param requestBody: is a json string
        :param requestBody['dicomWEBPrefix']: is the start of the url
        :param requestBody['dicomWEBStore']: is the middle of the url
        :param requestBody['studyUID']: is the end of the url
        :param requestBody['accessToken']: is the authorization bearer token for the DICOMweb server
        """
        p = urllib.parse.urlparse(request.decode())
        q = urllib.parse.parse_qs(p.query)

        request = json.loads(requestBody), b'application/json'

        dicomWebEndpoint = request['dicomWEBPrefix'] + '/' + request[
            'dicomWEBStore']
        print(f"Loading from {dicomWebEndpoint}")

        from DICOMLib import DICOMUtils
        loadedUIDs = DICOMUtils.importFromDICOMWeb(
            dicomWebEndpoint=request['dicomWEBPrefix'] + '/' +
            request['dicomWEBStore'],
            studyInstanceUID=request['studyUID'],
            accessToken=request['accessToken'])

        files = []
        for studyUID in loadedUIDs:
            for seriesUID in slicer.dicomDatabase.seriesForStudy(studyUID):
                for instance in slicer.dicomDatabase.instancesForSeries(
                        seriesUID):
                    files.append(
                        slicer.dicomDatabase.fileForInstance(instance))
        loadables = DICOMUtils.getLoadablesFromFileLists([files])
        loadedNodes = DICOMUtils.loadLoadables(loadLoadables)

        print(f"Loaded {loadedUIDs}, and {loadedNodes}")

        return b'{"result": "ok"}'
Example #10
0
  def test_BatchStructureSetConversion_FullTest1(self):
    # Create logic
    self.logic = BatchStructureSetConversionLogic()

    # Check for modules
    self.assertTrue(slicer.modules.dicomrtimportexport)
    self.assertTrue(slicer.modules.segmentations)

    self.TestSection_0_SetupPathsAndNames()
    # Open test database and empty it
    with DICOMUtils.TemporaryDICOMDatabase(self.dicomDatabaseDir) as self.db:
      self.TestSection_1_LoadDicomData()
      self.TestSection_2_ConvertStructureSetToLabelmap()
      self.TestSection_3_SaveLabelmaps()
    logging.info('Test finished')
def generateSlicenamesTextfile(ctDicomSeriesUID, slicenamesFilename, 
  outputFolder):
  """ Generate slicenames.txt file, with list of ct dicom slices, in increasing slice order (IS direction)
  """
  filePaths = slicer.dicomDatabase.filesForSeries(ctDicomSeriesUID)
  if len(filePaths) == 0:
    logging.error('Failed to find files in DICOM database for UID ' + str(ctDicomSeriesUID))
    return False

  unsortedFileList = slicer.dicomDatabase.filesForSeries(ctDicomSeriesUID)
  sortedFileList, distances, warnings = DICOMUtils.getSortedImageFiles(unsortedFileList)

  outFile = open(os.path.join(outputFolder, slicenamesFilename), "wb")
  counter = 1
  numDicomFiles = len(sortedFileList)
  for sliceFileName in   sortedFileList:
    outFile.write(sliceFileName)
    if counter != numDicomFiles:
      outFile.write("\n")
    counter += 1
  outFile.close()
  return True
  def _loadTestData_WithPETDICOMExtension(self):
    """ load SUV normalized PET scan from DICOM fileassuming Slicer-PETDICOM extension is installed and enabled
    """
    from DICOMLib import DICOMUtils
    import urllib
    data = {
      'PETVolume': {
        'UID': '1.3.6.1.4.1.14519.5.2.1.2744.7002.886851941687931416391879144903',
        'url': 'http://slicer.kitware.com/midas3/download/item/257234/QIN-HEADNECK-01-0139-PET.zip',
        'zipFile': 'QIN-HEADNECK-01-0139-PET.zip',
        'SUVNormalizationFactor': 0.00040166400000000007
      }
    }
    destinationDirectory = self.tempDataDir
    for key, value in data.iteritems(): # download data if necessary
      UID = value['UID']
      if not len(slicer.dicomDatabase.filesForSeries(UID)):
        url = value['url']
        zipFile = value['zipFile']
        filePath = os.path.join(destinationDirectory, zipFile)
        if not os.path.exists(os.path.dirname(filePath)):
          os.makedirs(os.path.dirname(filePath))
        logging.debug('Saving download %s to %s ' % (url, filePath))
        if not os.path.exists(filePath) or os.stat(filePath).st_size == 0:
          slicer.util.delayDisplay('Requesting download of %s...\n' % url, 1000)
          urllib.urlretrieve(url, filePath)
        if os.path.exists(filePath) and os.path.splitext(filePath)[1]=='.zip':
          success = slicer.app.applicationLogic().Unzip(filePath, destinationDirectory)
          if not success:
            logging.error("Archive %s was NOT unzipped successfully." %  filePath)
      indexer = ctk.ctkDICOMIndexer()
      indexer.addDirectory(slicer.dicomDatabase, destinationDirectory, None)
      indexer.waitForImportFinished()
    # load dataset
    success=DICOMUtils.loadSeriesByUID([data['PETVolume']['UID']])
    if not success:
      logging.error("Unable to load dicom data %s\n." %  data['PETVolume']['UID'])

    return slicer.mrmlScene.GetFirstNodeByClass('vtkMRMLScalarVolumeNode')
def generateSlicenamesTextfile(ctDicomSeriesUID, slicenamesFilename,
                               outputFolder):
    """ Generate slicenames.txt file, with list of ct dicom slices, in increasing slice order (IS direction)
  """
    filePaths = slicer.dicomDatabase.filesForSeries(ctDicomSeriesUID)
    if len(filePaths) == 0:
        logging.error('Failed to find files in DICOM database for UID ' +
                      str(ctDicomSeriesUID))
        return False

    unsortedFileList = slicer.dicomDatabase.filesForSeries(ctDicomSeriesUID)
    sortedFileList, distances, warnings = DICOMUtils.getSortedImageFiles(
        unsortedFileList)

    outFile = open(os.path.join(outputFolder, slicenamesFilename), "wb")
    counter = 1
    numDicomFiles = len(sortedFileList)
    for sliceFileName in sortedFileList:
        outFile.write(sliceFileName)
        if counter != numDicomFiles:
            outFile.write("\n")
        counter += 1
    outFile.close()
    return True
Example #14
0
 def LoadFirstPatientIntoSlicer(self):
     # Choose first patient from the patient list
     patient = slicer.dicomDatabase.patients()[0]
     DICOMUtils.loadPatientByUID(patient)
Example #15
0
def main(argv):
    try:
        # Parse command-line arguments
        parser = argparse.ArgumentParser(
            description="Batch Structure Set Conversion")
        parser.add_argument(
            "-i",
            "--input-folder",
            dest="input_folder",
            metavar="PATH",
            default="-",
            required=True,
            help=
            "Folder of input DICOM study (or database path to use existing)")
        parser.add_argument("-x",
                            "--exist-db",
                            dest="exist_db",
                            default=False,
                            required=False,
                            action='store_true',
                            help="Process an existing database")
        parser.add_argument(
            "-p",
            "--patient",
            "--patients",
            dest="patients",
            default=[],
            required=False,
            nargs='+',
            help="Process particular patients in the existing database")
        parser.add_argument("-m",
                            "--export-images",
                            dest="export_images",
                            default=False,
                            required=False,
                            action='store_true',
                            help="Export image data with labelmaps")
        parser.add_argument("-o",
                            "--output-folder",
                            dest="output_folder",
                            metavar="PATH",
                            default=".",
                            help="Folder for output labelmaps")

        parser.add_argument(
            "--stop-on-error",
            dest="stop_on_error",
            default=False,
            required=False,
            action='store_true',
            help="Halt the script if processing a patient fails")
        parser.add_argument(
            "--do-not-exit",
            dest="do_not_exit",
            default=False,
            required=False,
            action='store_true',
            help="Don't exit Slicer after performing conversion(s)")

        args = parser.parse_args(argv)
        logging.info("Running with arguments:")
        logging.info(args)

        # Check required arguments
        if args.input_folder == "-":
            logging.error("Please specify input DICOM study folder!")
            sys.exit(-1)
        if args.output_folder == ".":
            logging.error(
                "Current directory is selected as output folder (default). To change it, please specify --output-folder"
            )
            sys.exit(-1)
        if len(args.patients) > 0 and not args.exist_db:
            logging.error("Can only specify patients in an existing database")
            sys.exit(-1)

        # Convert to python path style
        input_folder = args.input_folder.replace('\\', '/')
        output_folder = args.output_folder.replace('\\', '/')
        exist_db = args.exist_db
        export_images = args.export_images

        # Perform batch conversion
        logic = BatchStructureSetConversionLogic()

        def save_rtslices(output_dir):
            logging.info("Convert loaded structure set to labelmap volumes")
            labelmaps = logic.ConvertStructureSetToLabelmap()

            logging.info("Save labelmaps to directory " + output_dir)
            logic.SaveLabelmaps(labelmaps, output_dir)

            if export_images:
                logic.SaveImages(output_dir)

            logging.info("Save screen shots to directory " + output_dir)
            logic.SaveSegmentScreenShots(output_dir)

            logging.info("DONE saving to %s" % output_dir)

        def create_results_page(output_folder, converted_patients,
                                skipped_patients, failed_patients):
            """Make an html page that summarizes the results with thumbnails
            for the screenshots"""

            fp = open(os.path.join(output_folder, "index.html"), "w")

            fp.write('''
                <style>
                /* based on: https://www.w3schools.com/howto/howto_css_thumbnail.asp */
                img {
                    border: 1px solid #ddd; /* Gray border */
                    border-radius: 4px;  /* Rounded border */
                    padding: 5px; /* Some padding */
                    width: 550px; /* Set a small width */
                }
                /* Add a hover effect (blue shadow) */
                img:hover {
                    box-shadow: 0 0 2px 1px rgba(0, 140, 186, 0.5);
                }
                </style>

                <body>
                ''')

            fp.write("<p>converted: %s</p>" % converted_patients)
            fp.write("<p>skipped: %s</p>" % skipped_patients)
            fp.write("<p>failed: %s</p>" % failed_patients)
            for patient in converted_patients:
                fp.write('''
                    <a target="_blank" href="%s/GTV-1.png">
                      <img src="%s/GTV-1.png" alt="%s">
                    </a>
                    <p>Patient: %s</p>
                    ''' % (
                    patient,
                    patient,
                    patient,
                    patient,
                ))

            fp.write("</body>")
            fp.close()

        if exist_db:
            logging.info("BatchStructureSet Running in Existing Database Mode")
            database_start_time = time.time()
            logic.LoadDatabase(input_folder)
            all_patients = slicer.dicomDatabase.patients()
            if len(args.patients) > 0:
                all_patients = args.patients
            logging.info("Must Process Patients %s" % len(all_patients))
            converted_patients = []
            skipped_patients = []
            failed_patients = []
            for patient in all_patients:
                patient_start_time = time.time()
                try:
                    slicer.mrmlScene.Clear(0)  # clear the scene
                    if len(slicer.dicomDatabase.studiesForPatient(
                            patient)) == 0:
                        logging.warning(
                            "Skipping patient with no studies: %s" % patient)
                        skipped_patients.append(patient)
                        continue
                    DICOMUtils.loadPatientByUID(patient)
                    output_dir = os.path.join(output_folder, patient)
                    if not os.access(output_dir, os.F_OK):
                        os.mkdir(output_dir)
                    save_rtslices(output_dir)
                    converted_patients.append(patient)
                except Exception, e:
                    import traceback
                    traceback.print_exc()
                    logging.error("Failed to convert patient: %s", patient)
                    failed_patients.append(patient)
                    if args.stop_on_error:
                        logging.warning(
                            "Stopped processing after failure on patient %s" %
                            patient)
                        sys.exit(-1)
                logging.info("Finished patient %s in %d seconds" %
                             (patient, time.time() - patient_start_time))
            create_results_page(output_folder, converted_patients,
                                skipped_patients, failed_patients)
            logging.info("\n------------")
            logging.info("Results")
            logging.info("------------\n")
            logging.info("Finished database in %d seconds" %
                         (time.time() - database_start_time))
            logging.info("Converted %d patients" % len(converted_patients))
            logging.info("Skipped %d patients" % len(skipped_patients))
            logging.error("Conversion failed on %d patients" %
                          len(failed_patients))
            logging.error("Failed patients:")
            logging.error(failed_patients)

        else:
Example #16
0
def main(argv):
  try:
    # Parse command-line arguments
    parser = argparse.ArgumentParser(description="Batch Structure Set Conversion")
    parser.add_argument("-i", "--input-folder", dest="input_folder", metavar="PATH",
                        default="-", required=True, help="Folder of input DICOM study (or database path to use existing)")
    parser.add_argument("-x", "--exist-db", dest="exist_db",
                        default=False, required=False, action='store_true',
                        help="Process an existing database")
    parser.add_argument("-m", "--export-images", dest="export_images",
                        default=False, required=False, action='store_true',
                        help="Export image data with labelmaps")
    parser.add_argument("-o", "--output-folder", dest="output_folder", metavar="PATH",
                        default=".", help="Folder for output labelmaps")

    args = parser.parse_args(argv)

    # Check required arguments
    if args.input_folder == "-":
      logging.warning('Please specify input DICOM study folder!')
    if args.output_folder == ".":
      logging.info('Current directory is selected as output folder (default). To change it, please specify --output-folder')

    # Convert to python path style
    input_folder = args.input_folder.replace('\\', '/')
    output_folder = args.output_folder.replace('\\', '/')
    exist_db = args.exist_db
    export_images = args.export_images

    # Perform batch conversion
    logic = BatchStructureSetConversionLogic()
    def save_rtslices(output_dir):
      # package the saving code into a subfunction
      logging.info("Convert loaded structure set to labelmap volumes")
      labelmaps = logic.ConvertStructureSetToLabelmap()

      logging.info("Save labelmaps to directory " + output_dir)
      logic.SaveLabelmaps(labelmaps, output_dir)
      if export_images:
        logic.SaveImages(output_dir)
      logging.info("DONE")

    if exist_db:
      logging.info('BatchStructureSet running in existing database mode')
      DICOMUtils.openDatabase(input_folder)
      all_patients = slicer.dicomDatabase.patients()
      logging.info('Must Process Patients %s' % len(all_patients))
      for patient in all_patients:
        slicer.mrmlScene.Clear(0) # clear the scene
        DICOMUtils.loadPatientByUID(patient)
        output_dir = os.path.join(output_folder,patient)
        if not os.access(output_dir, os.F_OK):
          os.mkdir(output_dir)
        save_rtslices(output_dir)
    else:
      logging.info("Import DICOM data from " + input_folder)
      DICOMUtils.openTemporaryDatabase()
      DICOMUtils.importDicom(input_folder)

      logging.info("Load first patient into Slicer")
      logic.LoadFirstPatientIntoSlicer()
      save_rtslices(output_folder)

  except Exception as e:
      print(e)
  sys.exit(0)
Example #17
0
    def examineFiles(self, files):
        """ Returns a list of DICOMLoadable instances
    corresponding to ways of interpreting the
    files parameter.
    """

        seriesUID = slicer.dicomDatabase.fileValue(files[0],
                                                   self.tags['seriesUID'])
        seriesName = self.defaultSeriesNodeName(seriesUID)

        # default loadable includes all files for series
        allFilesLoadable = DICOMLoadable()
        allFilesLoadable.files = files
        allFilesLoadable.name = self.cleanNodeName(seriesName)
        allFilesLoadable.tooltip = "%d files, first file: %s" % (len(
            allFilesLoadable.files), allFilesLoadable.files[0])
        allFilesLoadable.selected = True
        # add it to the list of loadables later, if pixel data is available in at least one file

        # make subseries volumes based on tag differences
        subseriesTags = [
            "seriesInstanceUID",
            "acquisitionNumber",
            # GE volume viewer and Siemens Axiom CBCT systems put an overview (localizer) slice and all the reconstructed slices
            # in one series, using two different image types. Splitting based on image type allows loading of these volumes
            # (loading the series without localizer).
            "imageType",
            "imageOrientationPatient",
            "diffusionGradientOrientation",
        ]

        if self.allowLoadingByTime():
            subseriesTags.append("contentTime")
            subseriesTags.append("triggerTime")

        # Values for these tags will only be enumerated (value itself will not be part of the loadable name)
        # because the vale itself is usually too long and complicated to be displayed to users
        subseriesTagsToEnumerateValues = [
            "seriesInstanceUID",
            "imageOrientationPatient",
            "diffusionGradientOrientation",
        ]

        #
        # first, look for subseries within this series
        # - build a list of files for each unique value
        #   of each tag
        #
        subseriesFiles = {}
        subseriesValues = {}
        for file in allFilesLoadable.files:
            # check for subseries values
            for tag in subseriesTags:
                value = slicer.dicomDatabase.fileValue(file, self.tags[tag])
                value = value.replace(
                    ",", "_")  # remove commas so it can be used as an index
                if tag not in subseriesValues:
                    subseriesValues[tag] = []
                if not subseriesValues[tag].__contains__(value):
                    subseriesValues[tag].append(value)
                if (tag, value) not in subseriesFiles:
                    subseriesFiles[tag, value] = []
                subseriesFiles[tag, value].append(file)

        loadables = []

        # Pixel data is available, so add the default loadable to the output
        loadables.append(allFilesLoadable)

        #
        # second, for any tags that have more than one value, create a new
        # virtual series
        #
        subseriesCount = 0
        # List of loadables that look like subseries that contain the full series except a single frame
        probableLocalizerFreeLoadables = []
        for tag in subseriesTags:
            if len(subseriesValues[tag]) > 1:
                subseriesCount += 1
                for valueIndex, value in enumerate(subseriesValues[tag]):
                    # default loadable includes all files for series
                    loadable = DICOMLoadable()
                    loadable.files = subseriesFiles[tag, value]
                    # value can be a long string (and it will be used for generating node name)
                    # therefore use just an index instead
                    if tag in subseriesTagsToEnumerateValues:
                        loadable.name = seriesName + " - %s %d" % (
                            tag, valueIndex + 1)
                    else:
                        loadable.name = seriesName + " - %s %s" % (tag, value)
                    loadable.name = self.cleanNodeName(loadable.name)
                    loadable.tooltip = "%d files, grouped by %s = %s. First file: %s. %s = %s" % (
                        len(loadable.files), tag, value, loadable.files[0],
                        tag, value)
                    loadable.selected = False
                    loadables.append(loadable)
                    if len(subseriesValues[tag]) == 2:
                        otherValue = subseriesValues[tag][1 - valueIndex]
                        if len(subseriesFiles[tag, value]) > 1 and len(
                                subseriesFiles[tag, otherValue]) == 1:
                            # this looks like a subseries without a localizer image
                            probableLocalizerFreeLoadables.append(loadable)

        # remove any files from loadables that don't have pixel data (no point sending them to ITK for reading)
        # also remove DICOM SEG, since it is not handled by ITK readers
        newLoadables = []
        for loadable in loadables:
            newFiles = []
            excludedLoadable = False
            for file in loadable.files:
                if slicer.dicomDatabase.fileValueExists(
                        file, self.tags['pixelData']):
                    newFiles.append(file)
                if slicer.dicomDatabase.fileValue(
                        file, self.tags['sopClassUID']
                ) == '1.2.840.10008.5.1.4.1.1.66.4':
                    excludedLoadable = True
                    if 'DICOMSegmentationPlugin' not in slicer.modules.dicomPlugins:
                        logging.warning(
                            'Please install Quantitative Reporting extension to enable loading of DICOM Segmentation objects'
                        )
                elif slicer.dicomDatabase.fileValue(
                        file, self.tags['sopClassUID']
                ) == '1.2.840.10008.5.1.4.1.1.481.3':
                    excludedLoadable = True
                    if 'DicomRtImportExportPlugin' not in slicer.modules.dicomPlugins:
                        logging.warning(
                            'Please install SlicerRT extension to enable loading of DICOM RT Structure Set objects'
                        )
            if len(newFiles) > 0 and not excludedLoadable:
                loadable.files = newFiles
                loadable.grayscale = (
                    'MONOCHROME' in slicer.dicomDatabase.fileValue(
                        newFiles[0], self.tags['photometricInterpretation']))
                newLoadables.append(loadable)
            elif excludedLoadable:
                continue
            else:
                # here all files in have no pixel data, so they might be
                # secondary capture images which will read, so let's pass
                # them through with a warning and low confidence
                loadable.warning += "There is no pixel data attribute for the DICOM objects, but they might be readable as secondary capture images.  "
                loadable.confidence = 0.2
                loadable.grayscale = (
                    'MONOCHROME' in slicer.dicomDatabase.fileValue(
                        loadable.files[0],
                        self.tags['photometricInterpretation']))
                newLoadables.append(loadable)
        loadables = newLoadables

        #
        # now for each series and subseries, sort the images
        # by position and check for consistency
        # then adjust confidence values based on warnings
        #
        for loadable in loadables:
            loadable.files, distances, loadable.warning = DICOMUtils.getSortedImageFiles(
                loadable.files, self.epsilon)

        loadablesBetterThanAllFiles = []
        if allFilesLoadable.warning != "":
            for probableLocalizerFreeLoadable in probableLocalizerFreeLoadables:
                if probableLocalizerFreeLoadable.warning == "":
                    # localizer-free loadables are better then all files, if they don't have warning
                    loadablesBetterThanAllFiles.append(
                        probableLocalizerFreeLoadable)
            if not loadablesBetterThanAllFiles and subseriesCount == 1:
                # there was a sorting warning and
                # only one kind of subseries, so it's probably correct
                # to have lower confidence in the default all-files version.
                for loadable in loadables:
                    if loadable != allFilesLoadable and loadable.warning == "":
                        loadablesBetterThanAllFiles.append(loadable)

        # if there are loadables that are clearly better then all files, then use those (otherwise use all files loadable)
        preferredLoadables = loadablesBetterThanAllFiles if loadablesBetterThanAllFiles else [
            allFilesLoadable
        ]
        # reduce confidence and deselect all non-preferred loadables
        for loadable in loadables:
            if loadable in preferredLoadables:
                loadable.selected = True
            else:
                loadable.selected = False
                if loadable.confidence > .45:
                    loadable.confidence = .45

        return loadables
  def test_PETLiverUptakeMeasurementQR1(self):
    """ test standard segmentation and report generation
    """
    try:
      self.assertIsNotNone( slicer.modules.petliveruptakemeasurement )
      with DICOMUtils.TemporaryDICOMDatabase(self.tempDicomDatabaseDir) as db:
        self.assertTrue(db.isOpen)
        self.assertEqual(slicer.dicomDatabase, db)

        self.delayDisplay('Loading PET DICOM dataset (including download if necessary)')
        petNode = self.loadTestData()

        self.delayDisplay('Running segmentation')
        m = slicer.util.mainWindow()
        m.moduleSelector().selectModule('PETLiverUptakeMeasurementQR')
        qrWidget = slicer.modules.PETLiverUptakeMeasurementQRWidget
        qrWidget.inputSelector.setCurrentNode(petNode)
        segmentationNode = qrWidget.segmentationSelector.addNode()
        qrWidget.segmentButton.click()

        self.assertTrue(abs(float(qrWidget.meanValueLineEdit.text)-2.36253)<0.01)
        self.assertTrue(abs(float(qrWidget.stdValueLineEdit.text)-0.402997)<0.01)
        self.assertTrue(abs(float(qrWidget.medianValueLineEdit.text)-2.335)<0.01)

        self.delayDisplay('Completing and writing DICOM report')
        qrWidget.readerValueLineEdit.text = 'autotest'
        self.assertTrue(qrWidget.saveReport(completed=True))

        self.delayDisplay('Checking for DICOM SEG and SR')
        import dicom
        patientUID = DICOMUtils.getDatabasePatientUIDByPatientName(self.patienName)
        studies = slicer.dicomDatabase.studiesForPatient(patientUID)
        series = slicer.dicomDatabase.seriesForStudy(studies[0])
        SRSeries = None
        SEGSeries = None
        for serie in series:
          description = slicer.dicomDatabase.descriptionForSeries(serie)
          if description=='Automatic Liver Reference Region Segmentation':
            SEGSeries = serie
          if description=='Liver Reference Region Measurement Report':
            SRSeries = serie
        self.assertIsNotNone(SRSeries)
        self.assertIsNotNone(SEGSeries)
        SRFile = slicer.dicomDatabase.filesForSeries(SRSeries)[0]

        self.delayDisplay('Loading DICOM SR and verifying stored measurements')
        sr = dicom.read_file(SRFile)
        dicomMean = self._getMeasuredValue(sr,'Mean')
        self.assertIsNotNone(dicomMean)
        self.assertEqual(dicomMean.MeasuredValueSequence[0].NumericValue, 2.36253)
        dicomStandardDeviation = self._getMeasuredValue(sr,'Standard Deviation')
        self.assertIsNotNone(dicomStandardDeviation)
        self.assertEqual(dicomStandardDeviation.MeasuredValueSequence[0].NumericValue, 0.402997)
        dicomMedian = self._getMeasuredValue(sr,'Median')
        self.assertIsNotNone(dicomMedian)
        self.assertEqual(dicomMedian.MeasuredValueSequence[0].NumericValue, 2.335)

        # clean up data from DICOM database
        db.removePatient(patientUID)

        self.delayDisplay('Test passed!')

    except Exception, e:
      import traceback
      traceback.print_exc()
      self.delayDisplay('Test caused exception!\n' + str(e),self.delayMs*2)
Example #19
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 #20
0
class RSNAVisTutorialTest(unittest.TestCase):
  """
  This is the test case for your scripted module.
  """

  def delayDisplay(self,message,msec=1000):
    """This utility method displays a small dialog and waits.
    This does two things: 1) it lets the event loop catch up
    to the state of the test so that rendering and widget updates
    have all taken place before the test continues and 2) it
    shows the user/developer/tester the state of the test
    so that we'll know when it breaks.
    """
    print(message)
    self.info = qt.QDialog()
    self.infoLayout = qt.QVBoxLayout()
    self.info.setLayout(self.infoLayout)
    self.label = qt.QLabel(message,self.info)
    self.infoLayout.addWidget(self.label)
    qt.QTimer.singleShot(msec, self.info.close)
    self.info.exec_()

  def takeScreenshot(self,name,description,type=-1):
    # show the message even if not taking a screen shot
    self.delayDisplay(description)

    if self.enableScreenshots == 0:
      return

    lm = slicer.app.layoutManager()
    # switch on the type to get the requested window
    widget = 0
    if type == slicer.qMRMLScreenShotDialog.FullLayout:
      # full layout
      widget = lm.viewport()
    elif type == slicer.qMRMLScreenShotDialog.ThreeD:
      # just the 3D window
      widget = lm.threeDWidget(0).threeDView()
    elif type == slicer.qMRMLScreenShotDialog.Red:
      # red slice window
      widget = lm.sliceWidget("Red")
    elif type == slicer.qMRMLScreenShotDialog.Yellow:
      # yellow slice window
      widget = lm.sliceWidget("Yellow")
    elif type == slicer.qMRMLScreenShotDialog.Green:
      # green slice window
      widget = lm.sliceWidget("Green")
    else:
      # default to using the full window
      widget = slicer.util.mainWindow()
      # reset the type so that the node is set correctly
      type = slicer.qMRMLScreenShotDialog.FullLayout

    # grab and convert to vtk image data
    qpixMap = qt.QPixmap().grabWidget(widget)
    qimage = qpixMap.toImage()
    imageData = vtk.vtkImageData()
    slicer.qMRMLUtils().qImageToVtkImageData(qimage,imageData)

    annotationLogic = slicer.modules.annotations.logic()
    annotationLogic.CreateSnapShot(name, description, type, self.screenshotScaleFactor, imageData)

  def setUp(self):
    """ Do whatever is needed to reset the state - typically a scene clear will be enough.
    """
    self.delayDisplay("Closing the scene")
    layoutManager = slicer.app.layoutManager()
    layoutManager.setLayout(slicer.vtkMRMLLayoutNode.SlicerLayoutConventionalView)
    slicer.mrmlScene.Clear(0)

  def runTest(self):
    """Run as few or as many tests as needed here.
    """
    self.setUp()
    self.test_Part1DICOM()
    self.setUp()
    self.test_Part2Head()
    self.setUp()
    self.test_Part3Liver()
    self.setUp()
    self.test_Part4Lung()

  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 = slicer.modules.DICOMWidget
      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))

    self.delayDisplay("Restoring original database directory")
    DICOMUtils.closeTemporaryDatabase(originalDatabaseDirectory)
Example #21
0
  def examineFiles(self,files):
    """ Returns a list of DICOMLoadable instances
    corresponding to ways of interpreting the
    files parameter.
    """

    seriesUID = slicer.dicomDatabase.fileValue(files[0],self.tags['seriesUID'])
    seriesName = self.defaultSeriesNodeName(seriesUID)

    # default loadable includes all files for series
    loadable = DICOMLoadable()
    loadable.files = files
    loadable.name = seriesName
    loadable.tooltip = "%d files, first file: %s" % (len(loadable.files), loadable.files[0])
    loadable.selected = True
    # add it to the list of loadables later, if pixel data is available in at least one file

    # make subseries volumes based on tag differences
    subseriesTags = [
        "seriesInstanceUID",
        "imageOrientationPatient",
        "diffusionGradientOrientation",
    ]

    if self.allowLoadingByTime():
      subseriesTags.append("contentTime")
      subseriesTags.append("triggerTime")

    #
    # first, look for subseries within this series
    # - build a list of files for each unique value
    #   of each tag
    #
    subseriesFiles = {}
    subseriesValues = {}
    for file in loadable.files:
      # check for subseries values
      for tag in subseriesTags:
        value = slicer.dicomDatabase.fileValue(file,self.tags[tag])
        value = value.replace(",","_") # remove commas so it can be used as an index
        if tag not in subseriesValues:
          subseriesValues[tag] = []
        if not subseriesValues[tag].__contains__(value):
          subseriesValues[tag].append(value)
        if (tag,value) not in subseriesFiles:
          subseriesFiles[tag,value] = []
        subseriesFiles[tag,value].append(file)

    loadables = []

    # Pixel data is available, so add the default loadable to the output
    loadables.append(loadable)

    #
    # second, for any tags that have more than one value, create a new
    # virtual series
    #
    for tag in subseriesTags:
      if len(subseriesValues[tag]) > 1:
        for valueIndex, value in enumerate(subseriesValues[tag]):
          # default loadable includes all files for series
          loadable = DICOMLoadable()
          loadable.files = subseriesFiles[tag,value]
          # value can be a long string (and it will be used for generating node name)
          # therefore use just an index instead
          loadable.name = seriesName + " - %s %d" % (tag,valueIndex+1)
          loadable.tooltip = "%d files, grouped by %s = %s. First file: %s" % (len(loadable.files), tag, value, loadable.files[0])
          loadable.selected = False
          loadables.append(loadable)

    # remove any files from loadables that don't have pixel data (no point sending them to ITK for reading)
    # also remove DICOM SEG, since it is not handled by ITK readers
    newLoadables = []
    for loadable in loadables:
      newFiles = []
      excludedLoadable = False
      for file in loadable.files:
        if slicer.dicomDatabase.fileValue(file,self.tags['pixelData'])!='':
          newFiles.append(file)
        if slicer.dicomDatabase.fileValue(file,self.tags['sopClassUID'])=='1.2.840.10008.5.1.4.1.1.66.4':
          excludedLoadable = True
          logging.error('Please install Quantitative Reporting extension to enable loading of DICOM Segmentation objects')
        elif slicer.dicomDatabase.fileValue(file,self.tags['sopClassUID'])=='1.2.840.10008.5.1.4.1.1.481.3':
          excludedLoadable = True
          logging.error('Please install SlicerRT extension to enable loading of DICOM RT Structure Set objects')
      if len(newFiles) > 0 and not excludedLoadable:
        loadable.files = newFiles
        newLoadables.append(loadable)
      elif excludedLoadable:
        continue
      else:
        # here all files in have no pixel data, so they might be
        # secondary capture images which will read, so let's pass
        # them through with a warning and low confidence
        loadable.warning += "There is no pixel data attribute for the DICOM objects, but they might be readable as secondary capture images.  "
        loadable.confidence = 0.2
        newLoadables.append(loadable)
    loadables = newLoadables

    #
    # now for each series and subseries, sort the images
    # by position and check for consistency
    #
    for loadable in loadables:
      loadable.files, distances, loadable.warning = DICOMUtils.getSortedImageFiles(loadable.files, self.epsilon)

    return loadables
    def test_Part1DICOM(self,
                        enableScreenshotsFlag=0,
                        screenshotScaleFactor=1):
        """ Test the DICOM part of the test using the head atlas
    """

        logic = RSNAVisTutorialLogic()
        logic.enableScreenshots = enableScreenshotsFlag
        logic.screenshotScaleFactor = screenshotScaleFactor

        import os
        self.delayDisplay("Starting the DICOM test")
        #
        # first, get the data - a zip file of dicom data
        #
        import SampleData
        dicomFilesDirectory = SampleData.downloadFromURL(
            fileNames='dataset1_Thorax_Abdomen.zip',
            uris=TESTING_DATA_URL +
            'SHA256/17a4199aad03a373dab27dc17e5bfcf84fc194d0a30975b4073e5b595d43a56a',
            checksums=
            'SHA256:17a4199aad03a373dab27dc17e5bfcf84fc194d0a30975b4073e5b595d43a56a'
        )[0]

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

            slicer.util.selectModule('DICOM')
            browserWidget = slicer.modules.DICOMWidget.browserWidget
            dicomBrowser = browserWidget.dicomBrowser
            dicomBrowser.importDirectory(dicomFilesDirectory,
                                         dicomBrowser.ImportDirectoryAddLink)
            dicomBrowser.waitForImportFinished()

            # load the data by series UID
            dicomBrowser.dicomTableManager().patientsTable().selectFirst()
            browserWidget.examineForLoading()

            self.delayDisplay('Loading Selection')
            browserWidget.loadCheckedLoadables()

            logic.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))

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

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

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

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

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

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

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

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

            presetsScene = slicer.modules.volumerendering.logic(
            ).GetPresetsScene()
            ctCardiac3 = presetsScene.GetFirstNodeByName('CT-Cardiac3')
            volumeRenderingWidgetRep.mrmlVolumePropertyNode().Copy(ctCardiac3)
            logic.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)
            logic.takeScreenshot('VolumeRendering-ViewRendering',
                                 'View Volume Rendering', -1)

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

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

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

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

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

            redWidget.sliceController().setSliceVisible(True)
            logic.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)
            logic.takeScreenshot('VolumeRendering-SizedROI',
                                 'Position the ROI over a kidney', -1)

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

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

            self.delayDisplay('Test passed!')
        except Exception as e:
            import traceback
            traceback.print_exc()
            self.delayDisplay('Test caused exception!\n' + str(e))

        self.delayDisplay("Restoring original database directory")
        DICOMUtils.closeTemporaryDatabase(originalDatabaseDirectory)
Example #23
0
class RSNAVisTutorialTest(ScriptedLoadableModuleTest):
  """
  This is the test case for your scripted module.
  Uses ScriptedLoadableModuleTest base class, available at:
  https://github.com/Slicer/Slicer/blob/master/Base/Python/slicer/ScriptedLoadableModule.py
  """

  def setUp(self):
    """ Do whatever is needed to reset the state - typically a scene clear will be enough.
    """
    self.delayDisplay("Closing the scene")
    layoutManager = slicer.app.layoutManager()
    layoutManager.setLayout(slicer.vtkMRMLLayoutNode.SlicerLayoutConventionalView)
    slicer.mrmlScene.Clear(0)

  def runTest(self):
    """Run as few or as many tests as needed here.
    """
    self.setUp()
    self.test_Part1DICOM()
    self.setUp()
    self.test_Part2Head()
    self.setUp()
    self.test_Part3Liver()
    self.setUp()
    self.test_Part4Lung()

  def test_Part1DICOM(self,enableScreenshotsFlag=0,screenshotScaleFactor=1):
    """ Test the DICOM part of the test using the head atlas
    """

    logic = RSNAVisTutorialLogic()
    logic.enableScreenshots = enableScreenshotsFlag
    logic.screenshotScaleFactor = screenshotScaleFactor

    import os
    self.delayDisplay("Starting the DICOM test")
    #
    # first, get the data - a zip file of dicom data
    #
    import SampleData
    dicomFilesDirectory = SampleData.downloadFromURL(
      fileNames='dataset1_Thorax_Abdomen.zip',
      uris='http://slicer.kitware.com/midas3/download?items=124183')[0]

    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 = slicer.modules.DICOMWidget
      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()

      logic.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))

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

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

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

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

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

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

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

      volumeRenderingWidgetRep = slicer.modules.volumerendering.widgetRepresentation()
      abdomenVolume = slicer.mrmlScene.GetFirstNodeByName('6: CT_Thorax_Abdomen')
      volumeRenderingWidgetRep.setMRMLVolumeNode(abdomenVolume)
      logic.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)
      logic.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)
      logic.takeScreenshot('VolumeRendering-ViewRendering','View Volume Rendering',-1)

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

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

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

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

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

      redWidget.sliceController().setSliceVisible(True)
      logic.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)
      logic.takeScreenshot('VolumeRendering-SizedROI','Position the ROI over a kidney',-1)

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

      annotationROI.SetXYZ(15,146,-186)
      annotationROI.SetRadiusXYZ(138,57,61)
      logic.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))

    self.delayDisplay("Restoring original database directory")
    DICOMUtils.closeTemporaryDatabase(originalDatabaseDirectory)
def main(argv):
  try:
    # Parse command-line arguments
    parser = argparse.ArgumentParser(description="Batch Structure Set Conversion")
    parser.add_argument("-i", "--input-folder", dest="input_folder", metavar="PATH",
                        default="-", required=True, help="Folder of input DICOM study (or database path to use existing)")
    parser.add_argument("-x", "--exist-db", dest="exist_db",
                        default=False, required=False, action='store_true',
                        help="Process an existing database")
    parser.add_argument("-m", "--export-images", dest="export_images",
                        default=False, required=False, action='store_true',
                        help="Export image data with labelmaps")
    parser.add_argument("-o", "--output-folder", dest="output_folder", metavar="PATH",
                        default=".", help="Folder for output labelmaps")

    args = parser.parse_args(argv)

    # Check required arguments
    if args.input_folder == "-":
      logging.warning('Please specify input DICOM study folder!')
    if args.output_folder == ".":
      logging.info('Current directory is selected as output folder (default). To change it, please specify --output-folder')

    # Convert to python path style
    input_folder = args.input_folder.replace('\\', '/')
    output_folder = args.output_folder.replace('\\', '/')
    exist_db = args.exist_db
    export_images = args.export_images

    # Perform batch conversion
    logic = BatchStructureSetConversionLogic()
    def save_rtslices(output_dir):
      # package the saving code into a subfunction
      logging.info("Convert loaded structure set to labelmap volumes")
      labelmaps = logic.ConvertStructureSetToLabelmap()

      logging.info("Save labelmaps to directory " + output_dir)
      logic.SaveLabelmaps(labelmaps, output_dir)
      if export_images:
        logic.SaveImages(output_dir)
      logging.info("DONE")

    if exist_db:
      logging.info('BatchStructureSet running in existing database mode')
      DICOMUtils.openDatabase(input_folder)
      all_patients = slicer.dicomDatabase.patients()
      logging.info('Must Process Patients %s' % len(all_patients))
      for patient in all_patients:
        slicer.mrmlScene.Clear(0) # clear the scene
        DICOMUtils.loadPatientByUID(patient)
        output_dir = os.path.join(output_folder,patient)
        if not os.access(output_dir, os.F_OK):
          os.mkdir(output_dir)
        save_rtslices(output_dir)
    else:
      logging.info("Import DICOM data from " + input_folder)
      DICOMUtils.openTemporaryDatabase()
      DICOMUtils.importDicom(input_folder)

      logging.info("Load first patient into Slicer")
      logic.LoadFirstPatientIntoSlicer()
      save_rtslices(output_folder)

  except Exception, e:
      print(e)
 def LoadFirstPatientIntoSlicer(self):
   # Choose first patient from the patient list
   patient = slicer.dicomDatabase.patients()[0]
   DICOMUtils.loadPatientByUID(patient)
  def test_PETLiverUptakeMeasurementQR2(self):
    """ test segmentation options
    """
    try:
      self.assertIsNotNone( slicer.modules.petliveruptakemeasurement )
      with DICOMUtils.TemporaryDICOMDatabase(self.tempDicomDatabaseDir) as db:
        self.assertTrue(db.isOpen)
        self.assertEqual(slicer.dicomDatabase, db)
        self.delayDisplay('Loading PET DICOM dataset (including download if necessary)')
        petNode = self.loadTestData()

        qrWidget = slicer.modules.PETLiverUptakeMeasurementQRWidget
        qrWidget.inputSelector.setCurrentNode(petNode)
        segmentationNode = qrWidget.segmentationSelector.addNode()

        self.delayDisplay('Running segmentation with standard settings')
        qrWidget.segmentButton.click()
        self.assertTrue(abs(float(qrWidget.meanValueLineEdit.text)-2.36253)<0.01)

        self.delayDisplay('Specifying annotation ROI')
        roi=slicer.vtkMRMLAnnotationROINode()
        roi.SetXYZ([-34,243,-1168])
        roi.SetRadiusXYZ([85,102,82])
        roi.SetName('ROI')
        slicer.mrmlScene.AddNode(roi)
        qrWidget.regionSelector.setCurrentNode(roi)
        qrWidget.segmentButton.click()
        self.assertTrue(abs(float(qrWidget.meanValueLineEdit.text)-2.91891)<0.01)

        self.delayDisplay('Changing erosion range')
        originalErosion = qrWidget.erosionSlider.value
        qrWidget.erosionSlider.value = 0
        qrWidget.segmentButton.click()
        self.assertTrue(abs(float(qrWidget.meanValueLineEdit.text)-2.71982)<0.01)

        self.delayDisplay('Changing thresholds')
        originalMinimValue = qrWidget.thresholdRangeSlider.minimumValue
        originalMaximumValue = qrWidget.thresholdRangeSlider.maximumValue
        qrWidget.thresholdRangeSlider.minimumValue = 2
        qrWidget.thresholdRangeSlider.maximumValue = 20
        qrWidget.segmentButton.click()
        self.assertTrue(abs(float(qrWidget.meanValueLineEdit.text)-3.72669)<0.01)

        self.delayDisplay('Completing and writing DICOM report')
        qrWidget.readerValueLineEdit.text = 'semiautotest'
        self.assertTrue(qrWidget.saveReport(completed=True))

        self.delayDisplay('Testing that report was saved as semiautomatic result')
        import dicom
        patientUID = DICOMUtils.getDatabasePatientUIDByPatientName('QIN-HEADNECK-01-0139')
        studies = slicer.dicomDatabase.studiesForPatient(patientUID)
        series = slicer.dicomDatabase.seriesForStudy(studies[0])
        SEGSeries = None
        for serie in series:
          description = slicer.dicomDatabase.descriptionForSeries(serie)
          if description=='Semiautomatic Liver Reference Region Segmentation':
            SEGSeries = serie
        self.assertIsNotNone(SEGSeries)

        # reset values
        qrWidget.regionSelector.removeCurrentNode()
        qrWidget.erosionSlider.value = originalErosion
        qrWidget.thresholdRangeSlider.minimumValue = originalMinimValue
        qrWidget.thresholdRangeSlider.maximumValue = originalMaximumValue

        # clean up data from DICOM database
        db.removePatient(patientUID)

        self.delayDisplay('Test passed!')

    except Exception, e:
      import traceback
      traceback.print_exc()
      self.delayDisplay('Test caused exception!\n' + str(e),self.delayMs*2)
Example #27
0
def main(argv):

    try:
        logging.info("Start batch RTSTRUCT conversion")

        # Parse command-line arguments
        parser = argparse.ArgumentParser(
            description="Batch Structure Set Conversion")
        parser.add_argument(
            "-i",
            "--input-folder",
            dest="input_folder",
            metavar="PATH",
            default="-",
            required=True,
            help=
            "Folder of input DICOM study (or database path to use existing)")
        parser.add_argument(
            "-r",
            "--ref-dicom-folder",
            dest="ref_dicom_folder",
            metavar="PATH",
            default="",
            required=False,
            help=
            "Folder containing reference anatomy DICOM image series, if stored outside the input study"
        )
        parser.add_argument(
            "-u",
            "--use-ref-image",
            dest="use_ref_image",
            default=False,
            required=False,
            action='store_true',
            help=
            "Use anatomy image as reference when converting structure set to labelmap"
        )
        parser.add_argument("-x",
                            "--exist-db",
                            dest="exist_db",
                            default=False,
                            required=False,
                            action='store_true',
                            help="Process an existing database")
        parser.add_argument("-m",
                            "--export-images",
                            dest="export_images",
                            default=False,
                            required=False,
                            action='store_true',
                            help="Export image data with labelmaps")
        parser.add_argument("-o",
                            "--output-folder",
                            dest="output_folder",
                            metavar="PATH",
                            default=".",
                            help="Folder for output labelmaps")
        parser.add_argument("-s",
                            "--export-surfaces",
                            dest="export_surfaces",
                            default=False,
                            required=False,
                            action='store_true',
                            help="Export surface mesh representation")
        parser.add_argument(
            "-c",
            "--show-python-console",
            dest="show_python_console",
            default=False,
            required=False,
            action='store_true',
            help=
            "If this flag is specified then messages are displayed in an interactive Python console and the application does not quit when the script is finished."
        )

        args = parser.parse_args(argv)

        if args.show_python_console:
            slicer.util.pythonShell().show()
            slicer.exit_when_finished = False

        # Check if SlicerRT is installed
        try:
            slicer.modules.dicomrtimportexport
        except AttributeError:
            logging.error("Please install SlicerRT extension")
            return 1

        # Check required arguments
        if args.input_folder == "-":
            logging.warning('Please specify input DICOM study folder!')
        if args.output_folder == ".":
            logging.info(
                'Current directory is selected as output folder (default). To change it, please specify --output-folder'
            )

        # Convert to python path style
        input_folder = args.input_folder.replace('\\', '/')
        ref_dicom_folder = args.ref_dicom_folder.replace('\\', '/')
        output_folder = args.output_folder.replace('\\', '/')

        use_ref_image = args.use_ref_image
        exist_db = args.exist_db
        export_images = args.export_images
        export_surfaces = args.export_surfaces

        # Perform batch conversion
        logic = BatchStructureSetConversionLogic()

        def save_rtslices(output_dir, use_ref_image, ref_image_node_id=None):
            # package the saving code into a subfunction
            logging.info("Convert loaded structure set to labelmap volumes")
            labelmaps = logic.ConvertStructureSetToLabelmap(
                use_ref_image, ref_image_node_id)

            logging.info("Save labelmaps to directory " + output_dir)
            logic.SaveLabelmaps(labelmaps, output_dir)
            if export_surfaces:
                logic.SaveModels(output_dir)
            if export_images:
                logic.SaveImages(output_dir)
            logging.info("DONE")

        if exist_db:
            logging.info('BatchStructureSet running in existing database mode')
            DICOMUtils.openDatabase(input_folder)
            all_patients = slicer.dicomDatabase.patients()
            logging.info('Processing %d patients...' % len(all_patients))

            for patient in all_patients:
                try:
                    slicer.mrmlScene.Clear(0)  # clear the scene
                    DICOMUtils.loadPatientByUID(patient)
                    output_dir = os.path.join(output_folder, patient)
                    if not os.access(output_dir, os.F_OK):
                        os.mkdir(output_dir)
                    save_rtslices(output_dir, use_ref_image)
                except OSError as e:
                    # Failed to load data from this patient, continue with the next one
                    print(e)

        else:
            logging.info('BatchStructureSet running in file mode')
            ref_volume_file_path = None
            if os.path.isdir(ref_dicom_folder):
                # If reference DICOM folder is given and valid, then import reference patient and save its ID
                logging.info("Import reference anatomy DICOM data from " +
                             ref_dicom_folder)
                DICOMUtils.openTemporaryDatabase()
                DICOMUtils.importDicom(ref_dicom_folder)
                # Save first volume to be used as reference
                logic.LoadFirstPatientIntoSlicer()
                scalarVolumeNodes = list(
                    slicer.util.getNodes('vtkMRMLScalarVolume*').values())
                if len(scalarVolumeNodes) > 0:
                    refVolNode = scalarVolumeNodes[0]
                    refVolStorageNode = refVolNode.CreateDefaultStorageNode()
                    ref_volume_file_path = os.path.join(
                        output_folder, 'refVolume.nrrd')
                    refVolStorageNode.SetFileName(ref_volume_file_path)
                    refVolStorageNode.WriteData(refVolNode)

            logging.info("Import DICOM data from " + input_folder)
            DICOMUtils.openTemporaryDatabase()
            DICOMUtils.importDicom(input_folder)

            all_patients = slicer.dicomDatabase.patients()
            logging.info('Processing %d patients...' % len(all_patients))
            for patient in all_patients:
                try:
                    slicer.mrmlScene.Clear(0)  # clear the scene
                    DICOMUtils.loadPatientByUID(patient)
                    output_dir = os.path.join(output_folder, patient)
                    if not os.access(output_dir, os.F_OK):
                        os.mkdir(output_dir)
                    ref_volume_node_id = None
                    if ref_volume_file_path:
                        try:
                            refVolNode = slicer.util.loadVolume(
                                ref_volume_file_path)
                            ref_volume_node_id = refVolNode.GetID()
                        except:
                            pass
                    save_rtslices(output_dir, use_ref_image,
                                  ref_volume_node_id)
                except OSError as e:
                    # Failed to load data from this patient, continue with the next one
                    print(e)

    except Exception as e:
        import traceback
        traceback.print_exc()
        print(e)
        return 1

    return 0
Example #28
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 = slicer.modules.DICOMWidget
      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 #29
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 #30
0
  def test_Part1DICOM(self,enableScreenshotsFlag=0,screenshotScaleFactor=1):
    """ Test the DICOM part of the test using the head atlas
    """

    logic = RSNAVisTutorialLogic()
    logic.enableScreenshots = enableScreenshotsFlag
    logic.screenshotScaleFactor = screenshotScaleFactor

    import os
    self.delayDisplay("Starting the DICOM test")
    #
    # first, get the data - a zip file of dicom data
    #
    import SampleData
    dicomFilesDirectory = SampleData.downloadFromURL(
      fileNames='dataset1_Thorax_Abdomen.zip',
      uris='http://slicer.kitware.com/midas3/download?items=124183')[0]

    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 = slicer.modules.DICOMWidget
      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()

      logic.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))

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

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

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

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

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

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

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

      volumeRenderingWidgetRep = slicer.modules.volumerendering.widgetRepresentation()
      abdomenVolume = slicer.mrmlScene.GetFirstNodeByName('6: CT_Thorax_Abdomen')
      volumeRenderingWidgetRep.setMRMLVolumeNode(abdomenVolume)
      logic.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)
      logic.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)
      logic.takeScreenshot('VolumeRendering-ViewRendering','View Volume Rendering',-1)

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

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

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

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

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

      redWidget.sliceController().setSliceVisible(True)
      logic.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)
      logic.takeScreenshot('VolumeRendering-SizedROI','Position the ROI over a kidney',-1)

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

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

      self.delayDisplay('Test passed!')
    except Exception as e:
      import traceback
      traceback.print_exc()
      self.delayDisplay('Test caused exception!\n' + str(e))

    self.delayDisplay("Restoring original database directory")
    DICOMUtils.closeTemporaryDatabase(originalDatabaseDirectory)
Example #31
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 SampleData
    SampleData.downloadFromURL(
      fileNames='Dcmtk-db.zip',
      uris='http://slicer.kitware.com/midas3/download?items=18822')[0]

    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 #32
0
    def test_AlternateReaders(self):
        """ Test the DICOM loading of sample testing data
    """
        testPass = True
        import os, json
        self.delayDisplay("Starting the DICOM test")

        referenceData = [{
            "url": "http://slicer.kitware.com/midas3/download?items=292839",
            "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?items=294857",
            "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")
        for dataset in referenceData:
            try:
                import SampleData
                dicomFilesDirectory = SampleData.downloadFromURL(
                    fileNames=dataset['fileName'], uris=dataset['url'])[0]
                self.delayDisplay('Finished with download')

                #
                # insert the data into th database
                #
                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()

                #
                # 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)
                    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'])

            except Exception, e:
                import traceback
                traceback.print_exc()
                self.delayDisplay('%s Test caused exception!\n' %
                                  dataset['name'] + str(e))
                testPass = False