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 from DICOMLib import DICOMUtils unsortedFileList = slicer.dicomDatabase.filesForSeries(ctDicomSeriesUID) sortedFileList, distances, warnings = DICOMUtils.getSortedImageFiles( unsortedFileList) outFile = open(os.path.join(outputFolder, slicenamesFilename), "w") 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 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 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 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 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