def mapSOPClassUIDToDICOMQuantityAndUnits(self, classUID): MRname2UID = { "MR Image Storage": "1.2.840.10008.5.1.4.1.1.4", "Enhanced MR Image Storage": "1.2.840.10008.5.1.4.1.1.4.1", "Legacy Converted Enhanced MR Image Storage": "1.2.840.10008.5.1.4.1.1.4.4" } CTname2UID = { "CT Image Storage": "1.2.840.10008.5.1.4.1.1.2", "Enhanced CT Image Storage": "1.2.840.10008.5.1.4.1.1.2.1", "Legacy Converted Enhanced CT Image Storage": "1.2.840.10008.5.1.4.1.1.2.2" } quantity = None units = None # Note more specialized definitions can be specified for MR by more # specialized plugins, see codes 110800 and on in # http://dicom.nema.org/medical/dicom/current/output/chtml/part16/chapter_D.html if classUID in MRname2UID.values(): quantity = slicer.vtkCodedEntry() quantity.SetValueSchemeMeaning("110852", "DCM", "MR signal intensity") units = slicer.vtkCodedEntry() units.SetValueSchemeMeaning("1", "UCUM", "no units") if classUID in CTname2UID.values(): quantity = slicer.vtkCodedEntry() quantity.SetValueSchemeMeaning("112031", "DCM", "Attenuation Coefficient") units = slicer.vtkCodedEntry() units.SetValueSchemeMeaning("[hnsf'U]", "UCUM", "Hounsfield unit") return (quantity, units)
def _downloadTestData(self): """ download DICOM PET scan and add to DICOM database """ import urllib quantity = slicer.vtkCodedEntry() quantity.SetFromString('CodeValue:126400|CodingSchemeDesignator:DCM|CodeMeaning:Standardized Uptake Value') units = slicer.vtkCodedEntry() units.SetFromString('CodeValue:{SUVbw}g/ml|CodingSchemeDesignator:UCUM|CodeMeaning:Standardized Uptake Value body weight') 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.tempDicomDatabase filePath = os.path.join(destinationDirectory, zipFile) # download dataset if necessary if not len(slicer.dicomDatabase.filesForSeries(self.UID)): 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()
def loadTestData(self): #download data and add to dicom database zipFileUrl = 'http://slicer.kitware.com/midas3/download/item/257234/QIN-HEADNECK-01-0139-PET.zip' zipFilePath = self.tempDataDir + '/dicom.zip' zipFileData = self.tempDataDir + '/dicom' expectedNumOfFiles = 545 if not os.access(self.tempDataDir, os.F_OK): os.mkdir(self.tempDataDir) if not os.access(zipFileData, os.F_OK): os.mkdir(zipFileData) 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['DICOMScalarVolumePlugin'].checked = True # Download, unzip, import, and load data. Verify loaded nodes. loadedNodes = {'vtkMRMLScalarVolumeNode': 1} with DICOMUtils.LoadDICOMFilesToDatabase(zipFileUrl, zipFilePath, zipFileData, expectedNumOfFiles, {}, loadedNodes) as success: self.assertTrue(success) print('loading returned true') self.assertEqual( len(slicer.util.getNodes('vtkMRMLSubjectHierarchyNode*')), 1) imageNode = slicer.mrmlScene.GetFirstNodeByClass( 'vtkMRMLScalarVolumeNode') for key, value in dicomPluginStates: dicomPluginCheckbox[key].checked = value # apply the SUVbw conversion factor and set units and quantity suvNormalizationFactor = 0.00040166400000000007 quantity = slicer.vtkCodedEntry() quantity.SetFromString( 'CodeValue:126400|CodingSchemeDesignator:DCM|CodeMeaning:Standardized Uptake Value' ) units = slicer.vtkCodedEntry() units.SetFromString( 'CodeValue:{SUVbw}g/ml|CodingSchemeDesignator:UCUM|CodeMeaning:Standardized Uptake Value body weight' ) multiplier = vtk.vtkImageMathematics() multiplier.SetOperationToMultiplyByK() multiplier.SetConstantK(suvNormalizationFactor) multiplier.SetInput1Data(imageNode.GetImageData()) multiplier.Update() imageNode.GetImageData().DeepCopy(multiplier.GetOutput()) imageNode.GetVolumeDisplayNode().SetWindowLevel(6, 3) imageNode.GetVolumeDisplayNode().SetAndObserveColorNodeID( 'vtkMRMLColorTableNodeInvertedGrey') imageNode.SetVoxelValueQuantity(quantity) imageNode.SetVoxelValueUnits(units) return imageNode
def loadTestData(self): #download data and add to dicom database zipFileUrl = 'http://slicer.kitware.com/midas3/download/item/257234/QIN-HEADNECK-01-0139-PET.zip' zipFilePath = self.tempDataDir + '/dicom.zip' zipFileData = self.tempDataDir + '/dicom' expectedNumOfFiles = 545 if not os.access(self.tempDataDir, os.F_OK): os.mkdir(self.tempDataDir) if not os.access(zipFileData, os.F_OK): os.mkdir(zipFileData) slicer.util.downloadAndExtractArchive(zipFileUrl, zipFilePath, zipFileData, expectedNumOfFiles) DICOMUtils.importDicom(zipFileData) # load dataset dicomFiles = slicer.util.getFilesInDirectory(zipFileData) loadablesByPlugin, loadEnabled = DICOMUtils.getLoadablesFromFileLists( [dicomFiles], ['DICOMScalarVolumePlugin']) loadedNodeIDs = DICOMUtils.loadLoadables(loadablesByPlugin) imageNode = slicer.mrmlScene.GetNodeByID(loadedNodeIDs[0]) imageNode.SetSpacing( 3.3940266832237, 3.3940266832237, 2.02490234375 ) # mimic spacing as produced by Slicer 4.10 for which the test was originally developed imageNode.SetOrigin( 285.367523193359375, 494.58682250976556816, -1873.3819580078125 ) # mimic origin as produced by Slicer 4.10 for which the test was originally developed # apply the SUVbw conversion factor and set units and quantity suvNormalizationFactor = 0.00040166400000000007 quantity = slicer.vtkCodedEntry() quantity.SetFromString( 'CodeValue:126400|CodingSchemeDesignator:DCM|CodeMeaning:Standardized Uptake Value' ) units = slicer.vtkCodedEntry() units.SetFromString( 'CodeValue:{SUVbw}g/ml|CodingSchemeDesignator:UCUM|CodeMeaning:Standardized Uptake Value body weight' ) multiplier = vtk.vtkImageMathematics() multiplier.SetOperationToMultiplyByK() multiplier.SetConstantK(suvNormalizationFactor) multiplier.SetInput1Data(imageNode.GetImageData()) multiplier.Update() imageNode.GetImageData().DeepCopy(multiplier.GetOutput()) imageNode.GetVolumeDisplayNode().SetWindowLevel(6, 3) imageNode.GetVolumeDisplayNode().SetAndObserveColorNodeID( 'vtkMRMLColorTableNodeInvertedGrey') imageNode.SetVoxelValueQuantity(quantity) imageNode.SetVoxelValueUnits(units) return imageNode
def loadTestData(self): self.patienName = 'QIN-HEADNECK-01-0139' #download data and add to dicom database zipFileUrl = 'http://slicer.kitware.com/midas3/download/item/257234/QIN-HEADNECK-01-0139-PET.zip' zipFilePath = self.tempDataDir+'/dicom.zip' zipFileData = self.tempDataDir+'/dicom' expectedNumOfFiles = 545 if not os.access(self.tempDataDir, os.F_OK): os.mkdir(self.tempDataDir) if not os.access(zipFileData, os.F_OK): os.mkdir(zipFileData) 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['DICOMScalarVolumePlugin'].checked = True # Download, unzip, import, and load data. Verify loaded nodes. loadedNodes = {'vtkMRMLScalarVolumeNode':1} with DICOMUtils.LoadDICOMFilesToDatabase(zipFileUrl, zipFilePath, zipFileData, expectedNumOfFiles, {}, loadedNodes) as success: self.assertTrue(success) print ('loading returned true') self.assertEqual( len( slicer.util.getNodes('vtkMRMLSubjectHierarchyNode*') ), 1 ) imageNode = slicer.mrmlScene.GetFirstNodeByClass('vtkMRMLScalarVolumeNode') for key,value in dicomPluginStates: dicomPluginCheckbox[key].checked=value # apply the SUVbw conversion factor and set units and quantity suvNormalizationFactor = 0.00040166400000000007 quantity = slicer.vtkCodedEntry() quantity.SetFromString('CodeValue:126400|CodingSchemeDesignator:DCM|CodeMeaning:Standardized Uptake Value') units = slicer.vtkCodedEntry() units.SetFromString('CodeValue:{SUVbw}g/ml|CodingSchemeDesignator:UCUM|CodeMeaning:Standardized Uptake Value body weight') multiplier = vtk.vtkImageMathematics() multiplier.SetOperationToMultiplyByK() multiplier.SetConstantK(suvNormalizationFactor) multiplier.SetInput1Data(imageNode.GetImageData()) multiplier.Update() imageNode.GetImageData().DeepCopy(multiplier.GetOutput()) imageNode.GetVolumeDisplayNode().SetWindowLevel(6,3) imageNode.GetVolumeDisplayNode().SetAndObserveColorNodeID('vtkMRMLColorTableNodeInvertedGrey') imageNode.SetVoxelValueQuantity(quantity) imageNode.SetVoxelValueUnits(units) return imageNode
def loadTestData(self): self.patienName = 'UNIFORMITY^Bio-mCT' #download data and add to dicom database zipFileUrl = 'https://github.com/QIICR/SlicerPETPhantomAnalysis/releases/download/test-data/PETCylinderPhantom.zip' zipFilePath = self.tempDataDir + '/dicom.zip' zipFileData = self.tempDataDir + '/dicom' expectedNumOfFiles = 171 if not os.access(self.tempDataDir, os.F_OK): os.mkdir(self.tempDataDir) if not os.access(zipFileData, os.F_OK): # download DICOM test dataset os.mkdir(zipFileData) slicer.util.downloadAndExtractArchive(zipFileUrl, zipFilePath, zipFileData, expectedNumOfFiles) DICOMUtils.importDicom(zipFileData) # load dataset dicomFiles = slicer.util.getFilesInDirectory(zipFileData) loadablesByPlugin, loadEnabled = DICOMUtils.getLoadablesFromFileLists( [dicomFiles], ['DICOMScalarVolumePlugin']) loadedNodeIDs = DICOMUtils.loadLoadables(loadablesByPlugin) imageNode = slicer.mrmlScene.GetNodeByID(loadedNodeIDs[0]) # apply the SUVbw conversion factor and set units and quantity suvNormalizationFactor = 0.00012595161151 quantity = slicer.vtkCodedEntry() quantity.SetFromString( 'CodeValue:126400|CodingSchemeDesignator:DCM|CodeMeaning:Standardized Uptake Value' ) units = slicer.vtkCodedEntry() units.SetFromString( 'CodeValue:{SUVbw}g/ml|CodingSchemeDesignator:UCUM|CodeMeaning:Standardized Uptake Value body weight' ) multiplier = vtk.vtkImageMathematics() multiplier.SetOperationToMultiplyByK() multiplier.SetConstantK(suvNormalizationFactor) multiplier.SetInput1Data(imageNode.GetImageData()) multiplier.Update() imageNode.GetImageData().DeepCopy(multiplier.GetOutput()) imageNode.GetVolumeDisplayNode().SetWindowLevel(6, 3) imageNode.GetVolumeDisplayNode().SetAndObserveColorNodeID( 'vtkMRMLColorTableNodeInvertedGrey') imageNode.SetVoxelValueQuantity(quantity) imageNode.SetVoxelValueUnits(units) return imageNode
def createCodedEntry(codeValue, codingScheme, codeMeaning, returnAsString=False): """Create a coded entry and return as string or vtkCodedEntry""" entry = slicer.vtkCodedEntry() entry.SetValueSchemeMeaning(codeValue, codingScheme, codeMeaning) return entry if not returnAsString else entry.GetAsString()
def mapSOPClassUIDToDICOMQuantityAndUnits(self, sopClassUID): quantity = None units = None modality = self.mapSOPClassUIDToModality(sopClassUID) if modality == "MR": quantity = slicer.vtkCodedEntry() quantity.SetValueSchemeMeaning("110852", "DCM", "MR signal intensity") units = slicer.vtkCodedEntry() units.SetValueSchemeMeaning("1", "UCUM", "no units") elif modality == "CT": quantity = slicer.vtkCodedEntry() quantity.SetValueSchemeMeaning("112031", "DCM", "Attenuation Coefficient") units = slicer.vtkCodedEntry() units.SetValueSchemeMeaning("[hnsf'U]", "UCUM", "Hounsfield unit") return (quantity, units)
def getHeaderNames(self, nonEmptyKeysOnly=True): # Derive column header names based on: (a) DICOM information if present, # (b) measurement info name if present (c) measurement key as fallback. # Duplicate names get a postfix [1][2]... to make them unique # Initial and unique column header names are returned keys = self.getNonEmptyKeys() if nonEmptyKeysOnly else self.keys statistics = self.getStatistics() headerNames = [] for key in keys: name = key info = statistics['MeasurementInfo'][key] if key in statistics[ 'MeasurementInfo'] else {} entry = slicer.vtkCodedEntry() dicomBasedName = False if info: if 'DICOM.DerivationCode' in info and info[ 'DICOM.DerivationCode']: entry.SetFromString(info['DICOM.DerivationCode']) name = entry.GetCodeMeaning() dicomBasedName = True elif 'DICOM.QuantityCode' in info and info[ 'DICOM.QuantityCode']: entry.SetFromString(info['DICOM.QuantityCode']) name = entry.GetCodeMeaning() dicomBasedName = True elif 'name' in info and info['name']: name = info['name'] if dicomBasedName and 'DICOM.UnitsCode' in info and info[ 'DICOM.UnitsCode']: entry.SetFromString(info['DICOM.UnitsCode']) units = entry.GetCodeValue() if len(units) > 0 and units[0] == '[' and units[-1] == ']': units = units[1:-1] if len(units) > 0: name += ' [' + units + ']' elif 'units' in info and info['units'] and len( info['units']) > 0: units = info['units'] name += ' [' + units + ']' headerNames.append(name) uniqueHeaderNames = list(headerNames) for name in set(name for name in uniqueHeaderNames if uniqueHeaderNames.count(name) > 1): j = 1 for i in range(len(uniqueHeaderNames)): if uniqueHeaderNames[i] == name: uniqueHeaderNames[i] = name + ' (' + str(j) + ')' j += 1 headerNames = dict((keys[i], headerNames[i]) for i in range(len(keys))) uniqueHeaderNames = dict( (keys[i], uniqueHeaderNames[i]) for i in range(len(keys))) return headerNames, uniqueHeaderNames
def getHeaderNames(self, nonEmptyKeysOnly = True): # Derive column header names based on: (a) DICOM information if present, # (b) measurement info name if present (c) measurement key as fallback. # Duplicate names get a postfix [1][2]... to make them unique # Initial and unique column header names are returned keys = self.getNonEmptyKeys() if nonEmptyKeysOnly else self.keys statistics = self.getStatistics() headerNames = [] for key in keys: name = key info = statistics['MeasurementInfo'][key] if key in statistics['MeasurementInfo'] else {} entry = slicer.vtkCodedEntry() dicomBasedName = False if info: if 'DICOM.DerivationCode' in info and info['DICOM.DerivationCode']: entry.SetFromString(info['DICOM.DerivationCode']) name = entry.GetCodeMeaning() dicomBasedName = True elif 'DICOM.QuantityCode' in info and info['DICOM.QuantityCode']: entry.SetFromString(info['DICOM.QuantityCode']) name = entry.GetCodeMeaning() dicomBasedName = True elif 'name' in info and info['name']: name = info['name'] if dicomBasedName and 'DICOM.UnitsCode' in info and info['DICOM.UnitsCode']: entry.SetFromString(info['DICOM.UnitsCode']) units = entry.GetCodeValue() if len(units)>0 and units[0]=='[' and units[-1]==']': units = units[1:-1] if len(units)>0: name += ' ['+units+']' elif 'units' in info and info['units'] and len(info['units'])>0: name += ' ['+units+']' headerNames.append(name) uniqueHeaderNames = list(headerNames) for name in set(name for name in uniqueHeaderNames if uniqueHeaderNames.count(name)>1): j = 1 for i in range(len(uniqueHeaderNames)): if uniqueHeaderNames[i]==name: uniqueHeaderNames[i] = name+' ('+str(j)+')' j += 1 headerNames = dict((keys[i], headerNames[i]) for i in range(len(keys))) uniqueHeaderNames = dict((keys[i], uniqueHeaderNames[i]) for i in range(len(keys))) return headerNames, uniqueHeaderNames
def load(self, loadable): """ Load the DICOM PM object """ logging.debug('DICOM PM load()') try: uid = loadable.uid logging.debug(f'in load(): uid = {uid}') except AttributeError: return False self.tempDir = os.path.join(slicer.app.temporaryPath, "QIICR", "PM", self.currentDateTime, loadable.uid) try: os.makedirs(self.tempDir) except OSError: pass pmFileName = slicer.dicomDatabase.fileForInstance(uid) if pmFileName is None: logging.debug( f'Failed to get the filename from the DICOM database for {uid}' ) self.cleanup() return False parameters = { "inputFileName": pmFileName, "outputDirName": self.tempDir, } try: pm2nrrd = slicer.modules.paramap2itkimage except AttributeError: logging.debug( 'Unable to find CLI module paramap2itkimage, unable to load DICOM ParametricMap object' ) self.cleanup() return False cliNode = None cliNode = slicer.cli.run(pm2nrrd, cliNode, parameters, wait_for_completion=True) if cliNode.GetStatusString() != 'Completed': logging.debug( 'PM converter did not complete successfully, unable to load DICOM ParametricMap' ) self.cleanup() return False pmNode = slicer.util.loadVolume(os.path.join(self.tempDir, "pmap.nrrd")) # load the metadata JSON to retrieve volume semantics (quantity stored and units) with open(os.path.join(self.tempDir, "meta.json")) as metafile: meta = json.load(metafile) qJson = meta["QuantityValueCode"] uJson = meta["MeasurementUnitsCode"] quantity = slicer.vtkCodedEntry() quantity.SetValueSchemeMeaning(qJson["CodeValue"], qJson["CodingSchemeDesignator"], qJson["CodeMeaning"]) units = slicer.vtkCodedEntry() units.SetValueSchemeMeaning(uJson["CodeValue"], uJson["CodingSchemeDesignator"], uJson["CodeMeaning"]) pmNode.SetVoxelValueQuantity(quantity) pmNode.SetVoxelValueUnits(units) pmNode.SetAttribute("DICOM.instanceUIDs", uid) # create Subject hierarchy nodes for the loaded series self.addSeriesInSubjectHierarchy(loadable, pmNode) self.cleanup() return True
def load(self,loadable): """ Load the DICOM PM object """ logging.debug('DICOM PM load()') try: uid = loadable.uid logging.debug('in load(): uid = ', uid) except AttributeError: return False self.tempDir = os.path.join(slicer.app.temporaryPath, "QIICR", "PM", self.currentDateTime, loadable.uid) try: os.makedirs(self.tempDir) except OSError: pass pmFileName = slicer.dicomDatabase.fileForInstance(uid) if pmFileName is None: logging.debug('Failed to get the filename from the DICOM database for ', uid) self.cleanup() return False parameters = { "inputFileName": pmFileName, "outputDirName": self.tempDir, } try: pm2nrrd = slicer.modules.paramap2itkimage except AttributeError: logging.debug('Unable to find CLI module paramap2itkimage, unable to load DICOM ParametricMap object') self.cleanup() return False cliNode = None cliNode = slicer.cli.run(pm2nrrd, cliNode, parameters, wait_for_completion=True) if cliNode.GetStatusString() != 'Completed': logging.debug('PM converter did not complete successfully, unable to load DICOM ParametricMap') self.cleanup() return False (_,pmNode) = slicer.util.loadVolume(os.path.join(self.tempDir,"pmap.nrrd"), returnNode=True) # load the metadata JSON to retrieve volume semantics (quantity stored and units) with open(os.path.join(self.tempDir,"meta.json")) as metafile: meta = json.load(metafile) qJson = meta["QuantityValueCode"] uJson = meta["MeasurementUnitsCode"] quantity = slicer.vtkCodedEntry() quantity.SetValueSchemeMeaning(qJson["CodeValue"], qJson["CodingSchemeDesignator"], qJson["CodeMeaning"]) units = slicer.vtkCodedEntry() units.SetValueSchemeMeaning(uJson["CodeValue"], uJson["CodingSchemeDesignator"], uJson["CodeMeaning"]) pmNode.SetVoxelValueQuantity(quantity) pmNode.SetVoxelValueUnits(units) pmNode.SetAttribute("DICOM.instanceUIDs", uid) # create Subject hierarchy nodes for the loaded series self.addSeriesInSubjectHierarchy(loadable, pmNode) self.cleanup() return True