def getLoadablesFromRWVMFile(self, file): rwvLoadable = DICOMLib.DICOMLoadable() rwvLoadable.files.append(file) rwvLoadable.patientName = self.__getSeriesInformation( rwvLoadable.files, self.tags['patientName']) rwvLoadable.patientID = self.__getSeriesInformation( rwvLoadable.files, self.tags['patientID']) rwvLoadable.studyDate = self.__getSeriesInformation( rwvLoadable.files, self.tags['studyDate']) dicomFile = dicom.read_file(file) rwvmSeq = dicomFile.ReferencedImageRealWorldValueMappingSequence[ 0].RealWorldValueMappingSequence unitsSeq = rwvmSeq[0].MeasurementUnitsCodeSequence rwvLoadable.name = rwvLoadable.patientName + ' ' + self.convertStudyDate( rwvLoadable.studyDate) + ' ' + unitsSeq[0].CodeMeaning rwvLoadable.unitName = unitsSeq[0].CodeMeaning (quantity, units) = self.getQuantityAndUnitsFromDICOM(dicomFile) rwvLoadable.quantity = quantity rwvLoadable.units = units rwvLoadable.tooltip = rwvLoadable.name rwvLoadable.selected = True rwvLoadable.confidence = 0.90 return [rwvLoadable]
def examineFilesMultiseries(self, files): """ This strategy is similar to examineFiles(), but does not separate the files by individual series before parsing multivolumes out. """ if self.detailedLogging: logging.debug('MultiVolumeImporterPlugin: examineMultiseries') loadables = [] mvNodes = self.initMultiVolumes(files, prescribedTags=[ 'SeriesTime', 'AcquisitionTime', 'FlipAngle', 'CardiacCycle' ]) if self.detailedLogging: logging.debug( 'MultiVolumeImporterPlugin: found {} multivolumes!'.format( len(mvNodes))) for mvNode in mvNodes: tagName = mvNode.GetAttribute( 'MultiVolume.FrameIdentifyingDICOMTagName') nFrames = mvNode.GetNumberOfFrames() orderedFiles = mvNode.GetAttribute( 'MultiVolume.FrameFileList').split(',') desc = slicer.dicomDatabase.fileValue( orderedFiles[0], self.tags['studyDescription']) # SeriesDescription num = slicer.dicomDatabase.fileValue(orderedFiles[0], self.tags['seriesNumber']) if num != "": name = num + ": " + desc else: name = desc if self.isFrameOriginConsistent(orderedFiles, mvNode) == False: continue loadable = DICOMLib.DICOMLoadable() loadable.files = orderedFiles loadable.tooltip = name + ' - ' + str( nFrames) + ' frames MultiVolume by ' + tagName loadable.name = name loadable.selected = True loadable.multivolume = mvNode if tagName == 'TemporalPositionIdentifier': loadable.confidence = 0.9 else: loadable.confidence = 1. loadables.append(loadable) return loadables
def examineFilesMultiseries(self, files): print('MultiVolumeImportPlugin:examineMultiseries') loadables = [] mvNodes = self.initMultiVolumes( files, prescribedTags=['SeriesTime', 'AcquisitionTime', 'FlipAngle']) print('DICOMMultiVolumePlugin found ' + str(len(mvNodes)) + ' multivolumes!') for mvNode in mvNodes: tagName = mvNode.GetAttribute( 'MultiVolume.FrameIdentifyingDICOMTagName') nFrames = mvNode.GetNumberOfFrames() orderedFiles = string.split( mvNode.GetAttribute('MultiVolume.FrameFileList'), ',') desc = slicer.dicomDatabase.fileValue( orderedFiles[0], self.tags['studyDescription']) # SeriesDescription num = slicer.dicomDatabase.fileValue(orderedFiles[0], self.tags['seriesNumber']) if num != "": name = num + ": " + desc else: name = desc if self.isFrameOriginConsistent(orderedFiles, mvNode) == False: continue loadable = DICOMLib.DICOMLoadable() loadable.files = orderedFiles loadable.tooltip = name + ' - ' + str( nFrames) + ' frames MultiVolume by ' + tagName loadable.name = name loadable.selected = True loadable.multivolume = mvNode loadable.confidence = 0.9 loadables.append(loadable) return loadables
def examine(self, fileLists): """ Returns a list of DICOMLoadable instances corresponding to ways of interpreting the fileLists parameter. Top-level examine() calls various individual strategies implemented in examineFiles*(). """ loadables = [] allfiles = [] for files in fileLists: loadables += self.examineFiles(files) # this strategy sorts the files into groups loadables += self.examineFilesIPPAcqTime(files) allfiles += files # here all files are lumped into one list for the situations when # individual frames should be parsed from series loadables += self.examineFilesMultiseries(allfiles) if len(allfiles) > len(files): # only examineFilesIPPAcqTime again if there are multiple file groups loadables += self.examineFilesIPPAcqTime(allfiles) # this strategy sorts the files into groups loadables += self.examineFilesIPPInstanceNumber(allfiles) # If Sequences module is available then duplicate all the loadables # for loading them as volume sequence. # A slightly higher confidence value is set for volume sequence loadables, # therefore by default data will be loaded as volume sequence. if hasattr(slicer.modules, 'sequences'): seqLoadables = [] for loadable in loadables: seqLoadable = DICOMLib.DICOMLoadable() seqLoadable.files = loadable.files seqLoadable.tooltip = loadable.tooltip.replace( ' frames MultiVolume by ', ' frames Volume Sequence by ') seqLoadable.name = loadable.name.replace( ' frames MultiVolume by ', ' frames Volume Sequence by ') seqLoadable.multivolume = loadable.multivolume seqLoadable.selected = loadable.selected seqLoadable.confidence = loadable.confidence seqLoadable.loadAsVolumeSequence = True seqLoadables.append(seqLoadable) # Among all selected loadables, the ones that are listed first will be selected by default, # therefore we need to prepend loadables if sequence format is preferred. # Determine from settings loading into sequence node should have higher confidence (selected by default). settings = qt.QSettings() sequenceFormatPreferred = (settings.value( "DICOM/PreferredMultiVolumeImportFormat", "default") == "sequence") if sequenceFormatPreferred: # prepend loadables[0:0] = seqLoadables else: # append loadables += seqLoadables return loadables
def examineFiles(self, files): """ This is the main strategy that assumes all files (instances) belong to the same series, and all instances within the same frame have the same value for one of the attributes defined in self.multiVolumeTags """ logging.debug("MultiVolumeImportPlugin::examine") """ Returns a list of DICOMLoadable instances corresponding to ways of interpreting the files parameter. """ loadables = [] # Look for series with several values in either of the volume-identifying # tags in files # first separate individual series, then try to find multivolume in each # of the series (code from DICOMScalarVolumePlugin) subseriesLists = {} subseriesDescriptions = {} for file in files: value = slicer.dicomDatabase.fileValue( file, self.tags['seriesInstanceUID']) # SeriesInstanceUID desc = slicer.dicomDatabase.fileValue( file, self.tags['seriesDescription']) # SeriesDescription if value == "": value = "Unknown" if desc == "": desc = "Unknown" if not subseriesLists.has_key(value): subseriesLists[value] = [] subseriesLists[value].append(file) subseriesDescriptions[value] = desc # now iterate over all subseries file lists and try to parse the # multivolumes for key in subseriesLists.keys(): mvNodes = self.initMultiVolumes(subseriesLists[key]) logging.debug('DICOMMultiVolumePlugin found ' + str(len(mvNodes)) + ' multivolumes!') for mvNode in mvNodes: tagName = mvNode.GetAttribute( 'MultiVolume.FrameIdentifyingDICOMTagName') nFrames = mvNode.GetNumberOfFrames() orderedFiles = string.split( mvNode.GetAttribute('MultiVolume.FrameFileList'), ',') if self.isFrameOriginConsistent(orderedFiles, mvNode) == False: continue loadable = DICOMLib.DICOMLoadable() loadable.files = files loadable.name = subseriesDescriptions[key] + ' - as a ' + str( nFrames) + ' frames MultiVolume by ' + tagName mvNode.SetName(subseriesDescriptions[key]) loadable.tooltip = loadable.name loadable.selected = True loadable.multivolume = mvNode loadables.append(loadable) return loadables
def examineFilesIPPAcqTime(self, files): """ This strategy first orders files into lists, where each list is indexed by ImagePositionPatient (IPP). Next, files within each list are ordered by AcquisitionTime attribute. Finally, loadable frames are indexed by AcquisitionTime, and files within each frame are ordered by IPP. This strategy was required to handle DSC MRI data collected on Siemens, tested with a DSC sequence obtained using software version "syngo MR B15" """ loadables = [] subseriesLists = {} orderedFiles = [] desc = slicer.dicomDatabase.fileValue( files[0], self.tags['seriesDescription']) # SeriesDescription minTime = self.tm2ms( slicer.dicomDatabase.fileValue(files[0], self.tags['AcquisitionTime'])) for file in files: ipp = slicer.dicomDatabase.fileValue(file, self.tags['position']) time = self.tm2ms( slicer.dicomDatabase.fileValue(file, self.tags['AcquisitionTime'])) if time < minTime: minTime = time if not subseriesLists.has_key(ipp): subseriesLists[ipp] = {} subseriesLists[ipp][time] = file nSlicesEqual = True allIPPs = subseriesLists.keys() for ipp in subseriesLists.keys(): if len(subseriesLists[allIPPs[0]].keys()) != len( subseriesLists[ipp].keys()): nSlicesEqual = False break if len(subseriesLists[allIPPs[0]].keys()) < 2 or not nSlicesEqual: return [] if nSlicesEqual: nFrames = len(subseriesLists[allIPPs[0]].keys()) nSlices = len(allIPPs) orderedFiles = [0] * nFrames * nSlices frameLabelsStr = "" frameFileListStr = "" frameLabelsArray = vtk.vtkDoubleArray() ippPositionCnt = 0 for ipp in subseriesLists.keys(): timesSorted = subseriesLists[ipp].keys() timesSorted.sort() timeCnt = 0 for time in timesSorted: orderedFiles[timeCnt * nSlices + ippPositionCnt] = subseriesLists[ipp][time] timeCnt = timeCnt + 1 if ippPositionCnt == 0: frameLabelsStr = frameLabelsStr + str(time - minTime) + ',' frameLabelsArray.InsertNextValue(time - minTime) ippPositionCnt = ippPositionCnt + 1 scalarVolumePlugin = slicer.modules.dicomPlugins[ 'DICOMScalarVolumePlugin']() firstFrameTime = 0 for f in range(nFrames): frameFileList = orderedFiles[f * nSlices:(f + 1) * nSlices] svs = scalarVolumePlugin.examine([frameFileList]) if len(svs) == 0: print( 'Failed to parse one of the multivolume frames as scalar volume!' ) break time = self.tm2ms( slicer.dicomDatabase.fileValue( svs[0].files[0], self.tags['AcquisitionTime'])) if f == 0: frameLabelsStr = '0,' frameLabelsArray.InsertNextValue(0) firstFrameTime = time else: frameLabelsStr = frameLabelsStr + str(time - firstFrameTime) + ',' frameLabelsArray.InsertNextValue(time) for file in orderedFiles: frameFileListStr = frameFileListStr + str(file) + ',' frameLabelsStr = frameLabelsStr[:-1] frameFileListStr = frameFileListStr[:-1] mvNode = slicer.mrmlScene.CreateNodeByClass( 'vtkMRMLMultiVolumeNode') mvNode.SetReferenceCount(mvNode.GetReferenceCount() - 1) mvNode.SetScene(slicer.mrmlScene) mvNode.SetAttribute("MultiVolume.FrameLabels", frameLabelsStr) mvNode.SetAttribute("MultiVolume.FrameIdentifyingDICOMTagName", "AcquisitionTime") mvNode.SetAttribute("MultiVolume.ParseStrategy", "AcquisitionTime+ImagePositionPatient") mvNode.SetAttribute('MultiVolume.NumberOfFrames', str(nFrames)) mvNode.SetAttribute('MultiVolume.FrameIdentifyingDICOMTagUnits', "ms") # keep the files in the order by the detected tag # files are not ordered within the individual frames -- this will be # done by ScalarVolumePlugin later mvNode.SetAttribute('MultiVolume.FrameFileList', frameFileListStr) self.addAcquisitionAttributes(mvNode, frameFileList) mvNode.SetNumberOfFrames(nFrames) mvNode.SetLabelName("AcquisitionTime") mvNode.SetLabelArray(frameLabelsArray) loadable = DICOMLib.DICOMLoadable() loadable.files = orderedFiles loadable.name = desc + ' - as a ' + str( nFrames ) + ' frames MultiVolume by ImagePositionPatient+AcquisitionTime' mvNode.SetName(desc) loadable.tooltip = loadable.name loadable.selected = True loadable.multivolume = mvNode loadable.confidence = 1. loadables.append(loadable) return loadables
def getLoadablePetSeriesFromRWVMFile(self, file): """ Returns DICOMLoadable instances associated with an RWVM object.""" newLoadables = [] dicomFile = dicom.read_file(file) if dicomFile.Modality == "RWV": refRWVMSeq = dicomFile.ReferencedImageRealWorldValueMappingSequence refSeriesSeq = dicomFile.ReferencedSeriesSequence if refRWVMSeq: # May have more than one RWVM value, create loadables for each for item in refRWVMSeq: rwvLoadable = DICOMLib.DICOMLoadable() # Get the referenced files from the database refImageSeq = item.ReferencedImageSequence instanceFiles = [] for instance in refImageSeq: uid = instance.ReferencedSOPInstanceUID if uid: instanceFiles += [slicer.dicomDatabase.fileForInstance(uid)] # Get the Real World Values rwvLoadable.files = instanceFiles rwvLoadable.rwvFile = file rwvLoadable.patientName = self.__getSeriesInformation(rwvLoadable.files, self.tags['patientName']) rwvLoadable.patientID = self.__getSeriesInformation(rwvLoadable.files, self.tags['patientID']) rwvLoadable.studyDate = self.__getSeriesInformation(rwvLoadable.files, self.tags['studyDate']) rwvmSeq = item.RealWorldValueMappingSequence unitsSeq = rwvmSeq[0].MeasurementUnitsCodeSequence rwvLoadable.name = rwvLoadable.patientName + ' ' + self.convertStudyDate(rwvLoadable.studyDate) + ' ' + unitsSeq[0].CodeMeaning rwvLoadable.tooltip = rwvLoadable.name rwvLoadable.unitName = unitsSeq[0].CodeMeaning rwvLoadable.units = unitsSeq[0].CodeValue rwvLoadable.confidence = 0.90 rwvLoadable.selected = True # added by CB rwvLoadable.slope = rwvmSeq[0].RealWorldValueSlope rwvLoadable.referencedSeriesInstanceUID = refSeriesSeq[0].SeriesInstanceUID # determine modality of referenced series refSeriesFiles = slicer.dicomDatabase.filesForSeries(refSeriesSeq[0].SeriesInstanceUID) refSeriesFile0 = dicom.read_file(refSeriesFiles[0]) rwvLoadable.referencedModality = refSeriesFile0.Modality # add radiopharmaceutical info if PET if rwvLoadable.referencedModality == 'PT': print('Found Referenced PET series') ris = refSeriesFile0.RadiopharmaceuticalInformationSequence[0] try: # TODO Many DICOM series do not have radiopharmaceutical code sequence! rcs = ris.RadiopharmaceuticalCodeSequence if len(rcs) > 0: rwvLoadable.RadiopharmaceuticalCodeValue = rcs[0].CodeValue except AttributeError: print('WARNING: series does not have radiopharmaceutical code sequence.') try: rcs = ris.RadionuclideCodeSequence if len(rcs) > 0: rwvLoadable.RadionuclideCodeValue = rcs[0].CodeValue except AttributeError: print('WARNING: Cannot find radionuclide info for PET Series.') self.sortLoadableSeriesFiles(rwvLoadable) newLoadables.append(rwvLoadable) return newLoadables
def loadMeasurementVolumesEndOf2013(self, measurements, sampleIndex=0): """Load the volumes corresponding to the given measurement. If there is more than one sample, load the sampleIndex'th data. """ import DICOMScalarVolumePlugin import DICOMLib results = {} m = measurements # load the original MR scans loader = DICOMScalarVolumePlugin.DICOMScalarVolumePluginClass() loadable = DICOMLib.DICOMLoadable() loadable.name = m.label loadable.files = m.rawFiles[sampleIndex] results['MR'] = loader.load(loadable) results['MR', "files"] = m.rawFiles[sampleIndex] # load the CRO-provided overall label map results['classmap'] = slicer.util.loadVolume( m.classmapFiles[sampleIndex][0], returnNode=True)[1] results['classmap'].SetName(m.label + "-classmap") results['classmap', "files"] = m.classmapFiles[sampleIndex] # load the semi-automated per-muscle segmentations volumesLogic = slicer.modules.volumes.logic() labelFile = m.labelFiles[sampleIndex] results['muscleLabel'] = volumesLogic.AddArchetypeVolume( labelFile, m.label + "-label", 1) results['muscleLabel', "files"] = labelFile for result in ("MR", "classmap", "muscleLabel"): if not results[result]: print('Could not load %s' % result) print(results[result, "files"]) return # use the same physical mapping for all volumes, since # they should all be in the same pixel space # but: # -- the CRO provided DICOM files have embedded NULL # characters in PixelSpacing so they do not always load correctly # -- control points are expressed in muscleIJKToRAS coordinates, # but we want to export them with respect to mrIJKToRAS, so # we keep muscleToMR as a 4x4 matrix to map the control points. mrIJKToRAS = vtk.vtkMatrix4x4() muscleIJKToRAS = vtk.vtkMatrix4x4() results['MR'].GetIJKToRASMatrix(mrIJKToRAS) results['muscleLabel'].GetIJKToRASMatrix(muscleIJKToRAS) results['classmap'].SetIJKToRASMatrix(mrIJKToRAS) results['muscleLabel'].SetIJKToRASMatrix(mrIJKToRAS) # in the file we have Pmuscle == points in muscle space # we want Mmusle2mr == matrix from muscle to mr space # thus: # Pmr = Mmuscle2mr * Pmuscle mMuscleToMR = vtk.vtkMatrix4x4() # so we first map from Pmuscle to IJK, then IJK to MR # or: # Pmr = MmrIJKToRAS * MmuscleIJKToRAS-1 * Pmuscle muscleIJKToRAS.Invert() vtk.vtkMatrix4x4.Multiply4x4(mrIJKToRAS, muscleIJKToRAS, mMuscleToMR) results['mMuscleToMR'] = mMuscleToMR # select volumes to display appLogic = slicer.app.applicationLogic() selNode = appLogic.GetSelectionNode() selNode.SetReferenceActiveVolumeID(results['MR'].GetID()) selNode.SetReferenceSecondaryVolumeID(results['classmap'].GetID()) selNode.SetReferenceActiveLabelVolumeID(results['muscleLabel'].GetID()) appLogic.PropagateVolumeSelection() # set up the composite nodes and slice nodes compositeNodes = slicer.util.getNodes('vtkMRMLSliceCompositeNode*') for compositeNode in compositeNodes.values(): compositeNode.SetForegroundOpacity(0.5) sliceNodes = slicer.util.getNodes('vtkMRMLSliceNode*') for sliceNode in sliceNodes.values(): sliceNode.SetUseLabelOutline(True) layoutManager = slicer.app.layoutManager() layoutManager.layout = 3 # the four-up view # let the layout take effect and re-render new data slicer.app.processEvents() layoutManager.resetSliceViews() threeDView = layoutManager.threeDWidget(0).threeDView() threeDView.lookFromAxis(ctk.ctkAxesWidget.Anterior) threeDView.resetFocalPoint() renderer = threeDView.renderWindow().GetRenderers().GetItemAsObject(0) camera = renderer.GetActiveCamera() camera.SetPosition(-125, 1307.15, 211) camera.SetFocalPoint(-125, 21, 211) camera.SetClippingRange(934.232, 1849.54) camera.SetDistance(1286.15) # let the layout take effect and re-render new data slicer.app.processEvents() return results
def examine(self, fileLists): """ Returns a list of DICOMLoadable instances corresponding to ways of interpreting the fileLists parameter. """ petCtStudies = self.__scanForValidPETCTStudies(fileLists) mainLoadable = DICOMLib.DICOMLoadable() mainLoadable.tooltip = "PET/CT Studies for Longitudinal PET/CT Report" studyplrlsing = "Studies" if len(petCtStudies) != 1 else "Study" mainLoadable.name = str(len(petCtStudies)) + " PET/CT " + studyplrlsing mainLoadable.selected = True mainLoadable.confidence = 1.0 petImageSeries = 0 ctImageSeries = 0 for fileList in fileLists: petSeries = self.__getSeriesInformation( fileList, self.tags['seriesModality']) == self.petTerm ctSeries = self.__getSeriesInformation( fileList, self.tags['seriesModality']) == self.ctTerm fromPetCtStudy = self.__getSeriesInformation( fileList, self.tags['studyInstanceUID']) in petCtStudies if (petSeries | ctSeries) & fromPetCtStudy: loadables = self.getCachedLoadables(fileList) if not loadables: loadables = self.scalarVolumePlugin.examineFiles(fileList) self.cacheLoadables(fileList, loadables) for ldbl in loadables: if ldbl.selected: type = "" if petSeries: petImageSeries += 1 type = "PT" elif ctSeries: ctImageSeries += 1 type = "CT" mainLoadable.files += ldbl.files ldbl.name = type + "_" + self.__getSeriesInformation( ldbl.files, self.tags['studyDate'] ) + "_" + self.__getSeriesInformation( ldbl.files, self.tags['seriesTime']) self.cacheLoadables(ldbl.files, [ldbl]) if ldbl.warning: mainLoadable.warning += ldbl.warning + " " break # break for loop because only one loadable needed loadables = [] if mainLoadable.files: mainLoadable.name = mainLoadable.name + " containing " + str( petImageSeries) + " PET and " + str( ctImageSeries) + " CT image series" loadables = [mainLoadable] return loadables
def getLoadablePetSeriesFromRWVMFile(self, file): """ Returns DICOMLoadable instances associated with an RWVM object.""" newLoadables = [] dicomFile = dicom.read_file(file) if dicomFile.Modality == "RWV": refRWVMSeq = dicomFile.ReferencedImageRealWorldValueMappingSequence refSeriesSeq = dicomFile.ReferencedSeriesSequence if refRWVMSeq: # May have more than one RWVM value, create loadables for each for item in refRWVMSeq: rwvLoadable = DICOMLib.DICOMLoadable() # Get the referenced files from the database refImageSeq = item.ReferencedImageSequence instanceFiles = [] for instance in refImageSeq: uid = instance.ReferencedSOPInstanceUID if uid: instanceFiles += [slicer.dicomDatabase.fileForInstance(uid)] # Get the Real World Values rwvLoadable.files = instanceFiles rwvLoadable.rwvFile = file rwvLoadable.patientName = self.__getSeriesInformation(rwvLoadable.files, self.tags['patientName']) rwvLoadable.patientID = self.__getSeriesInformation(rwvLoadable.files, self.tags['patientID']) rwvLoadable.studyDate = self.__getSeriesInformation(rwvLoadable.files, self.tags['studyDate']) rwvmSeq = item.RealWorldValueMappingSequence unitsSeq = rwvmSeq[0].MeasurementUnitsCodeSequence rwvLoadable.name = rwvLoadable.patientName + ' ' + self.convertStudyDate(rwvLoadable.studyDate) + ' ' + unitsSeq[0].CodeMeaning rwvLoadable.tooltip = rwvLoadable.name import json rwvLoadable.unitName = unitsSeq[0].CodeMeaning rwvLoadable.units = unitsSeq[0].CodeValue unitsCode = CodedValueTuple(unitsSeq[0].CodeValue, unitsSeq[0].CodeMeaning, unitsSeq[0].CodingSchemeDesignator) rwvLoadable.unitsCode = json.dumps(unitsCode.getDictionary()) quantityCode = CodedValueTuple() # Slicer is using an older version of pydicom that does not have this # item in the dictionary, thus need to access by tag, instead of # this: # quantitySeq = rwvmSeq[0].QuantityDefinitionSequence try: quantitySeq = rwvmSeq[0][0x0040,0x9220] if quantitySeq: for qsItem in quantitySeq: if qsItem.ConceptNameCodeSequence[0].CodeMeaning == "Quantity": concept = qsItem.ConceptCodeSequence[0] quantityCode = CodedValueTuple(concept.CodeValue, concept.CodeMeaning, concept.CodingSchemeDesignator) rwvLoadable.quantityCode = json.dumps(quantityCode.getDictionary()) except: # it does not matter what goes wrong pass rwvLoadable.confidence = 0.90 rwvLoadable.selected = True # added by CB rwvLoadable.slope = rwvmSeq[0].RealWorldValueSlope rwvLoadable.referencedSeriesInstanceUID = refSeriesSeq[0].SeriesInstanceUID # determine modality of referenced series refSeriesFiles = slicer.dicomDatabase.filesForSeries(refSeriesSeq[0].SeriesInstanceUID) refSeriesFile0 = dicom.read_file(refSeriesFiles[0]) rwvLoadable.referencedModality = refSeriesFile0.Modality # add radiopharmaceutical info if PET if rwvLoadable.referencedModality == 'PT': print('Found Referenced PET series') ris = refSeriesFile0.RadiopharmaceuticalInformationSequence[0] try: # TODO Many DICOM series do not have radiopharmaceutical code sequence! rcs = ris.RadiopharmaceuticalCodeSequence if len(rcs) > 0: rwvLoadable.RadiopharmaceuticalCodeValue = rcs[0].CodeValue except AttributeError: print('WARNING: series does not have radiopharmaceutical code sequence.') try: rcs = ris.RadionuclideCodeSequence if len(rcs) > 0: rwvLoadable.RadionuclideCodeValue = rcs[0].CodeValue except AttributeError: print('WARNING: Cannot find radionuclide info for PET Series.') self.sortLoadableSeriesFiles(rwvLoadable) newLoadables.append(rwvLoadable) return newLoadables